引子
上一篇 spring源码解析-启动流程与循环依赖 分析了spring ioc部分的源码,这一篇则分析aop源码,但aop需要先准备面向切面编程的相关知识,否则学起来会很吃力,所以aop第一篇是先看一下spring aop用到的面向切面的相关知识,为下篇读源码做准备。
aspectJ与spring aop区别
spring核心包里引入aspectJ,而aspectJ是一个完整的aop框架,那是不是spring直接使用aspectJ来实现aop相关功能呢?
我们先简单了解一下aspectJ,它属于编译时增强, 有专门的编译器来生成符合java字节码规范的class文件,在编译期就生成增强类,所以运行时性能损耗小,aspectJ是一个独立的完整的aop框架,也就是说它确实可以帮spring来实现所有的aop相关功能。
但实际上spring并没有完全依赖aspectJ, spring aop属于运行时增强,有接口时用jdk动态代理,其它的使用cglib动态生成代理类,它主要针对bean来使用.aspectJ与spring aop工作在两个时期,并不属于同一类,spring也没用aspectJ来生成代理类,那它们有什么关系呢? spring为什么要引入aspectJ包呢?
aspectJ与spring aop联系
springj是有引入aspectJ包,但没有使用它的编译器相关功能,只使用注解功能,通过这样的方式去掉了对xml文件的依赖,但当切面比较多时也可以使用aspectJ。
spring aop的实现方式
spring aop支持四种切面通知方式: 前置,后置,环绕,异常
前置通知:org.springframework.aop.MethodBeforeAdvice
后置通知:org.springframework.aop.AfterReturningAdvice
环绕通知:org.aopalliance.intercept.MethodInterceptor
异常通知:org.springframework.aop.ThrowsAdvice
spring aop支持四种使用方法:
1.经典aop配置方式
2.纯POJO切面
3.@AspectJ注解
4. 使用aspectJ
先定义一个被代理的类User.java
public class User {
public Object get() {
System.out.println("get.............");
return "sssss";
}
}
- 经典aop配置方式
这种方式的通知类型需要在Advice(UserAdvice)中指定,如:
//实现环绕增强
public class UserAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before interceptor");
Object o = invocation.proceed();
System.out.println("after interceptor");
return o;
}
}
然后在xml中定义两个bean,一个
RegexpMethodPointcutAdvisor类型,一个是BeanNameAutoProxyCreator类型
第一个是advisor,定义增强类方法与被代理类方法关系,解决怎么代理问题
第二个是定义目标类,解决代理谁的问题,也可以用ProxyFactoryBean为每个接口指定代理。
<bean id="user" class="com.xxx.User"></bean>
<bean id="userAdvice" class="scom.xxx.UserAdvice"></bean>
<bean id="userAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!--patterns,如果有多个指定的值的话,可以使用,隔开,例如value=".*add,.*delete"-->
<property name="patterns" value=".*get"/>
<property name="advice" ref="userAdvice"/>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="*user*"></property>
<property name="interceptorNames" value="userAdvisor"></property>
</bean>
2.pojo配置方式
通过aop:config来配置所有代理方法与被代理方法之间关系
public class UserAdvice {
public void before() {
System.out.println("before.......");
}
public void after(Object o) {
System.out.println("after.........." + o);
}
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("around before");
Object o = proceedingJoinPoint.proceed();
System.out.println("around after");
return o;
}
public void afterThrowing(Throwable t) {
System.out.println("after throwing....");
}
}
<bean id="user" class="com.xxx.User"></bean>
<bean id="userAdvice" class="scom.xxx.UserAdvice"></bean>
<aop:config>
<aop:pointcut id="p" expression="execution (* *.get(..))"/>
<aop:aspect ref="userAdvice">
<aop:before method="before" pointcut-ref="p"></aop:before>
<!--通过设置returning来将返回值传递给通知-->
<aop:after-returning method="after" pointcut-ref="p" returning="o"/>
<aop:around method="around" pointcut-ref="p"/>
<!--通过设置returning来将异常对象传递给通知-->
<aop:after-throwing method="afterThrowing" pointcut-ref="p" throwing="t"/>
</aop:aspect>
</aop:config>
3.@ASpectJ注解驱动的切面
@Aspect
@Component
public class UserIntercept {
@AfterReturning(returning = "o", pointcut = "execution(public int com.xxx.User.get())")
public void after(Object o) {
System.out.println("i get map:" + o);
}
}
4.直接使用aspectJ
这里就不展述了,下一篇根据示例分析aop源码。
关注个人微信公众号:肌肉码农,回复“好友”讨论问题。