Spring源码学习六——AOP主流程

一、SpringAOP介绍

AOP就是面向切面编程的简称,最早是由AspectJ首创,它里面有一些概念来帮助理解。在SpringAOP中,对应的也有几个名词(和AspectJ有细微差别)。

  1. 切面(Aspect):在SpringAOP中指的是切面类,它管理着切点、通知
  2. 连接点(Join Point):可以被增强的业务方法
  3. 切点(Pointcut):被增强的业务方法,结合切点表达式进行实现
  4. 通知(Advice):需要增加到业务方法中的公共代码,通知可以在业务方法不同位置进行执行(前置通知、后置通知、异常通知、返回通知、环绕通知)
  5. 目标对象(Target Object):被增强的方法所在的对象
  6. 织入(Weaving):SpringAOP在运行期织入,织入是一个将通知加入切点的动作。AOP容器会为目标对象动态创建一个代理对象。

我觉得上面这些都不重要,记一下应付面试就行了。Spring的AOP我理解是利用动态代理增强方法,不侵入原来方法,在方法的执行前后增加逻辑,并把这一功能做到灵活配置。可用于权限认证、日志、事务处理等。

二、JDK动态代理与Cglib动态代理

SpringAOP有两种实现方式,JDK动态代理和Cglib动态代理,下面看看简单的使用案例

2.1 JDK动态代理

JDKInvocationHandler

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JDKInvocationHandler implements InvocationHandler {
    final private Object target;//目标对象

    public JDKInvocationHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before method");//公共逻辑代码,通知
        Object result = method.invoke(target, args);
        System.out.println("after method");
        return result;
    }
}

TargetInterFace

public interface TargetInterFace {
    public void run();
}

TargetObject

public class TargetObject implements TargetInterFace {
    @Override
    public void run() {
        System.out.println("run quickly!");
    }
}

JDKProxtTest

public class JDKProxtTest {
    public static void main(String[] args) {
        TargetObject targetObject = new TargetObject();
        //newProxyInstance第二个参数需要传入接口,因此jdk动态代理只能代理接口中的方法
        TargetInterFace target = (TargetInterFace) Proxy.newProxyInstance(JDKProxtTest.class.getClassLoader(), new Class[]{TargetInterFace.class}
                ,new JDKInvocationHandler(targetObject));
        target.run();
    }
}

2.2 Cglib动态代理

CglibMethodInterceptor

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CglibMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before method");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("after method");
        return result;
    }
}

CglibProxyTest

import org.springframework.cglib.proxy.Enhancer;

public class CglibProxyTest {
    public static void main(String[] args) {
        TargetObject targetObject = (TargetObject) Enhancer.create(TargetObject.class, new CglibMethodInterceptor());
        System.out.println(targetObject.getClass().getName());
        targetObject.run();
    }
}

三、代码跟踪

3.1 环境准备

YexAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//切面
@Aspect
@Component
public class YexAspect {
	//通知,引号里面是切点
    @Before("execution(public void com.spring.study.service.UserService.run())")
    public void yexBefore(JoinPoint joinPoint){
        System.out.println("Before run");
    }
}

AppConfig

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@ComponentScan("com.spring.study")
@EnableAspectJAutoProxy //开启AOP,将AnnotationAwareAspectJAutoProxyCreator注册到容器,后面用它进行AOP
public class AppConfig {
}

IUserService

public interface IUserService {
    public void run();
}

UserService

import org.springframework.stereotype.Component;
@Component
public class UserService implements IUserService {
    @Override
    public void run() {
        System.out.println("run run run quickly!");
    }
}

TestSpring

import com.spring.study.service.IUserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestSpring {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        IUserService o = (IUserService) context.getBean("userService");
        System.out.println(o.getClass());//class com.sun.proxy.$Proxy20 代理对象
        o.run();
    }
}

3.2 源码实现

先找到实现AOP的地方,在前面时深入分析过Bean的生命周期。AOP就是在bean初始化的最后阶段,处理BeanPostProcessor的postProcessAfterInitialization时进行的

进入
doCreateBean–>initializeBean(beanName, exposedObject, mbd)–>applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)

在这里插入图片描述
到目前为止UserService还没被代理,AnnotationAwareAspectJAutoProxyCreator将会进行AOP。

3.2.1 AnnotationAwareAspectJAutoProxyCreator

在这里插入图片描述
翻译一下:

  • AspectJAwareAdvisorAutoProxyCreator的子类,用于处理当前应用程序上下文中的所有AspectJ注 解,以及Spring Advisors。
  • 任何AspectJ注解的类,如果可以被Spring AOP的代理模式代理,那么它们都将被自动识别,并且使用它们的通知。
  • 如果使用aop:include元素,则只有名称与include模式匹配的@AspectJ bean才会被视为定义用于Spring自动代理的切面。
  • Spring Advisors的处理遵循AbstractAdvisorAutoProxyCreator。

因此关于AOP的处理应该在AbstractAdvisorAutoProxyCreator里面

3.2.2 AbstractAdvisorAutoProxyCreator

在这里插入图片描述
看第一句就够了,通用自动代理创建者,基于检测到的每个bean的Advisors为特定bean构建AOP代理。就是说,通知和目标bean的匹配是在这个地方做的。AOP的入口postProcessAfterInitialization是在它的父类AbstractAutoProxyCreator里面实现的。

3.2.3 AbstractAutoProxyCreator

真正实现postProcessAfterInitialization方法的在这个类里面
在这里插入图片描述
进入wrapIfNecessary
在这里插入图片描述
这块能匹配到,说明需要被代理getAdvicesAndAdvisorsForBean是一个模板方法,真正的实现就在前面讲的AbstractAdvisorAutoProxyCreator里面。往下走,匹配到了我们自定义的advice
在这里插入图片描述
继续进入createProxy
在这里插入图片描述
这个方法先构建一个代理工厂,把目标对象和通知添加进去。再通过工厂得到代理对象。进入getProxy

getProxy–>createAopProxy()–>getAopProxyFactory().createAopProxy(this)

在这里插入图片描述
UserService实现了接口,所以采用JDK动态代理。返回一个JdkDynamicAopProxy,返回到

getProxy–>getProxy(classLoader)

在这里插入图片描述
进入到了JdkDynamicAopProxy的getProxy。最终找到了如JDK动态代理示例的代码:
Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
JdkDynamicAopProxy本身实现了InvocationHandler,由他自己来处理代理到目标方法之后的逻辑。
回到wrapIfNecessary
在这里插入图片描述
返回最后的代理对象,正确输出
在这里插入图片描述

四、SpringAOP与AspectJAOP区别

  • SpringAOP是基于代理的,有接口的时候,就是基于JDK动态代理,无接口的时候是基于Cglib动态代理。这两种代理都只能对方法进行代理
  • AspectJ可以对field、constructor的访问进行拦截
  • SpringAOP的采用运行期间去生成目标对象的代理对象来实现,导致其只能在运行期工作
  • AspectJ可以在编译期通过特殊的编译,就把切面逻辑,织入到class中,而且可以嵌入切面逻辑到任意地方,比如constructor、静态初始化块、field的set/get等
  • SpringAOP依赖AspectJ,但是只用到了AspectJ的切点解析和匹配。@Aspect、@Before等这些注解都是AspectJ提供的
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值