Spring AOP 的基本使用

AOP

一、  什么是AOP

AOP是面向切面编程的简称,将程序运行过程分解成各个切面,可以在不修改源码的情况下给程序方法动态地添加功能,其底层实现是使用了动态代理模式;

二、  为什么要用AOP

分离系统中的各种关注点,将核心关注点和横切关注点分离开来(例如主业务程序和一些校验、日志、安全类的程序分离),实现业务逻辑和切面逻辑的解耦;

三、  实现效果

可以在方法的前后加入其它操作,比如:每次进行一次数据库查询操作之后,要添加一条日志,而添加日志的操作我们不用写在数据库查询这个方法内,而是交由AOP框架来做;使用动态代理后,每当我们执行这个数据库查询方法时,都会被AOP框架截获,从而进行方法增强,在方法的前后添加一些其它操作,我们自己调用的方法(例如这个数据库查询方法),称之为连接点(JoinPoint);

四、  使用注解的配置方式(使用 AspectJ 类库实现)

0、jar包

aspectiweaver.jar    aopaliance-.jar    asperctjrt.jar

  1. 基本配置

(0)IOC容器的注解方式配置照旧

(1)首先要把一个类声明成切面,就需要先把该类放到IOC容器里去(配置它的bean),再声明为一个切面,即在类上面加上@Aspect和@Component这两个注解

(2)在xml配置文件中加入aop命名空间,并在beans标签中写入:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

表示自动为匹配的方法生成代理对象;

  1. 前置通知(在目标方法执行前做的操作)

(1)   在切面类中加入一个方法,并在该方法前加入注解:

@Before(“execution(匹配的目标方法)”)

括号里的值称为切入点表达式,可以使用模糊匹配(比如*./正则)

表达式格式为:访问权限 返回值 方法的完整限定名(参数类型)

(2)   该前置通知方法可以有类型为JoinPoint参数

通过该参数可以获取目标方法的各种参数,例如方法名、参数值列表、类…

 

  1. 后置通知(在目标方法执行后做的操作)

(1)   使用@After注解,与前置通知功能相对应;

(2)   即使是目标方法发生了异常,后置通知照样执行;

(3)   前置通知和后置通知都无法访问目标方法的返回结果;

  1. 返回通知

(1)   在方法正常结束之后执行的方法;

(2)   在方法前使用:

@AfterReturning(value=” execution(匹配的目标方法)”,returning=”变量名”)

(3)   在方法的参数中加入  Object 变量名(该变量名与注解中的变量名对应),即可在返回通知方法内访问到目标方法的返回值;

  1. 异常通知

(1)   在目标方法发生异常的时候使用;

(2)   在方法前加入:

@AfterThrowing(value=” execution(匹配的目标方法)”,throwing=”变量名”)

(3)   在方法的参数中加入 异常类型 变量名(该变量名与注解中的变量名对应),即可在异常通知方法内访问到目标方法抛出的异常

  1. 上述四个通知的执行顺序

前置通知->返回|异常->后置通知;

  1. 环绕通知

(1)   环绕通知相当于动态代理的全过程,包含了以上四个通知

(2)   在方法前加入@Around(” execution(匹配的目标方法)”)

(3)   在方法的参数中 加入ProceedingJoinPoint类型参数,使用该类型参数的.proceed();方法可以控制目标方法的执行,并且该方法的返回值就是目标方法的返回值;

(4)   同样,ProceedingJoinPoint类型参数也可以获得目标方法的方法名和参数之类的值;

(5)   环绕通知方法必须得有返回值;

  1. 切面优先级

当有多个切面类时,使用@order(整数值),该注解写在切面类上,值越小,优先级越高;

  1. 重用切入点表达式

(0)   我们在每个通知方法的注解里都要写入切入点表达式executtion(….),里面的值很可能都是一样的,所以将其封装起来,以便重用;

(1)   写一个空方法(即方法里没有写任何代码的方法);

(2)   在方法前加入@Pointcut(” execution(匹配的目标方法)”);

(3)   而后,在其它通知的注解里,就可以将” execution(匹配的目标方法)”替换为该方法名;

(4)   跨类、跨包引用需要加上相应的类、包前缀;

五、  使用XML文件的配置方式(使用 AspectJ 类库实现)

  1. (1)切面类首先得是一个bean,所以在xml配置文件里,得先把切面类配置成一个bean;

(2)切面类、切面类里的通知方法代码都和注解配置时的代码一致;

  1. AOP基本配置

(1)先写<aop:config></aop:config>标签,而后所有的aop配置都写在该标签里面;

(2)配置切入点表达式:

<aop:pointcut expression=”execution(匹配的方法名)” id=”该表达式ID”>

该表达式指定了目标方法,该方法执行时,就会被引用了该表达式的通知截获;

(3)配置切面:

<aop:aspect ref=”切面类的bean” order=”优先级整数值”></aop:aspect>

而后,属于该切面的通知都写在该标签内;

(4)配置通知:

<aop:before method=”切面类里的方法名” pointcut-ref=”切入点表达式ID”>

返回、后置、异常、环绕通知配置类似;注意异常通知和返回通知的变量名称要和方法参数的变量名一致;

(5)<aop:config>标签内可以有多个切面配置标签,而切面标签内可以有多个通知;

(6)要使用目标bean时,就直接获取目标对象的bean,AOP会自动生成代理,自动织入;

  1.  
  2. 需要将通知各自写成一个类并继承相应的接口:
  3. 配置

六、  使用XML文件的配置方式(使用 spring框架自身aop实现)

      前置通知:MethodBeforeAdvice   环绕通知:MethodInterceptor 
            后置通知:AfterReturningAdvice  异常通知:ThrowsAdvice
 

(1)   将目标对象、各个通知方法(称为切面)都配置成bean

(2)   将代理对象也配置成bean ,id随意,

class=” org.springframework.aop.framework.ProxyFactoryBean”

分别有三个参数:

interceptorNames,值是切面的list(<list><value>通知/顾问beanID</value>…)

target,值为目标对象<ref=”目标对象beanID”>

interfaces,是目标对象实现的接口list(<list><value>接口路径</value>…)

(3)   要使用目标对象时,获取代理对象的beanID;

  1. 使用 名称匹配方法切入点 顾问(advisor)

(0)   顾问包装通知,让通知更精细地织入

(1)   没有使用顾问时,默认地,会为目标对象的全部方法实现通知操作,使用顾问后,就可以更精细地指定拦截哪些方法;

顾问也要先配置成bean,id随意

class=”org.springframework.aop.support.NameMatchMethodPointcutAdvisor”

(2)   该顾问有两三个参数:advice,值为引用类型,引用通知的beanID(即想让某个通知精确地作用某个某个/些方法);mappedName/ mappedNames,第一个为value类型,指定目标方法名,第二个为array类型,指定多个方法名(第二个的值也可以写value里,各方法名用逗号隔开);

(3)   mappedNames的值可以用*号去匹配,比如do*,指定以do开头的所有方法

(4)   顾问也是切面,所以要使用顾问时,将其像通知一样放入代理对象bean里的interceptorNames参数

  1. 使用 正则表达式匹配方法切入点 顾问(advisor)

(0)   与”名称匹配方法切入点”写法类似;

(1)   class要换成org.springframework.aop.support.RegexpMethodPointcutAdvisor

(2)   该顾问也要三个参数:advice,使用方法同上;

pattern/patterns:其value值为正则表达式(其实多个表达式都可以写在pattern里,用|隔开);

(3)   常用的正则表达式运算符有 . 表示任意单个字符,

* 表示前一个字符出现0次或多次   + 表示前一个字符出现1次或多次;

(4)   正则表达式匹配的是方法的全限定名(包.类.方法名);

(5)   例:  .*do.* 全限定名中表示包含do的方法;

  1. 默认Advisor自动代理器

(0)   没使用自动代理器时,若有多个目标对象,则需要手动配置多个代理对象,造成代码冗余且耗费时间;

(1)   默认Advisor自动代理器底层使用了bean的后置处理器(BeanPostProcessor),在bean初始化之后,会自动地帮配置文件中的所有目标对象生成代理对象,并自动绑定配置文件中的顾问而需要使用目标对象时,只需要获取目标对象的beanID就可以了;

(2)   配置:目标对象、通知、顾问的bean配置都照旧,只需加多以下bean:

<bean class=”org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator”>

(3)   弊端:该自动代理的切面只能是Advisor;不能指定目标对象;

  1. Bean名称自动代理生成器

(0)   可以指定需要代理的目标对象;需要手动指定切面(可以是通知也可以是顾问);

(1)   配置:目标对象、通知、顾问的bean配置都照旧,只需加多以下bean:

<bean class=”org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator”>

(2)   该bean需要使用两个参数:beanNames,value值为目标对象的beanID,当有多个beanID时使用逗号隔开;  interceptorNames,value值为切面beanID,当有多个beanID时使用逗号隔开;

  1. CGLIB代理

(0)   以上所说的代理皆为JDK动态代理,在代理对象配置中需要指定目标对象的接口;

(1)   而当目标对象没有接口时,则Spring AOP会自动使用CGLIB代理(当然在代理对象配置中也不用指定接口了);

(2)   当目标对象有接口时,也可以指定使用CGLIB代理:在代理对象配置中,加上ProxyTargetClass参数,该参数value值为true|false,为true时,强制使用CGLIB代理;

也可以使用optimize参数,value值也是true|false,为true时,指定了接口则使用jdk代理,无接口则使用CGLIB代理;

(3)   JDK代理和CGLIB代理区别:JDK创建代理对象快,但执行慢;CGLIB则相反;

(4)   注:JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

ps.请另外查阅开发过程中需要使用的jar包

原文地址:https://www.cnblogs.com/Drajun/p/8278839.html