SpringAOP+源码解析,切就完事了

本文是对近期学习知识的一个总结,附带源码注释及流程图,若有不足之处,还望评论区批评指正。html

此处感谢javadoop的源码解析,收益匪浅:https://javadoop.com/post/spring-aop-introjava

1、AOP、SpringAOP、AspectJ的区别

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,经过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。利用AOP能够对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度下降,提升程序的可重用性,同时提升了开发的效率。web

文绉绉的,没用过确实很懵,可是用过以后,不说清晰,起码有内意思了。spring

关于SpringAOP和AspectJ,参考Javadoop老师的解释:https://javadoop.com/post/spring-aop-intro编程

Spring AOP:数组

  • 它基于动态代理来实现。默认地,若是使用接口的,用 JDK 提供的动态代理实现,若是没有接口,使用 CGLIB 实现。你们必定要明白背后的意思,包括何时会不用 JDK 提供的动态代理,而用 CGLIB 实现。
  • Spring 3.2 之后,spring-core 直接就把 CGLIB 和 ASM 的源码包括进来了,这也是为何咱们不须要显式引入这两个依赖
  • Spring 的 IOC 容器和 AOP 都很重要,Spring AOP 须要依赖于 IOC 容器来管理。
  • 若是你是 web 开发者,有些时候,你可能须要的是一个 Filter 或一个 Interceptor,而不必定是 AOP。
  • Spring AOP 只能做用于 Spring 容器中的 Bean,它是使用纯粹的 Java 代码实现的,只能做用于 bean 的方法。
  • Spring 提供了 AspectJ 的支持,后面咱们会单独介绍怎么使用,通常来讲咱们用纯的 Spring AOP 就够了。
  • 不少人会对比 Spring AOP 和 AspectJ 的性能,Spring AOP 是基于代理实现的,在容器启动的时候须要生成代理实例,在方法调用上也会增长栈的深度,使得 Spring AOP 的性能不如 AspectJ 那么好。

AspectJ:安全

  • AspectJ 出身也是名门,来自于 Eclipse 基金会,link:https://www.eclipse.org/aspectjapp

  • 属于静态织入,它是经过修改代码来实现的,它的织入时机能够是:eclipse

    • Compile-time weaving:编译期织入,如类 A 使用 AspectJ 添加了一个属性,类 B 引用了它,这个场景就须要编译期的时候就进行织入,不然无法编译类 B。
    • Post-compile weaving:也就是已经生成了 .class 文件,或已经打成 jar 包了,这种状况咱们须要加强处理的话,就要用到编译后织入。
    • Load-time weaving:指的是在加载类的时候进行织入,要实现这个时期的织入,有几种常见的方法。一、自定义类加载器来干这个,这个应该是最容易想到的办法,在被织入类加载到 JVM 前去对它进行加载,这样就能够在加载的时候定义行为了。二、在 JVM 启动的时候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar
  • AspectJ 能干不少 Spring AOP 干不了的事情,它是 AOP 编程的彻底解决方案。Spring AOP 致力于解决的是企业级开发中最广泛的 AOP 需求(方法织入),而不是力求成为一个像 AspectJ 同样的 AOP 编程彻底解决方案。ide

  • 由于 AspectJ 在实际代码运行前完成了织入,因此你们会说它生成的类是没有额外运行时开销的。

2、AOP关键术语

  • 切面(Aspect):也就是咱们定义的专一于提供辅助功能的模块,好比安全管理,日志信息等。

  • 链接点(JoinPoint):切面代码能够经过链接点切入到正常业务之中,图中每一个方法的每一个点都是链接点。

  • 切入点(PointCut):一个切面不须要通知全部的链接点,而在链接点的基础之上增长切入的规则,选择须要加强的点,最终真正通知的点就是切入点。

  • 通知方法(Advice):就是切面须要执行的工做,主要有五种通知:before,after,afterReturning,afterThrowing,around。

  • 织入(Weaving):将切面应用到目标对象并建立代理对象的过程,SpringAOP选择再目标对象的运行期动态建立代理对

  • 引入(introduction):在不修改代码的前提下,引入能够在运行期为类动态地添加方法或字段。

3、通知的五种类型

  • 前置通知Before:目标方法调用以前执行的通知。
  • 后置通知After:目标方法完成以后,不管如何都会执行的通知。
  • 返回通知AfterReturning:目标方法成功以后调用的通知。
  • 异常通知AfterThrowing:目标方法抛出异常以后调用的通知。
  • 环绕通知Around:能够看做前面四种通知的综合。

4、切入点表达式

上面提到:链接点增长切入规则就至关于定义了切入点,固然切入点表达式分为两种:within和execution,这里主要学习execution表达式。

  • 写法:execution(访问修饰符 返回值 包名.包名……类名.方法名(参数列表))
  • 例:execution(public void com.smday.service.impl.AccountServiceImpl.saveAccount())
  • 访问修饰符能够省略,返回值可使用通配符*匹配。
  • 包名也可使用*匹配,数量表明包的层级,当前包可使用..标识,例如* *..AccountServiceImpl.saveAccount()
  • 类名和方法名也均可以使用*匹配:* *..*.*()
  • 参数列表使用..能够标识有无参数都可,且参数可为任意类型。

全通配写法:* *…*.*(…)

一般状况下,切入点应当设置再业务层实现类下的全部方法:* com.smday.service.impl.*.*(..)

5、AOP应用场景

  • 记录日志
  • 监控性能
  • 权限控制
  • 事务管理

6、AOP源码分析

SpringBean的生命周期

写了好多篇文章,每次都要来回顾一下SpringBean的生命周期,可见它真的十分重要。

Spring的Aop又是在哪完成的对目标对象的代理呢?咱们大概也可以想到,其实就是在执行回调的时候。按照惯例,先复习一下,从getBean开始到返回Bean经历了什么:

回顾完SpringBean的建立流程以后,咱们以注解方式@EnableAspectJAutoProxy配置Aop开启@Aspectj为例,进行一波AOP的流程总结:

AOP的流程总结

经过源码能够发现,实际上是经过@EnableAspectJAutoProxy注解注入了一个AnnotationAwareAspectJAutoProxyCreator,但这个类中其实并无重写postProcessAfterInitialization(),最终实现实际上是在AbstractAutoProxyCreator中。

具体干的事情,我已经经过一张图总结出来了,若是想要了解更加具体的信息,不妨打开源码,能够看的更加清晰一些。

AnnotationAwareAspectJAutoProxyCreator的注册

首先是对AnnotationAwareAspectJAutoProxyCreator的注册环节:【在此不做赘述】

class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {

    @Override
    @Nullable
     // 1. 注册proxy creator
    public BeanDefinition parse(Element element, ParserContext parserContext) { 
   AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
        extendBeanDefinition(element, parserContext);
        return null;
    }

}

applyBeanPostProcessorsAfterInitialization入口

AbstractAutowireCapableBeanFactory.java

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    //若是bean实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口, 回调
    invokeAwareMethods(beanName, bean);


    Object wrappedBean = bean;
    //aop在init-method以前并无进行操做, 目前仍是原来那个对象
    if (mbd == null || !mbd.isSynthetic()) {
        //BeanPostProcessor 的 postProcessBeforeInitialization 回调
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
     //处理bean中定义的init-method或 bean实现了InitializingBean ,调用afterPropertiesSet() 方法
    invokeInitMethods(beanName, wrappedBean, mbd);

    if (mbd == null || !mbd.isSynthetic()) {
        //BeanPostProcessor 的 postProcessAfterInitialization 回调 注意这里!
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        //AnnotationAwareAspectJAutoProxyCreator
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;//返回[可能代理后的]结果
}

AbstractAutoProxyCreator的主要方法

//SpringAop在IOC容器建立bean实例的最后对bean进行处理,进行代理加强, AbstractAutoProxyCreato	
@Override 
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);//这个方法将返回代理类
        }
    }
    return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

    //返回匹配当前bean 的全部的advisor, advice, interceptor
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        //在这里建立代理
        Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

createProxy过程

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

	//建立ProxyFactory实例
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);
	//在schema-based配置方式里,能够将 proxy-target-class="true",这样无论有没有接口都使用cglib
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
	//返回当前bean的advisors数组
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors); //设置advisors数组
    proxyFactory.setTargetSource(targetSource);//targetSource 携带了真实实现的信息
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }

    return proxyFactory.getProxy(getProxyClassLoader());//getProxy(getProxyClassLoader())这一步建立代理
}

JDK动态代理和CGLIB动态代理什么时候使用

这一步产生分歧的地方在ProxyFactory的getProxy方法,在getProxy以前,首先须要执行createAopProxy,而createAopProxy方法又被这个AopProxyFactory调用:

protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        activate();
    }
    //建立AopProxy以前,须要一个AopProxyFactory
    return getAopProxyFactory().createAopProxy(this);
}

// ProxyCreatorSupport
//这个aopProxyFactory用于建立aopProxy, 以后能够用aopProxy.getProxy(classLoader)建立代理
public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
}

也就是最后会走到DefaultAopProxyFactory中

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    
    if (!IN_NATIVE_IMAGE && 
        (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException();
        }
        //若是要代理的类自己就是接口,使用JDK动态代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        //jdk动态代理基于接口,只有接口中的方法才会被加强, cglib基于类继承,若是方法使用了final或者private修饰,也不能加强
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // 若是有接口,会跑到这个分支
        return new JdkDynamicAopProxy(config);
    }
}

总结:

  • 若是被代理的目标实现了一个或多个自定义的接口,那么就会使用JDK动态代理。

  • 若是没有实现任何接口,则使用CGLIB实现代理。

  • 若是设置proxy-target-class=true <property name="proxyTargetClass" value="true"/>则无论有没有实现接口都会使用CGLIB。

7、JDK动态代理的实现

最终的最终,都会走到真正建立代理对象的流程上:

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
	//获取代理接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    //查找代理目标的接口是否认义equals和hashcode方法
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    //使用jdk动态代理建立代理对象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

第一个参数:classLoader。

第二个参数:实现的接口。

第三个参数:InvocationHandler实例。

而自己JdkDynamicAopProxy本就实现了InvocationHandler,所以传入this。至此,当调用被代理类的方法的时候,都会最终调用代理类实现的invoke方法,在这个方法中定义横切的逻辑。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
  • proxy:代理对象的引用。
  • method:当前执行的方法。
  • args:当前执行方法所需的参数。
  • return:和被代理对象有相同的返回值。
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 	//当生成的代理类对外提供服务的时候,都会导入到这个invoke方法中
    Object oldProxy = null;
    boolean setProxyContext = false;
	
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 对equals方法的代理
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            //对hashCode()方法的代理
            return hashCode();
        }
        //...

        Object retVal;
		//若是设置了exposeProxy,将proxy放入ThreadLocal中
        if (this.advised.exposeProxy) { 
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 获取目标方法的拦截链,包含全部要执行的 advice
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // 检查一下这个链上是否是有advice,若是没有的话,能够跳过建立MethodInvocation
        if (chain.isEmpty()) { //chain若是是空的,表示不须要被加强,直接调用目标方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 若是chain里有advice 执行方法,获得返回值
            MethodInvocation invocation =
                new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            //沿着拦截器链,执行通知
            retVal = invocation.proceed();
        }

        // 对返回值的处理
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException();
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // 释放目标对象
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // 存储代理对象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

8、总结

以@AspectJ注解方式为例

  • 首先,依据<aop:aspectj-autoproxy>@EnableAspectJAutoProxy,Spring会在容器启动的时候注册名叫internalAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator
  • 在bean实例化完成,属性装配完成以后,开始执行回调方法,这时取出全部的BeanPostProcessor,执行其postProcessAfterInitialization方法,准备开始对目标对象代理的建立。
  • 首先建立一个代理工厂ProxyFactory,设置一系列的属性,如全部的通知方法,加强器,拦截器和目标类等注入代理工厂,再调用ProxyFactory.getProxy(classLoader)获取代理。
  • 经过判断是用JDK动态代理仍是CGLIB建立不一样的AopProxy,最后获取getProxy。

参考资料

相关文章
相关标签/搜索