aop:
【1】原理:通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能。
【2】概念:
1,切面(Aspect)
事务、日志、权限等都是切面,切面是类
2,通知(Advice)
切面中的方法就是通知,通知是方法
1)前置通知:在目标方法执行之前执行。无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常
2)后置通知:在目标方法执行之后执行。当目标方法遇到异常,后置通知将不再执行。
后置通知可以接受目标方法的返回值,但是必须注意:后置通知的参数的名称和配置文件中returning="var"的值必须是一致的
3)最终通知:在目标方法执行之后执行。无论目标方法是否抛出异常,都执行,因为相当于finally
4)异常通知:在目标方法抛出异常退出时执行
接受目标方法抛出的异常信息
在异常通知方法中有一个参数Throwable ex
在配置文件中:<aop:after-throwing method="throwingMethod" pointcut-ref="perform" throwing="ex"/>
5)环绕通知:环绕通知可以控制目标方法的执行,如果不在环绕通知中调用ProceedingJoinPoint的proceed方法,目标方法不会执行
在proceed方法前面添加的东西相当于在前置通知里添加的,proceed方法后面添加的东西相当于在后置通知里添加的
3,目标对象(Target Object)
被一个或者多个切面所通知的对象,即被代理的的对象
4,切入点(Pointcut):又称切入点表达式
1)只有符合切入点表达式,才能让通知和目标方法结合在一起
2)切入点即一个条件表达式,故可以称为:切入点表达式。通常情况下,通知和一个切入点表达式关联
5,织入(Weaving)
形成代理对象的方法的过程
6,连接点(Joinpoint):
概念:在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。
说明:AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象:
1)任何一个通知都可以通过将第一个入参声明为JoinPoint类型,从而访问到连接点上下文的信息:
JoinPoint中用于获取上下文信息的方法:
java.lang.Object[] getArgs(): 获取连接点方法运行时的入参列表;
Signature getSignature() : 获取连接点的方法签名对象;
java.lang.Object getTarget(): 获取连接点所在的目标对象;
java.lang.Object getThis(): 获取代理对象本身;
2)如果是环绕通知,则使用org.aspectj.lang.ProceedingJoinPoint来表示连接点对象,它继承了JoinPoint接口,并新增了两个用于执行连接点方法的方法:
ProceedingJoinPoint:
java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法;
java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
7,引入(Introduction):
在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
好处:事务、日志、权限、目标方法之间是低耦合的
【3】在spring中使用AOP
1)配置命名空间
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
2)配置<aop:config>、<aop:aspect ref="">、<aop:pointcut expression="" id="" />及<aop:around method="" pointcut-ref=""/>等
<!-- 定义切面处理器 -->
<bean id="myAdvice" class="com.jxn.MyAdvice"></bean>
<!-- 所有的切面和通知都必须定义在aop:config元素内部 -->
<aop:config>
<!-- 配置切面:定义一个切面,并指定该切面的处理器 -->
<aop:aspect ref="myAdvice">
<!-- 声明切入点表达式 -->
<aop:pointcut expression="execution(public * com.jxn.MyAdvice.*(..))" id="txPointcut" />
<!--声明前置通知:method的值是切面处理器myAdvice中的方法-->
<aop:before method="before" pointcut-ref="txPointcut" />
<!--声明后置通知-->
<aop:after-returning method="afterReturning" pointcut-ref="txPointcut"/>
<!--声明异常通知-->
<aop:after-throwing method="throwsException" pointcut="execution(public * com.jxn.MyAdvice.*(..))"/>
<!--声明最终通知-->
<aop:after method="after" pointcut-ref="txPointcut"/>
<!--声明环绕通知-->
<aop:around method="around" pointcut-ref="txPointcut"/>
</aop:aspect>
</aop:config>
3)切入点表达式execution的格式如下:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
↓ ↓ ↓ ↓ ↓ ↓
eg: public final void java.lang.Object.wait (long,int) throws java.lang.InterruptedException
注:除了返回类型模式(ret-type-pattern)、名字模式和参数模式以外,所有的部分都是可选的。
参数模式:
模式()匹配了一个不接受任何参数的方法
模式(..)匹配了一个接受任意数量参数的方法(零或者更多)
模式(*)匹配了一个接受一个任何类型的参数的方法
模式(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。
execution(public * *(..)) 所有的公共方法
execution(* set*(..)) 以set开头的任意方法
execution(* com.xyz.service.AccountService.*(..)) com.xyz.service.AccountService类(或接口)中的所有的方法
execution(* com.xyz.service.*.*(..)) com.xyz.service包中的所有的类的所有的方法
execution(* com.xyz.service..*.*(..)) com.xyz.service包及子包中所有的类的所有的方法
execution(* com.jxn.service..*.*(String,?,Integer))
com.jxn.service包及子包中所有的类的满足以下条件的方法:第一个参数为String,第二个参数为任意类型,第三个参数为Integer类型的方法
【4】springAOP的具体加载步骤:
1,当spring容器启动的时候,加载了spring的配置文件
2,为配置文件中所有的bean创建对象
3,spring容器会解析aop:config的配置
解析切入点表达式,用切入点表达式和spring容器中的bean做匹配
如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知
如果匹配不成功,不会创建代理对象
4,在客户端用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果没有代理对象,则返回目标对象
【5】SpringAOP中的动态代理:
spring在运行期创建代理,不需要特殊的编译器。spring有两种代理方式:
1,若目标对象实现了若干接口,spring就会使用jdk动态代理(默认)
2,若目标对象没有实现任何接口,spring就使用cglib库生成目标对象的子类
Spring中的AOP
最新推荐文章于 2024-08-20 16:09:02 发布