1. 概念
1.1 概念
- JoinPoint(连接点):需要增强的单个方法;
- Advice(通知点): 要执行的增强(方法)及增强执行的时机( before JoinPoint execute?after JoinPoint return?after JoinPoint throwing advice? at advice finally ?)
- Pointcut(切点):一条规则,用于指定需要增强的oinPoint
- Aspect(切面):符合PointCut的所有JoinPoint以及一个Advice
- TargetObject(目标实例):要被多个切面增强的某个实例;
- AOP proxy(AOP代理对象): 需要被增强的实例被一系切面修改过后生成的代理对象(Spring 默认使用JDK动态代理和CGlIB生成 相应的代理对象);
- 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
必填其他的都是可选项。
例子:
execution(public * com.*.* (..) *?)
:匹配com包所有的public的方法;execution(* com.service.*Service.Insert*(..) )
:匹配com.service包所有的以Service结尾类里面所有的以Insert开头的方法;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需要执行时)
- 在多个切面上时:
除非特别声明否则无法决定顺序,aspect 有个order属性,在调用前order值越小越先执行,再返回时order越小越后执行;
- 在一个切面的:
无法确定执行顺序,还是考虑怎么合并到一个方法吧
2.5 代理模式的工作机制
2.5.1 关于底层代理技术
- Spring优先使用JDK做代理(只要被代理类实现了至少一个接口),只有被代理类完全没有实现任何接口才会使用CGLIB
- 如果想全面使用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做了兼容处理,请参看下面的源码部分)
- spring-aspects.jar (这里面有个Aspectj的jar,repackaged)on calsspath;
- 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
补充:
-
如果上面的日志开到debug level,你会发现test6()方式再调用test5()时,test5的事务会并入到test6(),而不是像是proxy-base mode 那样test5()就不是个事务
-
除了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);
}
}
}