关于spring:Spring框架系列4-深入浅出Spring核心之面向切面编程AOP

43次阅读

共计 18323 个字符,预计需要花费 46 分钟才能阅读完成。

在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 AOP 的根底含意,同时以此发散了一些 AOP 相干知识点; 本节将在此基础上进一步解读 AOP 的含意以及 AOP 的应用形式。@pdai

  • Spring 框架系列(4) – 深入浅出 Spring 外围之面向切面编程(AOP)

    • 引入
    • 如何了解 AOP

      • AOP 是什么
      • AOP 术语
      • Spring AOP 和 AspectJ 是什么关系
    • AOP 的配置形式

      • XML Schema 配置形式
      • AspectJ 注解形式

        • 接口应用 JDK 代理
        • 非接口应用 Cglib 代理
    • AOP 应用问题小结

      • 切入点(pointcut)的申明规定?
      • 多种加强告诉的程序?
      • Spring AOP 和 AspectJ 之间的要害区别?
      • Spring AOP 还是齐全用 AspectJ?
    • 参考文章
    • 更多文章

引入

咱们在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 AOP 的根底含意,同时以此发散了一些 AOP 相干知识点。

  1. Spring 框架通过定义切面, 通过拦挡切点实现了不同业务模块的解耦,这个就叫 面向切面编程 – Aspect Oriented Programming (AOP)
  2. 为什么 @Aspect 注解应用的是 aspectj 的 jar 包呢?这就引出了Aspect4J 和 Spring AOP 的历史渊源,只有了解了 Aspect4J 和 Spring 的渊源能力了解有些注解上的兼容设计
  3. 如何反对 更多拦挡形式 来实现解耦,以满足更多场景需要呢?这就是 @Around, @Pointcut… 等的设计
  4. 那么 Spring 框架又是如何实现 AOP 的呢?这就引入 代理技术,分动态代理和动静代理,动静代理又蕴含 JDK 代理和 CGLIB 代理等

本节将在此基础上进一步解读 AOP 的含意以及 AOP 的应用形式;后续的文章还将深刻 AOP 的实现原理:

  • Spring 进阶 – Spring AOP 实现原理详解之切面实现
  • Spring 进阶 – Spring AOP 实现原理详解之 AOP 代理

如何了解 AOP

AOP 的实质也是为理解耦,它是一种设计思维;在了解时也应该简化了解。

AOP 是什么

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程

AOP 最早是 AOP 联盟的组织提出的, 指定的一套标准,spring 将 AOP 的思维引入框架之中, 通过 预编译形式 运行期间动静代理 实现程序的对立保护的一种技术,

  • 先来看一个例子,如何给如下 UserServiceImpl 中所有办法增加进入办法的日志,
/**
 * @author pdai
 */
public class UserServiceImpl implements IUserService {

    /**
     * find user list.
     *
     * @return user list
     */
    @Override
    public List<User> findUserList() {System.out.println("execute method:findUserList");
        return Collections.singletonList(new User("pdai", 18));
    }

    /**
     * add user
     */
    @Override
    public void addUser() {System.out.println("execute method:addUser");
        // do something
    }

}

咱们将记录日志性能解耦为日志切面,它的指标是解耦。进而引出 AOP 的理念:就是将扩散在各个业务逻辑代码中雷同的代码通过 横向切割 的形式抽取到一个独立的模块中!

OOP 面向对象编程,针对业务处理过程的实体及其属性和行为进行形象封装,以取得更加清晰高效的逻辑单元划分。而 AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程的某个步骤或阶段,以取得逻辑过程的中各局部之间低耦合的隔离成果。这两种设计思维在指标上有着实质的差别。

AOP 术语

首先让咱们从一些重要的 AOP 概念和术语开始。这些术语不是 Spring 特有的

  • 连接点(Jointpoint):示意须要在程序中插入横切关注点的扩大点,连接点可能是类初始化、办法执行、办法调用、字段调用或解决异样等等 ,Spring 只反对办法执行连接点,在 AOP 中示意为 在哪里干
  • 切入点(Pointcut):抉择一组相干连接点的模式,即能够认为连接点的汇合,Spring 反对 perl5 正则表达式和 AspectJ 切入点模式,Spring 默认应用 AspectJ 语法,在 AOP 中示意为 在哪里干的汇合
  • 告诉(Advice):在连接点上执行的行为,告诉提供了在 AOP 中须要在切入点所抉择的连接点处进行扩大现有行为的伎俩;包含前置告诉(before advice)、后置告诉 (after advice)、盘绕告诉(around advice),在 Spring 中通过代理模式实现 AOP,并通过拦截器模式以盘绕连接点的拦截器链织入告诉;在 AOP 中示意为 干什么
  • 方面 / 切面(Aspect):横切关注点的模块化,比方上边提到的日志组件。能够认为是告诉、引入和切入点的组合;在 Spring 中能够应用 Schema 和 @AspectJ 形式进行组织实现;在 AOP 中示意为 在哪干和干什么汇合
  • 引入(inter-type declaration):也称为外部类型申明,为已有的类增加额定新的字段或办法,Spring 容许引入新的接口(必须对应一个实现)到所有被代理对象(指标对象), 在 AOP 中示意为 干什么(引入什么)
  • 指标对象(Target Object):须要被织入横切关注点的对象,即该对象是切入点抉择的对象,须要被告诉的对象,从而也可称为被告诉对象;因为 Spring AOP 通过代理模式实现,从而这个对象永远是被代理对象,在 AOP 中示意为 对谁干
  • 织入(Weaving):把切面连贯到其它的应用程序类型或者对象上,并创立一个被告诉的对象。这些能够在编译时(例如应用 AspectJ 编译器),类加载时和运行时实现。Spring 和其余纯 Java AOP 框架一样,在运行时实现织入。在 AOP 中示意为 怎么实现的
  • AOP 代理(AOP Proxy):AOP 框架应用代理模式创立的对象,从而实现在连接点处插入告诉(即利用切面),就是通过代理来对指标对象利用切面。在 Spring 中,AOP 代理能够用 JDK 动静代理或 CGLIB 代理实现,而通过拦截器模型利用切面。在 AOP 中示意为 怎么实现的一种典型形式

告诉类型

  • 前置告诉(Before advice):在某连接点之前执行的告诉,但这个告诉不能阻止连接点之前的执行流程(除非它抛出一个异样)。
  • 后置告诉(After returning advice):在某连接点失常实现后执行的告诉:例如,一个办法没有抛出任何异样,失常返回。
  • 异样告诉(After throwing advice):在办法抛出异样退出时执行的告诉。
  • 最终告诉(After (finally) advice):当某连接点退出的时候执行的告诉(不论是失常返回还是异样退出)。
  • 盘绕告诉(Around Advice):突围一个连接点的告诉,如办法调用。这是最弱小的一种告诉类型。盘绕告诉能够在办法调用前后实现自定义的行为。它也会抉择是否继续执行连接点或间接返回它本人的返回值或抛出异样来完结执行。

盘绕告诉是最罕用的告诉类型。和 AspectJ 一样,Spring 提供所有类型的告诉,咱们举荐你应用尽可能简略的告诉类型来实现须要的性能。例如,如果你只是须要一个办法的返回值来更新缓存,最好应用后置告诉而不是盘绕告诉,只管盘绕告诉也能实现同样的事件。用最合适的告诉类型能够使得编程模型变得简略,并且可能防止很多潜在的谬误。比方,你不须要在 JoinPoint 上调用用于盘绕告诉的 proceed()办法,就不会有调用的问题。

咱们把这些术语串联到一起,不便了解

Spring AOP 和 AspectJ 是什么关系

  • 首先 AspectJ 是什么

AspectJ 是一个 java 实现的 AOP 框架,它可能对 java 代码进行 AOP 编译(个别在编译期进行),让 java 代码具备 AspectJ 的 AOP 性能(当然须要非凡的编译器)

能够这样说 AspectJ 是目前实现 AOP 框架中最成熟,性能最丰盛的语言,更侥幸的是,AspectJ 与 java 程序齐全兼容,简直是无缝关联,因而对于有 java 编程根底的工程师,上手和应用都非常容易。

  • 其次,为什么须要理分明 Spring AOP 和 AspectJ 的关系

咱们看下 @Aspect 以及加强的几个注解,为什么不是 Spring 包,而是来源于 aspectJ 呢?

  • Spring AOP 和 AspectJ 是什么关系
  1. AspectJ 是更强的 AOP 框架,是实际意义的AOP 规范
  2. Spring 为何不写相似 AspectJ 的框架?Spring AOP 应用纯 Java 实现, 它不须要专门的编译过程, 它一个 重要的准则就是无侵入性(non-invasiveness); Spring 小组齐全有能力写相似的框架,只是 Spring AOP 素来没有打算通过提供一种全面的 AOP 解决方案来与 AspectJ 竞争。Spring 的开发小组置信无论是基于代理(proxy-based)的框架如 Spring AOP 或者是成熟的框架如 AspectJ 都是很有价值的,他们之间应该是 互补而不是竞争的关系
  3. Spring 小组喜爱 @AspectJ 注解格调更胜于 Spring XML 配置;所以 在 Spring 2.0 应用了和 AspectJ 5 一样的注解,并应用 AspectJ 来做切入点解析和匹配 然而,AOP 在运行时仍旧是纯的 Spring AOP,并不依赖于 AspectJ 的编译器或者织入器(weaver)
  4. Spring 2.5 对 AspectJ 的反对:在一些环境下,减少了对 AspectJ 的装载时编织反对,同时提供了一个新的 bean 切入点。
  • 更多对于 AspectJ

理解 AspectJ 利用到 java 代码的过程(这个过程称为织入),对于织入这个概念,能够简略了解为 aspect(切面)利用到指标函数 (类) 的过程。

对于这个过程,个别分为 动静织入 动态织入

  1. 动静织入的形式是在运行时动静将要加强的代码织入到指标类中,这样往往是通过动静代理技术实现的,如 Java JDK 的动静代理 (Proxy,底层通过反射实现) 或者 CGLIB 的动静代理(底层通过继承实现),Spring AOP 采纳的就是基于运行时加强的代理技术
  2. ApectJ 采纳的就是动态织入的形式。ApectJ 次要采纳的是编译期织入,在这个期间应用 AspectJ 的 acj 编译器 (相似 javac) 把 aspect 类编译成 class 字节码后,在 java 指标类编译时织入,即先编译 aspect 类再编译指标类。

AOP 的配置形式

Spring AOP 反对对 XML 模式和基于 @AspectJ 注解的两种配置形式。

XML Schema 配置形式

Spring 提供了应用 ”aop” 命名空间来定义一个切面,咱们来看个例子(例子代码):

  • 定义指标类
package tech.pdai.springframework.service;

/**
 * @author pdai
 */
public class AopDemoServiceImpl {public void doMethod1() {System.out.println("AopDemoServiceImpl.doMethod1()");
    }

    public String doMethod2() {System.out.println("AopDemoServiceImpl.doMethod2()");
        return "hello world";
    }

    public String doMethod3() throws Exception {System.out.println("AopDemoServiceImpl.doMethod3()");
        throw new Exception("some exception");
    }
}
  • 定义切面类
package tech.pdai.springframework.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * @author pdai
 */
public class LogAspect {

    /**
     * 盘绕告诉.
     *
     * @param pjp pjp
     * @return obj
     * @throws Throwable exception
     */
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {System.out.println("-----------------------");
        System.out.println("盘绕告诉: 进入办法");
        Object o = pjp.proceed();
        System.out.println("盘绕告诉: 退出办法");
        return o;
    }

    /**
     * 前置告诉.
     */
    public void doBefore() {System.out.println("前置告诉");
    }

    /**
     * 后置告诉.
     *
     * @param result return val
     */
    public void doAfterReturning(String result) {System.out.println("后置告诉, 返回值:" + result);
    }

    /**
     * 异样告诉.
     *
     * @param e exception
     */
    public void doAfterThrowing(Exception e) {System.out.println("异样告诉, 异样:" + e.getMessage());
    }

    /**
     * 最终告诉.
     */
    public void doAfter() {System.out.println("最终告诉");
    }

}
  • XML 配置 AOP
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/aop
 http://www.springframework.org/schema/aop/spring-aop.xsd
 http://www.springframework.org/schema/context
 http://www.springframework.org/schema/context/spring-context.xsd
">

    <context:component-scan base-package="tech.pdai.springframework" />

    <aop:aspectj-autoproxy/>

    <!-- 指标类 -->
    <bean id="demoService" class="tech.pdai.springframework.service.AopDemoServiceImpl">
        <!-- configure properties of bean here as normal -->
    </bean>

    <!-- 切面 -->
    <bean id="logAspect" class="tech.pdai.springframework.aspect.LogAspect">
        <!-- configure properties of aspect here as normal -->
    </bean>

    <aop:config>
        <!-- 配置切面 -->
        <aop:aspect ref="logAspect">
            <!-- 配置切入点 -->
            <aop:pointcut id="pointCutMethod" expression="execution(* tech.pdai.springframework.service.*.*(..))"/>
            <!-- 盘绕告诉 -->
            <aop:around method="doAround" pointcut-ref="pointCutMethod"/>
            <!-- 前置告诉 -->
            <aop:before method="doBefore" pointcut-ref="pointCutMethod"/>
            <!-- 后置告诉;returning 属性:用于设置后置告诉的第二个参数的名称,类型是 Object -->
            <aop:after-returning method="doAfterReturning" pointcut-ref="pointCutMethod" returning="result"/>
            <!-- 异样告诉:如果没有异样,将不会执行加强;throwing 属性:用于设置告诉第二个参数的的名称、类型 -->
            <aop:after-throwing method="doAfterThrowing" pointcut-ref="pointCutMethod" throwing="e"/>
            <!-- 最终告诉 -->
            <aop:after method="doAfter" pointcut-ref="pointCutMethod"/>
        </aop:aspect>
    </aop:config>

    <!-- more bean definitions for data access objects go here -->
</beans>
  • 测试类
/**
  * main interfaces.
  *
  * @param args args
  */
public static void main(String[] args) {
    // create and configure beans
    ApplicationContext context = new ClassPathXmlApplicationContext("aspects.xml");

    // retrieve configured instance
    AopDemoServiceImpl service = context.getBean("demoService", AopDemoServiceImpl.class);

    // use configured instance
    service.doMethod1();
    service.doMethod2();
    try {service.doMethod3();
    } catch (Exception e) {// e.printStackTrace();
    }
}
  • 输入后果
-----------------------
盘绕告诉: 进入办法
前置告诉
AopDemoServiceImpl.doMethod1()
盘绕告诉: 退出办法
最终告诉
-----------------------
盘绕告诉: 进入办法
前置告诉
AopDemoServiceImpl.doMethod2()
盘绕告诉: 退出办法
最终告诉
后置告诉, 返回值: hello world
-----------------------
盘绕告诉: 进入办法
前置告诉
AopDemoServiceImpl.doMethod3()
最终告诉
异样告诉, 异样: some exception

AspectJ 注解形式

基于 XML 的申明式 AspectJ 存在一些有余,须要在 Spring 配置文件配置大量的代码信息,为了解决这个问题,Spring 应用了 @AspectJ 框架为 AOP 的实现提供了一套注解。

注解名称 解释
@Aspect 用来定义一个切面。
@pointcut 用于定义切入点表达式。在应用时还须要定义一个蕴含名字和任意参数的办法签名来示意切入点名称,这个办法签名就是一个返回值为 void,且办法体为空的一般办法。
@Before 用于定义前置告诉,相当于 BeforeAdvice。在应用时,通常须要指定一个 value 属性值,该属性值用于指定一个切入点表达式(能够是已有的切入点,也能够间接定义切入点表达式)。
@AfterReturning 用于定义后置告诉,相当于 AfterReturningAdvice。在应用时能够指定 pointcut / value 和 returning 属性,其中 pointcut / value 这两个属性的作用一样,都用于指定切入点表达式。 returning 属性值用于示意 Advice 办法中定义与此同名的形参,该形参可用于拜访指标办法的返回值。
@Around 用于定义盘绕告诉,相当于 MethodInterceptor。在应用时须要指定一个 value 属性,该属性用于指定该告诉被植入的切入点。
@After-Throwing 用于定义异样告诉来处理程序中未解决的异样,相当于 ThrowAdvice。在应用时可指定 pointcut / value 和 throwing 属性。其中 pointcut/value 用于指定切入点表达式,而 throwing 属性值用于指定 - 一个形参名来示意 Advice 办法中可定义与此同名的形参,该形参可用于拜访指标办法抛出的异样。
@After 用于定义最终 final 告诉,不论是否异样,该告诉都会执行。应用时须要指定一个 value 属性,该属性用于指定该告诉被植入的切入点。
@DeclareParents 用于定义引介告诉,相当于 IntroductionInterceptor (不要求把握)。

Spring AOP 的实现形式是动静织入,动静织入的形式是在运行时动静将要加强的代码织入到指标类中,这样往往是通过动静代理技术实现的;如 Java JDK 的动静代理 (Proxy,底层通过反射实现) 或者 CGLIB 的动静代理(底层通过继承实现),Spring AOP 采纳的就是基于运行时加强的代理技术。所以咱们看下如下的两个例子(例子代码 中 05 模块):

  • 基于 JDK 代理例子
  • 基于 Cglib 代理例子

接口应用 JDK 代理

  • 定义接口
/**
 * Jdk Proxy Service.
 *
 * @author pdai
 */
public interface IJdkProxyService {void doMethod1();

    String doMethod2();

    String doMethod3() throws Exception;}
  • 实现类
/**
 * @author pdai
 */
@Service
public class JdkProxyDemoServiceImpl implements IJdkProxyService {

    @Override
    public void doMethod1() {System.out.println("JdkProxyServiceImpl.doMethod1()");
    }

    @Override
    public String doMethod2() {System.out.println("JdkProxyServiceImpl.doMethod2()");
        return "hello world";
    }

    @Override
    public String doMethod3() throws Exception {System.out.println("JdkProxyServiceImpl.doMethod3()");
        throw new Exception("some exception");
    }
}
  • 定义切面
package tech.pdai.springframework.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * @author pdai
 */
@EnableAspectJAutoProxy
@Component
@Aspect
public class LogAspect {

    /**
     * define point cut.
     */
    @Pointcut("execution(* tech.pdai.springframework.service.*.*(..))")
    private void pointCutMethod() {}


    /**
     * 盘绕告诉.
     *
     * @param pjp pjp
     * @return obj
     * @throws Throwable exception
     */
    @Around("pointCutMethod()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {System.out.println("-----------------------");
        System.out.println("盘绕告诉: 进入办法");
        Object o = pjp.proceed();
        System.out.println("盘绕告诉: 退出办法");
        return o;
    }

    /**
     * 前置告诉.
     */
    @Before("pointCutMethod()")
    public void doBefore() {System.out.println("前置告诉");
    }


    /**
     * 后置告诉.
     *
     * @param result return val
     */
    @AfterReturning(pointcut = "pointCutMethod()", returning = "result")
    public void doAfterReturning(String result) {System.out.println("后置告诉, 返回值:" + result);
    }

    /**
     * 异样告诉.
     *
     * @param e exception
     */
    @AfterThrowing(pointcut = "pointCutMethod()", throwing = "e")
    public void doAfterThrowing(Exception e) {System.out.println("异样告诉, 异样:" + e.getMessage());
    }

    /**
     * 最终告诉.
     */
    @After("pointCutMethod()")
    public void doAfter() {System.out.println("最终告诉");
    }

}
  • 输入
-----------------------
盘绕告诉: 进入办法
前置告诉
JdkProxyServiceImpl.doMethod1()
最终告诉
盘绕告诉: 退出办法
-----------------------
盘绕告诉: 进入办法
前置告诉
JdkProxyServiceImpl.doMethod2()
后置告诉, 返回值: hello world
最终告诉
盘绕告诉: 退出办法
-----------------------
盘绕告诉: 进入办法
前置告诉
JdkProxyServiceImpl.doMethod3()
异样告诉, 异样: some exception
最终告诉

非接口应用 Cglib 代理

  • 类定义
/**
 * Cglib proxy.
 *
 * @author pdai
 */
@Service
public class CglibProxyDemoServiceImpl {public void doMethod1() {System.out.println("CglibProxyDemoServiceImpl.doMethod1()");
    }

    public String doMethod2() {System.out.println("CglibProxyDemoServiceImpl.doMethod2()");
        return "hello world";
    }

    public String doMethod3() throws Exception {System.out.println("CglibProxyDemoServiceImpl.doMethod3()");
        throw new Exception("some exception");
    }
}
  • 切面定义

和下面雷同

  • 输入
-----------------------
盘绕告诉: 进入办法
前置告诉
CglibProxyDemoServiceImpl.doMethod1()
最终告诉
盘绕告诉: 退出办法
-----------------------
盘绕告诉: 进入办法
前置告诉
CglibProxyDemoServiceImpl.doMethod2()
后置告诉, 返回值: hello world
最终告诉
盘绕告诉: 退出办法
-----------------------
盘绕告诉: 进入办法
前置告诉
CglibProxyDemoServiceImpl.doMethod3()
异样告诉, 异样: some exception
最终告诉

AOP 应用问题小结

这里总结下理论开发中会遇到的一些问题:

切入点(pointcut)的申明规定?

Spring AOP 用户可能会常常应用 execution 切入点批示符。执行表达式的格局如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
  • ret-type-pattern 返回类型模式, name-pattern 名字模式和 param-pattern 参数模式是必选的,其它局部都是可选的。返回类型模式决定了办法的返回类型必须顺次匹配一个连接点。你会应用的最频繁的返回类型模式是 * 它代表了匹配任意的返回类型
  • declaring-type-pattern, 一个全限定的类型名将只会匹配返回给定类型的办法。
  • name-pattern 名字模式匹配的是办法名。你能够应用 * 通配符作为所有或者局部命名模式。
  • param-pattern 参数模式略微有点简单:()匹配了一个不承受任何参数的办法,而 (..) 匹配了一个承受任意数量参数的办法(零或者更多)。模式 () 匹配了一个承受一个任何类型的参数的办法。模式 (,String) 匹配了一个承受两个参数的办法,第一个能够是任意类型,第二个则必须是 String 类型。

对应到咱们下面的例子:

上面给出一些通用切入点表达式的例子。

// 任意公共办法的执行:execution(public * *(..))// 任何一个名字以“set”开始的办法的执行:execution(* set*(..))// AccountService 接口定义的任意办法的执行:execution(* com.xyz.service.AccountService.*(..))// 在 service 包中定义的任意办法的执行:execution(* com.xyz.service.*.*(..))// 在 service 包或其子包中定义的任意办法的执行:execution(* com.xyz.service..*.*(..))// 在 service 包中的任意连接点(在 Spring AOP 中只是办法执行):within(com.xyz.service.*)// 在 service 包或其子包中的任意连接点(在 Spring AOP 中只是办法执行):within(com.xyz.service..*)// 实现了 AccountService 接口的代理对象的任意连接点(在 Spring AOP 中只是办法执行):this(com.xyz.service.AccountService)// 'this' 在绑定表单中更加罕用

// 实现 AccountService 接口的指标对象的任意连接点(在 Spring AOP 中只是办法执行):target(com.xyz.service.AccountService)// 'target' 在绑定表单中更加罕用

// 任何一个只承受一个参数,并且运行时所传入的参数是 Serializable 接口的连接点(在 Spring AOP 中只是办法执行)args(java.io.Serializable)// 'args' 在绑定表单中更加罕用; 请留神在例子中给出的切入点不同于 execution(* *(java.io.Serializable)):args 版本只有在动静运行时候传入参数是 Serializable 时才匹配,而 execution 版本在办法签名中申明只有一个 Serializable 类型的参数时候匹配。// 指标对象中有一个 @Transactional 注解的任意连接点(在 Spring AOP 中只是办法执行)@target(org.springframework.transaction.annotation.Transactional)// '@target' 在绑定表单中更加罕用

// 任何一个指标对象申明的类型有一个 @Transactional 注解的连接点(在 Spring AOP 中只是办法执行):@within(org.springframework.transaction.annotation.Transactional)// '@within' 在绑定表单中更加罕用

// 任何一个执行的办法有一个 @Transactional 注解的连接点(在 Spring AOP 中只是办法执行)@annotation(org.springframework.transaction.annotation.Transactional)// '@annotation' 在绑定表单中更加罕用

// 任何一个只承受一个参数,并且运行时所传入的参数类型具备 @Classified 注解的连接点(在 Spring AOP 中只是办法执行)@args(com.xyz.security.Classified)// '@args' 在绑定表单中更加罕用

// 任何一个在名为 'tradeService' 的 Spring bean 之上的连接点(在 Spring AOP 中只是办法执行)bean(tradeService)// 任何一个在名字匹配通配符表达式 '*Service' 的 Spring bean 之上的连接点(在 Spring AOP 中只是办法执行)bean(*Service)

此外 Spring 反对如下三个逻辑运算符来组合切入点表达式

&&:要求连接点同时匹配两个切入点表达式
||:要求连接点匹配任意个切入点表达式
!::要求连接点不匹配指定的切入点表达式

多种加强告诉的程序?

如果有多个告诉想要在同一连接点运行会产生什么?Spring AOP 遵循跟 AspectJ 一样的优先规定来确定告诉执行的程序。在“进入”连接点的状况下,最高优先级的告诉会先执行(所以给定的两个前置告诉中,优先级高的那个会先执行)。在“退出”连接点的状况下,最高优先级的告诉会最初执行。(所以给定的两个后置告诉中,优先级高的那个会第二个执行)。

当定义在不同的切面里的两个告诉都须要在一个雷同的连接点中运行,那么除非你指定,否则执行的程序是未知的。你能够通过指定优先级来管制执行程序。在规范的 Spring 办法中能够在切面类中实现 org.springframework.core.Ordered 接口或者用 Order 注解 做到这一点。在两个切面中,Ordered.getValue()办法返回值(或者注解值)较低的那个有更高的优先级。

当定义在雷同的切面里的两个告诉都须要在一个雷同的连接点中运行,执行的程序是未知的(因为这里没有办法通过反射 javac 编译的类来获取申明程序)。思考在每个切面类中按连接点压缩这些告诉办法到一个告诉办法,或者重构告诉的片段到各自的切面类中 – 它能在切面级别进行排序。

Spring AOP 和 AspectJ 之间的要害区别?

AspectJ 能够做 Spring AOP 干不了的事件,它是 AOP 编程的齐全解决方案,Spring AOP 则致力于解决企业级开发中最广泛的 AOP(办法织入)。

下表总结了 Spring AOP 和 AspectJ 之间的要害区别:

Spring AOP AspectJ
在纯 Java 中实现 应用 Java 编程语言的扩大实现
不须要独自的编译过程 除非设置 LTW,否则须要 AspectJ 编译器 (ajc)
只能应用运行时织入 运行时织入不可用。反对编译时、编译后和加载时织入
性能不强 - 仅反对办法级编织 更弱小 – 能够编织字段、办法、构造函数、动态初始值设定项、最终类 / 办法等 ……。
只能在由 Spring 容器治理的 bean 上实现 能够在所有域对象上实现
仅反对办法执行切入点 反对所有切入点
代理是由指标对象创立的, 并且切面利用在这些代理上 在执行应用程序之前 (在运行时) 前, 各方面间接在代码中进行织入
比 AspectJ 慢多了 更好的性能
易于学习和利用 绝对于 Spring AOP 来说更简单

Spring AOP 还是齐全用 AspectJ?

以下 Spring 官网的答复:(总结来说就是 Spring AOP 更易用,AspectJ 更弱小)。

  • Spring AOP 比齐全应用 AspectJ 更加简略,因为它不须要引入 AspectJ 的编译器/织入器到你开发和构建过程中。如果你 仅仅须要在 Spring bean 上告诉执行操作,那么 Spring AOP 是适合的抉择
  • 如果你须要告诉 domain 对象或其它没有在 Spring 容器中治理的任意对象,那么你须要应用 AspectJ。
  • 如果你想告诉除了简略的办法执行之外的连接点(如:调用连接点、字段 get 或 set 的连接点等等),也须要应用 AspectJ。

当应用 AspectJ 时,你能够抉择应用 AspectJ 语言(也称为“代码格调”)或 @AspectJ 注解格调。如果切面在你的设计中表演一个很大的角色,并且你能在 Eclipse 等 IDE 中应用 AspectJ Development Tools (AJDT),那么首选 AspectJ 语言 :- 因为该语言专门被设计用来编写切面,所以会更清晰、更简略。如果你没有应用 Eclipse 等 IDE,或者在你的利用中只有很少的切面并没有作为一个次要的角色,你或者应该思考应用 @AspectJ 格调 并在你的 IDE 中附加一个一般的 Java 编辑器,并且在你的构建脚本中减少切面织入(链接)的段落。

参考文章

http://shouce.jb51.net/spring…

https://www.cnblogs.com/linhp…

https://www.cnblogs.com/bj-xi…

更多文章

首先,从 Spring 框架的整体架构和组成对整体框架有个认知。

  • Spring 根底 – Spring 和 Spring 框架组成

    • Spring 是什么?它是怎么诞生的?有哪些次要的组件和外围性能呢? 本文通过这几个问题帮忙你构筑 Spring 和 Spring Framework 的整体认知。

其次,通过案例引出 Spring 的外围(IoC 和 AOP),同时对 IoC 和 AOP 进行案例应用剖析。

  • Spring 根底 – Spring 简略例子引入 Spring 的外围

    • 上文中咱们简略介绍了 Spring 和 Spring Framework 的组件,那么这些 Spring Framework 组件是如何配合工作的呢?本文次要承接上文,向你展现 Spring Framework 组件的典型利用场景和基于这个场景设计出的简略案例,并以此引出 Spring 的外围要点,比方 IOC 和 AOP 等;在此基础上还引入了不同的配置形式,如 XML,Java 配置和注解形式的差别。
  • Spring 根底 – Spring 外围之管制反转(IOC)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 IoC 的根底含意,同时以此发散了一些 IoC 相干知识点; 本节将在此基础上进一步解读 IOC 的含意以及 IOC 的应用形式
  • Spring 根底 – Spring 外围之面向切面编程(AOP)

    • 在 Spring 根底 – Spring 简略例子引入 Spring 的外围中向你展现了 AOP 的根底含意,同时以此发散了一些 AOP 相干知识点; 本节将在此基础上进一步解读 AOP 的含意以及 AOP 的应用形式。

基于 Spring 框架和 IOC,AOP 的根底,为构建下层 web 利用,须要进一步学习 SpringMVC。

  • Spring 根底 – SpringMVC 申请流程和案例

    • 前文咱们介绍了 Spring 框架和 Spring 框架中最为重要的两个技术点(IOC 和 AOP),那咱们如何更好的构建下层的利用呢(比方 web 利用),这便是 SpringMVC;Spring MVC 是 Spring 在 Spring Container Core 和 AOP 等技术根底上,遵循上述 Web MVC 的标准推出的 web 开发框架,目标是为了简化 Java 栈的 web 开发。本文次要介绍 SpringMVC 的申请流程和根底案例的编写和运行。

Spring 进阶 – IoC,AOP 以及 SpringMVC 的源码剖析

  • Spring 进阶 – Spring IOC 实现原理详解之 IOC 体系结构设计

    • 在对 IoC 有了初步的认知后,咱们开始对 IOC 的实现原理进行深刻了解。本文将帮忙你站在设计者的角度去看 IOC 最顶层的结构设计
  • Spring 进阶 – Spring IOC 实现原理详解之 IOC 初始化流程

    • 上文,咱们看了 IOC 设计要点和设计构造;紧接着这篇,咱们能够看下源码的实现了:Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的
  • Spring 进阶 – Spring IOC 实现原理详解之 Bean 实例化(生命周期, 循环依赖等)

    • 上文,咱们看了 IOC 设计要点和设计构造;以及 Spring 如何实现将资源配置(以 xml 配置为例)通过加载,解析,生成 BeanDefination 并注册到 IoC 容器中的;容器中寄存的是 Bean 的定义即 BeanDefinition 放到 beanDefinitionMap 中,实质上是一个ConcurrentHashMap<String, Object>;并且 BeanDefinition 接口中蕴含了这个类的 Class 信息以及是否是单例等。那么如何从 BeanDefinition 中实例化 Bean 对象呢,这是本文次要钻研的内容?
  • Spring 进阶 – Spring AOP 实现原理详解之切面实现

    • 前文,咱们剖析了 Spring IOC 的初始化过程和 Bean 的生命周期等,而 Spring AOP 也是基于 IOC 的 Bean 加载来实现的。本文次要介绍 Spring AOP 原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应 Advice,并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor,为后续交给代理加强实现做筹备的过程)。
  • Spring 进阶 – Spring AOP 实现原理详解之 AOP 代理

    • 上文咱们介绍了 Spring AOP 原理解析的切面实现过程(将切面类的所有切面办法依据应用的注解生成对应 Advice,并将 Advice 连同切入点匹配器和切面类等信息一并封装到 Advisor)。本文在此基础上持续介绍,代理(cglib 代理和 JDK 代理)的实现过程。
  • Spring 进阶 – Spring AOP 实现原理详解之 Cglib 代理实现

    • 咱们在前文中曾经介绍了 SpringAOP 的切面实现和创立动静代理的过程,那么动静代理是如何工作的呢?本文次要介绍 Cglib 动静代理的案例和 SpringAOP 实现的原理。
  • Spring 进阶 – Spring AOP 实现原理详解之 JDK 代理实现

    • 上文咱们学习了 SpringAOP Cglib 动静代理的实现,本文次要是 SpringAOP JDK 动静代理的案例和实现局部。
  • Spring 进阶 – SpringMVC 实现原理之 DispatcherServlet 初始化的过程

    • 前文咱们有了 IOC 的源码根底以及 SpringMVC 的根底,咱们便能够进一步深刻了解 SpringMVC 次要实现原理,蕴含 DispatcherServlet 的初始化过程和 DispatcherServlet 解决申请的过程的源码解析。本文是第一篇:DispatcherServlet 的初始化过程的源码解析。
  • Spring 进阶 – SpringMVC 实现原理之 DispatcherServlet 解决申请的过程

    • 前文咱们有了 IOC 的源码根底以及 SpringMVC 的根底,咱们便能够进一步深刻了解 SpringMVC 次要实现原理,蕴含 DispatcherServlet 的初始化过程和 DispatcherServlet 解决申请的过程的源码解析。本文是第二篇:DispatcherServlet 解决申请的过程的源码解析。

正文完
 0