AOP In Spring With LTW

1. 概念

1.1 概念

  1. JoinPoint(连接点):需要增强的单个方法;
  2. Advice(通知点): 要执行的增强(方法)及增强执行的时机( before JoinPoint execute?after JoinPoint return?after JoinPoint throwing advice? at advice finally ?)
  3. Pointcut(切点):一条规则,用于指定需要增强的oinPoint
  4. Aspect(切面):符合PointCut的所有JoinPoint以及一个Advice
  5. TargetObject(目标实例):要被多个切面增强的某个实例;
  6. AOP proxy(AOP代理对象): 需要被增强的实例被一系切面修改过后生成的代理对象(Spring 默认使用JDK动态代理和CGlIB生成 相应的代理对象);
  7. Weaving(织入或者叫植入):对目标对象增强的这个行为叫织入(简单的说就是修改Class的字节码用于增强JoinPoint);织入增强代码的时机有三个:编译时,加载时,运行时(Spring是这个)

1.2 Advice类型

public class Proxy {
    Object target;
    public Object invoke(Advice advice,Method method,Object [] args){
         advice.invoke();//before advice,无法阻止线程向下执行到JoinPoint,除非before advice 抛出异常;
         try{
            Object result = method.invoke(target,args);//target就是目标对象,是this持有的一个引用指向
            advice.invoke();//after returning advicez,只要不要抛出异常就会执行
            return result;
         } catch(Exception e){
            advice.invoke();//after throwing advice
        }finally{
            advice.invoke(); //after (finally) advice ,一定会被执行JoinPoint的退出方式
        }
    }
}

还有一个Around advice 就是这个上面四个advice都执行的的集合体

2. Spring-AOP(Pure Proxy-based Mode)

Spring支持了AspectJ的部分切面语法,在底层默认使用JDK 动态代理和CGLIB 生成目标实例的代理类来完成AOP的操作;

最终代理Bean代码大概形式就是上面描述Advice的代码,这样的结构存在一个比较严重的问题:

target内部自我调用(this.doBusiness(..))被增强的方法会造成Advice失效,比较常见的就是Spring事务管理中事务失效的问题:
事务A方法内部调用自己的事务方法B,B抛出了异常但是B方法造成数据的更改并没有回滚,也就是B方法没有开启事务;
一个解决方法是:A方法内通过AopContext.getCurrentProxy.B(...)来造成B方法的调用;超级难受(:doge;
另一个解决办法是:在Spring Application中启用LoadTimeWeaver相关支持,利用AspectJ lib将运行时的目标类代码进行修改,使得对象的方法字节码就是想要的字节序列

除此之外,

在Spring使用JDK动态代理的时,只能接口方法进行增强;
在Spring使用CGLIB时,会生成目标类的子类,并在子类重写要代理的的方法(只能对非final classs的public protected 最多到default方法进行重写,子类的逻辑是持有一个目标类实例的reference 和MethodInterceptor bean methodInterceptor ,重写所有方法为methodInterceptor.invoke(target,method,args,....)),然后生成子类的实例注入到我们自己编写的类实例中;

2.1 启用@Aspect注解支持

Attention:@Aspect注解支持并不是Spring会在运行时或者其他任何时候修改源代码编译出的字节码(使用AspectJ.jar修改字节码),只是支持@AspectJ的部分切面语法

2.1.1 JavaBean 配置

AppConfig.java:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

classpath : 有aspectjweaver.jar;

2.1.2 xml 配置

<aop:aspectj-autoproxy/> + classpath 有aspectjweaver.jar;

2.2 定义一个切面

2.2.1 JavaBean

@Aspect
@Component//注意必须要加上这个注解,不然会scan不到
public class NotVeryUsefulAspect {

}

2.2.2 xml

<bean id="myAspect" class="NotVeryUsefulAspect"></bean>

2.3 声明一个切点PointCut

切点的声明分为两个部分: 切点签名(name+参数),一个Aspect表达式; 表达式Pattern,只写最常用的execution吧:

execution : execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?).除了 ret-type, name-pattern,param-pattern必填其他的都是可选项。

例子:

  1. execution(public * com.*.* (..) *?) :匹配com包所有的public的方法;
  2. execution(* com.service.*Service.Insert*(..) ) :匹配com.service包所有的以Service结尾类里面所有的以Insert开头的方法;
  3. execution(* com..*.*(..) :匹配com及其子包下所有的方法;

2.3.1 JavaBean

package com.aruforce.aoptest.aspects;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component//注意必须要加上这个注解,不然会scan不到
public class BaseAspect {
    @Pointcut("execution(public * com.aruforce.aoptest.logics.*.*(..))")// indicate by method name;
    public void anyPublicOperationsInLogicPackage(){} //方法的返回值类型必须是void类型;

}

2.3.2 xml

<aop:config>
   <aop:pointcut id="anyPublicOperationsInLogicPackage" expression="execution(public * com.aruforce.aoptest.logics.*.*(..))"></aop:pointcut>
</aop:config>

2.4 增强

2.4.1 声明

BeforAdvice:

package com.aruforce.aoptest.advices;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
@Component //need
public class BeforeExample {

    @Before("execution(public * com.aruforce.aoptest.logics.*.*(..))")
    public void doAccessCheck() {
        // ...
    }
    @Before("com.aruforce.aoptest.aspects.BaseAspect.anyPublicOperationsInLogicPackage()")
    public void doLog(){
        //.....
    }
    //上面两个增强处理的方法集是一样的
}

AfterReturnAdvice:

package com.aruforce.aoptest.advices;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class AfterReturnExample {
    @AfterReturning(pointcut="com.aruforce.aoptest.aspects.BaseAspect.anyPublicOperationsInLogicPackage()",returning="retVal)
    public void doAccessCheck(Object retVal) {// returning="xxx" 必须和 方法的参数名对应
        // ...
    }
}

Around Advice: 尽可能不要使用这个类型的Advice

package com.aruforce.aoptest.advices;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class AroundExample {

    @Around("com.aruforce.aoptest.aspects.BaseAspect.anyPublicOperationsInLogicPackage()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {//第一个形参的类型必须是ProceedingJoinPoint
        // start stopwatch
        Object retVal = pjp.proceed();//执行被增强的方法
        // stop stopwatch
        return retVal;
    }
}

2.4.2 增强的执行顺序(一个JoinPoint上有多个advice需要执行时)

  1. 在多个切面上时:

除非特别声明否则无法决定顺序,aspect 有个order属性,在调用前order值越小越先执行,再返回时order越小越后执行;

  1. 在一个切面的:

无法确定执行顺序,还是考虑怎么合并到一个方法吧

2.5 代理模式的工作机制

2.5.1 关于底层代理技术

  1. Spring优先使用JDK做代理(只要被代理类实现了至少一个接口),只有被代理类完全没有实现任何接口才会使用CGLIB
  2. 如果想全面使用CGLIB,在使用@Aspect形式配置的时候,请使用这个标签<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>,在使用XML配置的时候<aop:config proxy-target-class="true"> </aop:config>,3.2 以后的版本不再需要把CGLIB.jar 加到 classpath.

2.5.2 必须理解Spring AOP是基于代理的这句话的意思

请参看关于Spring-AOP 开头的那段文字,尤其是关于proxy-based aop 的缺陷那段,事务失效的原因分析;

//todo: 什么时候分析下AopNameSpaceHandler的代码,确认下源代码的工作逻辑;

2.6 Configure Sample

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd"
       default-autowire="byName">

    <context:annotation-config/>
    <context:component-scan base-package="com.aruforce.aoptest"/>
    <!--aspectj-autoproxy 开启@AspectJ 语法支持, -->
    <!--proxy-target-class 代表强制使用CGLIB生成代理类,否则Spring会尽可能使用JDK -->
    <!--expose-proxy 是否允许获取代理对象,如果否AopContext.getCurrentProxy()会抛出异常-->
    <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>

BaseAspect:

package com.aruforce.aoptest.aspects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class BaseAspect {
    private Logger logger = LoggerFactory.getLogger(BaseAspect.class);

    @Pointcut("execution(* com.aruforce.aoptest.logics.*.*(..))")
    public void allMethod(){}

    @Before("com.aruforce.aoptest.aspects.BaseAspect.allMethod()")
    public void doLog(){
        logger.info("log from methodBuf");
    }

    @Around("com.aruforce.aoptest.aspects.BaseAspect.allMethod()&&args(params)")
    public void doCache(ProceedingJoinPoint proceedingJoinPoint, Object [] params){
        try {
            Object proceed = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

    }
}

Job interface:

package com.aruforce.aoptest.logics;

public interface Job {
    public void  test5();
    public void  test6();
}

TransactiobJob:

package com.aruforce.aoptest.logics;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Component;

@Component
public class TransactionJob implements Job{
    private Logger logger = LoggerFactory.getLogger(TransactionJob.class);

    public void  test5(){
        logger.info("test5 for aop");
    }
    public void  test6(){
        logger.info("test6 for aop");
        //proxy-based make advice in effetive
        //注意这个proxy,当使用JDK代理接口Job时,不能转型为TransactionJob,当使用CGBLIB时,可以,原因呢?(就是JDK代理和CGLIB代理机制不一样);
       ((Job) AopContext.currentProxy()).test5();
        // proxy-based,will lose advice
       // test5();
    }
}

下面开始写真东西了;


3. Spring AOP(based-aspectJ mode,LTW)

3.1 一个Example

3.1.1 Jar包要求

Attention: spring-agent.jar不是必须的,只是为了在这里说明Command-line启动时的机制,完全可以不用-javaagent来做,Spring做了兼容处理,请参看下面的源码部分)

  1. spring-aspects.jar (这里面有个Aspectj的jar,repackaged)on calsspath;
  2. spring-agent.jar 利用JVM instrumention这个机制,对类加载器注入ClassFileTransformer在类的载入时进行字节码的修改

3.1.2 AspectJ的配置META-INF/aop.xml

Attetion:这是AspectJ的约定必须为classpath/META-INF/aop.xml

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver options="-verbose -debug -showWeaveInfo"><!--这几个参数应该可以见明知意吧..-->
        <!--设定要进行织入的类pattern-->
        <!--对了,这里有个小坑,不仅要加入要被增强的类还要加入切面这个类BaseApsect,不然会出NoSuchMethodError:***Aspect.aspectOf()-->
        <include within="com.aruforce.aoptest.logics.*"/>
        <include within="com.aruforce.aoptest.aspects.*"/>
    </weaver>

    <aspects>
        <!-- 切面 -->
        <aspect name="com.aruforce.aoptest.aspects.BaseAspect"/>
    </aspects>

</aspectj>

3.1.3 Spring的配置applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
             http://www.springframework.org/schema/beans/spring-beans.xsd
             http://www.springframework.org/schema/aop
             http://www.springframework.org/schema/aop/spring-aop.xsd
             http://www.springframework.org/schema/context
             http://www.springframework.org/schema/context/spring-context.xsd"
       default-autowire="byName">
    <context:annotation-config/>
    <context:component-scan base-package="com.aruforce.aoptest"/>
    <!--
    <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
    -->
    <!--开启标签,还可以定义weaver-class,-->
    <context:load-time-weaver aspectj-weaving="autodetect"/>
    <!--解决事务管理中自我调用造成事务失效的问题,注意mode 一定要改成 aspectj-->
    <tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/>
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

3.1.4 BaseAspect.java

package com.aruforce.aoptest.aspects;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch;

@Aspect
public class BaseAspect {
    private Logger logger = LoggerFactory.getLogger(BaseAspect.class);

    @Pointcut("execution(* com.aruforce.aoptest.logics.TransactionJob.*(..))")
    public void allMethod() {
    }

    @Around("allMethod()")
    public Object doProfile(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        logger.info("log from methodBuf");
        try {
            return proceedingJoinPoint.proceed();
        } finally {
        }
    }
}

3.1.5 事务方法

package com.aruforce.aoptest.logics;

public interface Job {
    public void  test5();
    public void  test6();
}
package com.aruforce.aoptest.logics;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class TransactionJob implements Job{
    private Logger logger = LoggerFactory.getLogger(TransactionJob.class);
    @Transactional
    public void  test5(){
        logger.info("test5 for aop");
    }
    @Transactional
    public void  test6(){
        logger.info("test6 for aop");
        test5();
    }
}

3.1.6 Main

public class Main{
   public void static main(String [] args){
      ApplicationContext app = new ClassPathXmlApplicationContext("spring-config.xml");
      Job job1 = (Job)app.getBean("transactionJob");
      job1.test6();
//      Job job2 = new TransactionJob();
//      job2.test6();
   }
}

3.1.7 Comman-line

Attention1:注意这一步不是必须的...只是为了说明机制,实际上只需要<context:load-time-weaver aspectj-weaving="autodetect"/> 及 META-INF/aop.xml即可.

Attention2:除了不是必须的外,实际上不建议这么做,-javaagent 实际上时JVM级别的增强,比如在一个tomcat有多个应用的这种情况下,这样启动,BaseAspect会对所有应用的符合切面描述的类进行增强,这绝对不是想要的结果

java -jar HelloWorldAop.Jar -javaagent:pathToSpringAgent.jar

3.1.8 结果(deubg level)

[2019-07-09 11:13:45.981] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.getTransaction:371] -Creating new transaction with name [com.aruforce.aoptest.logics.TransactionJob.test6]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
[2019-07-09 11:13:45.981] [DEBUG][org.springframework.jdbc.datasource.DriverManagerDataSource.getConnectionFromDriver:142] -Creating new JDBC DriverManager Connection to [jdbc:MySql://127.0.0.1:3306/quartz?characterEncoding=UTF-8]
[2019-07-09 11:13:45.990] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin:248] -Acquired Connection [com.mysql.jdbc.JDBC4Connection@14751b51] for JDBC transaction
[2019-07-09 11:13:45.990] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin:265] -Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@14751b51] to manual commit
[2019-07-09 11:13:45.990] [INFO ][com.aruforce.aoptest.aspects.BaseAspect.doProfile:21] -log from methodBuf
[2019-07-09 11:13:45.990] [INFO ][com.aruforce.aoptest.logics.TransactionJob.test6_aroundBody4:17] -test6 for aop
[2019-07-09 11:13:45.990] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.handleExistingTransaction:480] -Participating in existing transaction
[2019-07-09 11:13:45.990] [INFO ][com.aruforce.aoptest.aspects.BaseAspect.doProfile:21] -log from methodBuf
[2019-07-09 11:13:45.991] [INFO ][com.aruforce.aoptest.logics.TransactionJob.test5_aroundBody0:13] -test5 for aop
[2019-07-09 11:13:45.991] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.processCommit:763] -Initiating transaction commit
[2019-07-09 11:13:45.991] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit:310] -Committing JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@14751b51]
[2019-07-09 11:13:45.991] [DEBUG][org.springframework.jdbc.datasource.DataSourceTransactionManager.doCleanupAfterCompletion:368] -Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@14751b51] after transaction
[2019-07-09 11:13:45.991] [DEBUG][org.springframework.jdbc.datasource.DataSourceUtils.doReleaseConnection:329] -Returning JDBC Connection to DataSource

补充:

  1. 如果上面的日志开到debug level,你会发现test6()方式再调用test5()时,test5的事务会并入到test6(),而不是像是proxy-base mode 那样test5()就不是个事务

  2. 除了Spring自动注入Bean之外,直接Job job = new TransactionJob();job,test6() 也是会开启一套事务的,代理模式就不行了;

3.2 原理

3.2.1 Spring-config 配置这个Bean:

DefaultContextLoadTimeWeaver:

@Override
public void setBeanClassLoader(ClassLoader classLoader) {
    LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);//根据启动环境确定LTW,一般默认就是tomcat了..
    if (serverSpecificLoadTimeWeaver != null) {
        this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
    }else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {// 是用-javaagent:spring-agent.jar 启动的?:org.springframework.instrument.InstrumentationSavingAgent.instrumentation不是NULL
        this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
    }else {
        try {
            this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);//如果没使用tomcat 也没使用-javaagent的话;
        }catch (IllegalStateException ex) {
            throw new IllegalStateException(ex.getMessage() + " Specify a custom LoadTimeWeaver or start your Java virtual machine with Spring's agent: -javaagent:org.springframework.instrument.jar");
        }
    }
}
protected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {
    String name = classLoader.getClass().getName();
    try {
        if (name.startsWith("org.apache.catalina")) {
            return new TomcatLoadTimeWeaver(classLoader);
        }else if (name.startsWith("org.glassfish")) {
            return new GlassFishLoadTimeWeaver(classLoader);
        }else if (name.startsWith("org.jboss")) {
            return new JBossLoadTimeWeaver(classLoader);
        }else if (name.startsWith("com.ibm")) {
            return new WebSphereLoadTimeWeaver(classLoader)
        }else if (name.startsWith("weblogic")) {
             return new WebLogicLoadTimeWeaver(classLoader);
        }
    }catch (Exception ex) {}
    return null;
}
@Override
public void addTransformer(ClassFileTransformer transformer) {
    this.loadTimeWeaver.addTransformer(transformer);
}

loadTimeWeaver:用tomcat的做个例子吧

public void addTransformer(ClassFileTransformer transformer) {
    try {
        this.addTransformerMethod.invoke(this.classLoader, transformer);//在类加载器上执行addClassFileTranFormer实例, 这个Transformer 实际上就是
    }catch (InvocationTargetException ex) {
        throw new IllegalStateException("Tomcat addTransformer method threw exception", ex.getCause());
    }catch (Throwable ex) {
        throw new IllegalStateException("Could not invoke Tomcat addTransformer method", ex);
    }
}

3.2.2 怎么调用addClassFiletTransFormer:

ContextNameSpacehandler:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
   @Override
   public void init() {
      ......
      registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
      .....
   }
}

LoadTimeWeaverBeanDefinitionParser: 注入AspectJWeavingEnabler 定义为beanFactoryPostProcessor

class LoadTimeWeaverBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    public static final String ASPECTJ_WEAVING_ENABLER_BEAN_NAME = "org.springframework.context.config.internalAspectJWeavingEnabler";
    private static final String ASPECTJ_WEAVING_ENABLER_CLASS_NAME = "org.springframework.context.weaving.AspectJWeavingEnabler";
    private static final String DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME = "org.springframework.context.weaving.DefaultContextLoadTimeWeaver";
    private static final String WEAVER_CLASS_ATTRIBUTE = "weaver-class";
    private static final String ASPECTJ_WEAVING_ATTRIBUTE = "aspectj-weaving";
    @Override
    protected String getBeanClassName(Element element) {
        if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
            return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
        }
        return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
    }

    @Override
    protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
        return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;// 生成的BeanDefinition的Id ="loadTimeWeaver"
    }
    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

        if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
            if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {
                RootBeanDefinition def = new RootBeanDefinition(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
                parserContext.registerBeanComponent(new BeanComponentDefinition(def, ASPECTJ_WEAVING_ENABLER_BEAN_NAME));
            }
            if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
                new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
            }
        }
    }

    protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
        if ("on".equals(value)) {
            return true;
        }
        else if ("off".equals(value)) {
            return false;
        }
        else {
            // check if META-INF/aop.xml if exists or not
            ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader();
            return (cl.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) != null);
        }
    }

    protected boolean isBeanConfigurerAspectEnabled(ClassLoader beanClassLoader) {
        return ClassUtils.isPresent(SpringConfiguredBeanDefinitionParser.BEAN_CONFIGURER_ASPECT_CLASS_NAME,beanClassLoader);
    }

}

AspectJWeavingEnabler:

public class AspectJWeavingEnabler implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {
    public static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml";
    private ClassLoader beanClassLoader;
    private LoadTimeWeaver loadTimeWeaver;
    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }
    @Override
    public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
        this.loadTimeWeaver = loadTimeWeaver;
    }
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
    //AbstractApplicationContext.refresh()--->postProcessBeanFactory(beanFactory);
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
    }
    //这里启用增强
    public static void enableAspectJWeaving(LoadTimeWeaver weaverToUse, ClassLoader beanClassLoader) {
        if (weaverToUse == null) {
            if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
                weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
            }else {
                throw new IllegalStateException("No LoadTimeWeaver available");
            }
        }
        weaverToUse.addTransformer(new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));//AspectJ 的代码增强
    }
    private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {
        private final ClassFileTransformer delegate;
        public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {
            this.delegate = delegate;
        }
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) {
                return classfileBuffer;
            }
            return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
        }
    }
}

转载于:https://my.oschina.net/Aruforce/blog/3069666

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值