AOP 专题(注解版本)
代码库:
https://gitee.com/chenscript/spring_ioc_aop_mvc_learning.git
1、AOP是什么?
AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统
OOP(Object-Oriented Programming, 面向对象编程) 的补充. AOP 的主要编程对象是切面(aspect),
而切面模块化横切关注点.
2、AOP解决什么问题?
- 所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,
便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。- 每个事物逻辑位于一个位置,代码不分散,便于维护和升级
业务模块更简洁了,只包含核心的业务代码
3、AOP如何实现?
- main()
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigAOP.class);
MathCalculator mathCalculator = (MathCalculator) context.getBean("mathCalculator");
int div = mathCalculator.div(1, 2);
}
}
- 配置类
@EnableAspectJAutoProxy
@Configuration
public class MainConfigAOP {
@Bean
public MathCalculator mathCalculator(){
return new MathCalculator();
}
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
- 切点类
public class MathCalculator {
public int div(int i,int j){
System.out.println("div...");
return i/j;
}
}
- 切面 @Pointcut()定义切点位置,后面是五个aop的切面注解,
@Before: 方法运行前执行
@After: 方法运行后执行
@AfterReturning: 方法返回结果后执行
@AfterThrowing: 异常时执行
@Around: 围绕方法,可以在方法前后添加操作
@Aspect
public class LogAspects {
@Pointcut("execution(public int com.spring.springaop.test01.MathCalculator.*(..))")
public void pointCut(){};
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"除法运行...参数是{}");
}
@After("pointCut(){}")
public void logEnd(){
System.out.println("除法结束");
}
@AfterReturning(value = "pointCut()",returning = "result")
public void logReturn(Object result){
System.out.println("除法正常返回。。。运行结果:{}"+result);
}
@AfterThrowing(value = "pointCut()",throwing = "ex")
public void logException(JoinPoint joinPoint,Exception ex){
System.out.println(joinPoint.getSignature().getName()+"异常:{}"+ex.getMessage());
}
}
运行结果:
div除法运行…参数是{}
div…
除法结束
除法正常返回。。。运行结果:{}0
4、AOP原理是什么? (动态代理+反射)
从main()函数开始,我们跟踪代码进去看看。
当打断点看的时候发现,容器会增加一个自动代理对象的初始化过程。
相比ioc流程中的常规bean的初始化,可以推理得出org.springframework.aop.config.internalAutoProxyCreator代理对象应该就是这个类在作怪了。怎么验证呢?
其实我们可以通过注解@EnableAspectJAutoProxy知道。
1.探索@EnableAspectJAutoProxy
点开注解,可以看到段代码:
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
引入了AspectJAutoProxyRegistrar类,进行代理解析。 在这里,又能假设了之前说的internalAutoProxyCreator类可能就是这个代理注册类或者是由它生成的。
继续看代码~
点进AspectJAutoProxyRegistrar类中,
操作一:注册切面注解创建器
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
操作二: 暴露注册器出来
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
看来主要操作还是在操作一。再点进去看看
registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
从字面上看,这儿是要创建一个注解代理器,然后注册到容器。所以需要观察AnnotationAwareAspectJAutoProxyCreator.class的主要逻辑
点进这个类,好像没什么思路,对吧。没错,这时候就该想想,是不是被容器触发器搞上了? 怎么才能在容器启动的时候被搞上呢,无非就是ioc里面的xxxAware,xxxBeanPostProcessor。所以,要被搞上,就要去继承这两种类型的类呗。好的,去看看继承关系。
用IDEA,ctrl+H 可以看到,继承了AbstractAutoProxyCreator,实现XXBeanPostProcessor和BeanFactoryAware接口。 也就是说,会在ioc中的bean后置处理器执行阶段搞点事,以及会做一些关于beanFactory的事。
仔细看了一下AnnotationAwareAspectJAutoProxyCreator类的方法,确实有一个关于工厂的操作,而且除了初始化默认的工厂之外,还多构建了工厂和适配器:ReflectiveAspectJAdvisorFactory和BeanFactoryAspectJAdvisorsBuilderAdapter。 这么特殊的做处理,那肯定是跟它们有关了。
ReflectiveAspectJAdvisorFactory:for bean pointcut handling
BeanFactoryAspectJAdvisorsBuilderAdapter:就是个托。BeanFactoryAspectJAdvisorsBuilderAdapter的子类,它委托给周围的AnnotationAwareAspectJAutoProxyCreator设施
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
if (this.aspectJAdvisorFactory == null) {
this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
}
this.aspectJAdvisorsBuilder =
new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
}
这一步最终目的还只是给容器配上一个AspectJAdvisorFactory单例而已,也就是this.aspectJAdvisorFactory = new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory);生成的工厂。
到这里初始化部分就结束了,开始aop调用。
2.探索aop动态代理调用过程。
将断点打在这,然后进入方法调试(F7)
就进入到了拦截方法,
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
主要步骤:
- List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
- retVal = processReturnType(proxy, target, method, retVal);
看到上面的主要步骤,一下子蒙蔽了,为什么马上就是Cglib代理了? 不是还有个jdk动态代理吗? 判断在哪?
于是我找到了答案:
先看调用链深度:
然后是代码:
判断创建的是哪个aop代理:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
也就是说,在初始化的时候就已经定是哪个动态代理类型了。这个例子中,我没有设置,就默认给我取了cglib动态代理对象进行调用。
好的,现在转回到intercept的主要流程:
- List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
- retVal = processReturnType(proxy, target, method, retVal);
解析获取调用链
List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
1.获取各种advisors
Advisor[] advisors = config.getAdvisors();
2.遍历这些通知,如果是PointcutAdvisor,则会给他匹配对应的class,再注册Interceptors;如果是其他advisors,则直接添加方法拦截(。。。调试不到这。。)
3.如果调用链为空,则直接进行方法代理调用,也就是不做添加调用链操作的代理操作。
4.否则就创建一个新的CglibMethodInvocation()并且调用proceed();
这个方法可厉害了,链式调用!!!
解释都在代码里,只能帮到这里了。
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
//当遍历的index等于拦截的通知数时,就会调用被代理对象的方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
//每次调用都会增加一个index
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
//AspectJAroundAdvice
//AspectJAfterThrowingAdvice
//AspectJAfterReturningAdvice
//AspectJAfterAdvice
//AspectJMethodBeforeAdvice
//以上这五个advice的调用。可以一个个调试看看
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
//如果调用匹配失败,则进入下一个调用
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
5.处理代理后的返回对象类型:retVal = processReturnType(proxy, target, method, retVal);
这好像没什么说的。
6.调用完之后就回到正常的业务代码上了。
AOP 就这么结束了。