AOP: 面向切面编程,通过预编译方式和运行期动态代理实现的统一维护的一种技术,主要用于日志记录,性能统计,安全控制,事物处理,异常处理等。
AOP实现方式(两种):
·预编译—AspectJ
·运行期动态代理(JDK动态代理、CGLib动态代理)—SpringAOP、JbossAOP
AOP相关概念
名称 | 说明 |
---|---|
切面(Aspect) | 一个关注点的模块化,这个关注点可能会横切多个对象 |
连接点(Joinpoint) | 程序执行过程中的某个特定的点 |
通知(Advice) | 在切面的某个特定的连接点上执行的动作 |
切入点(Pointcut) | 匹配连接点的断言,在AOP中通知和一个切入点表达式关联 |
引入(Introdoction) | 在不修改类代码的前提下,为类添加新的方法和属性 |
目标对象(Target Object) | 被一个或者多个切面所通知的对象 |
AOP代理(AOP Proxy) | AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能) |
织入(Weaving) | 把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入 |
Advice的类型:
1、Before advice(前置通知):在某连接点之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
2、After-returning advice(返回后通知):在某连接点正常完成后执行的通知
3、After-throwing advice(抛出异常后通知):在方法抛出异常退出时执行的通知
4、After(finally) advice(后通知):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
5、Around Advice(环绕通知):包围一个连接点的通知
aspect(切面)
· 切面申明
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="..">
...
</bean>
//将“aBean”这个bean声明为一个切面,id为myAspect
pointcut(切点)
· 配置切入点
<aop:pointcut id="businessService" expression="..."
//当匹配有参数的方法时,需在末尾标明形参:(参数类型)and args(参数名)
//expression属性为配置何时何处执行动作(需要时查找相关文档即可)
advice(动作)
——以Before advice(前置通知类型)为例
<aop:brfore method="方法" pointcut-ref="切点id " / pointcut="execution(...)"/>
(指定pointcut的两种方法)
· Around advice:
-通知方法的第一个参数必须是ProceedingJoinPoint类型。
public Object doBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
// start stopwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
Introduction
※ 简介允许一个切面声明一个接口切面,并且为该接口指定实现类
※由aop:aspect中的aop:declare-parents元素声明,该元素用于声明所匹配的类型拥有一个新的parent
Proxy
ProxyFactoryBean:
· 创建Spring AOP代理的基本方法是使用org.spring
framework.aop.framwork.ProxyFactoryBean类
※ 使用ProxyFactoryBean或者其他IoC相关类来创建AOP代码的最重要好处是通知和切入点也可以由IoC来管理
※ 被代理类没有实现任何接口,使用CGLIB代理,否则使用JDK代理。
※ 通过设置proxyTargetClass为true,可强制使用CGLIB
※ 如果目标类实现了一个或多个接口,name创建代理的类型将依赖ProxyFactoryBean的配置
※ 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或多个全限定接口名(包名+类名),基于JDK的代理将被创建。
※ 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个或多个接口,那么ProxyFactoryBean将检测到这个目标类已经实现了至少一个接口,创建一个基于JDK代理。
<bean id="person" class="org.springframework.aop.framwork.ProxyFactoryBean"> //此bean固定绑定该类
<property name="proxyInterfaces" value="Person"/> //指定接口
<property name="target" ref="personTarget"/> //具体Person实现类
<property name="interceptorNames"> //指明要在代理类中添加的功能,即advice
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
(<property name="proxyTargetClass" value="true/false") //指定代理的方式,如果为true则为非接口类,用CGLIB代理,
</bean>
使用:<bean id="personUser" class="PersonUser">
<property name="person" ref="person"/> //此处person将使用上面的代理bean,返回的是PersonTarget的bean
</bean>
//CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标(注意:final方法不能被通知,因为它们不能被覆盖)
AspectJ
Spring中配置@AspectJ
· 对@AspectJ支持可以使用XML或Java风格的配置
1)注解方式:@EnableAspectJAutoProxy
2)xml配置方式:<aop:aspectj-autoproxy/>
· @AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用
· @Aspect注解是不能够通过类路径自动检测发现的,所以需要配合使用@Component注释或者在xml配置bean
· 一个类中的@Aspect注解标识它为一个切面,并且将自己从自动代理中排除
pointcut
一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,方法返回类型必须为void
@Pointcut申明方法可查找相关资料
※ 切入点表达式可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式(通过组合建立更复杂的切入点表达式)
@Pointcut("execution(public * (..))")
private void anyPublicOperation(){}
@Pointcut("within(com.xyz.someapp.trading..)")
private void inTrading(){}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation(){} //结合了上面两个切入点的内容(组合在一起)
Advice定义
—Before Advice
@Component
@Aspect
public class MoocAspect{
@Before("exexution(* com.aspectj.*Biz.*(..))") //在执行com.aspectj包下以Biz结尾的类的所有方法时匹配before Advice
public void before(){
//..
}
}
—After returning Advice
当需要返回值的时候,可以用pointcut属性指定切点,returning属性指定返回值(在形参处定义返回值变量)
@AfterReturning(pointcut="...",returning="..")
—After throwing Advice
同样可以通过throwing属性提供返回值
—After(finally)advice
最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源
※ 当After和AfterReturning一起出现时,先执行After再执行AfterReturning
—Aroud advice
通知方法的返回值为Object, 第一个参数必须是ProceedingJoinPoint类型
@Around('...")
public Object doBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{
// start startwatch
Object retVal = pjp.proceed();
// stop stopwatch
return retVal;
}
Advice扩展应用
· 给advice传递参数(实参来源于切点的实参)
@Pointcut("com.package.A.a() && args(account,..)")
或
@Pointcut("com.package.A.a(account,..)")
public void pointcui(Account account){}
· Introductions
—introductions使用@DeclareParents进行注解,这个注解用来定义匹配的类型拥有一个新的parent
—允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类代表执行对象