AOP In Spring With LTW
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 stopwatchObject retVal = pjp.proceed();//执行被增强的方法// stop stopwatchreturn 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/contexthttp://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);@Transactionalpublic void test5(){logger.info("test5 for aop");}@Transactionalpublic 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不是NULLthis.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 {@Overridepublic 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";@Overrideprotected String getBeanClassName(Element element) {if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);}return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;}@Overrideprotected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;// 生成的BeanDefinition的Id ="loadTimeWeaver"}@Overrideprotected 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 notClassLoader 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;@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.beanClassLoader = classLoader;}@Overridepublic void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {this.loadTimeWeaver = loadTimeWeaver;}@Overridepublic int getOrder() {return HIGHEST_PRECEDENCE;}//AbstractApplicationContext.refresh()--->postProcessBeanFactory(beanFactory);@Overridepublic 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;}@Overridepublic 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
AOP In Spring With LTW相关推荐
- AOP和Spring AOP介绍
AOP和Spring AOP介绍 文章目录 AOP和Spring AOP介绍 一.AOP简介 二. 传统开发存在的弊端 三. AOP实现原理 四.Spring AOP 五.AOP相关术语 一.AOP简 ...
- 从AOP到Spring AOP
文章目录 一.前言 二.AOP基础知识 2.1 AOP 2.1.1 引子:AOP,AOP是什么? 2.1.2 使用对比的方式理解AOP--AOP与OOP 2.1.3 使用对比的方式理解AOP--静态A ...
- 【spring 5】AOP:spring中对于AOP的的实现
在前两篇博客中,介绍了AOP实现的基础:静态代理和动态代理,这篇博客介绍spring中AOP的实现. 一.采用Annotation方式 首先引入jar包:aspectjrt.jar && ...
- aopaspect区别_面试官:什么是AOP?Spring AOP和AspectJ的区别是什么?
AOP(Aspect Orient Programming),它是面向对象编程的一种补充,主要应用于处理一些具有横切性质的系统级服务,如日志收集.事务管理.安全检查.缓存.对象池管理等. AOP实现的 ...
- Spring(四)——AOP、Spring实现AOP、Spring整合Mybatis、Spring中的事务管理
文章目录 1. 什么是AOP 2. 使用Spring实现AOP 2.1 使用Spring的API 接口实现 2.2 自定义实现 2.3 使用注解实现 3. 整合MyBatis 3.1 MyBatis- ...
- 使用ProxyFactoryBean创建AOP代理 - Spring Framework reference 2.0.5 参考手册中文版
http://doc.javanb.com/spring-framework-reference-zh-2-0-5/ch07s05.html 7.5. 使用ProxyFactoryBean创建AOP代 ...
- android aop静态方法,spring aop 不能对静态方法进行增强解决
想要通过aop的方式记录HttpUtils发出的post请求日志,但是 aop 不能对静态方法进行增强.只能对实例方法进行增强.. 如果一定要增强静态方法,我们可以对目标类使用单例模式,然后通过调用实 ...
- 2.SSM之Spring整合、AOP及Spring事务
1.Spring整合 1.1 Mybatis 步骤一:数据库表准备 Mybatis是来操作数据库表,所以先创建一个数据库及表 CREATE DATABASE spring_db CHARACTER S ...
- 手写Spring-第十二章-Engage!将AOP和Spring生命周期结合起来
前言 这里先说一点题外话.由于明年一月份火焰纹章系列新作-火焰纹章Engage就要发售了,我作为一个老火纹厨表示,这个2022年真的一秒钟也待不下去了(恼).所以就用了这么个标题.也算是强行契合了这次 ...
最新文章
- 情人节学写html5微信游戏
- 常用数据增广方法,解决数据单一问题
- Spring Boot 2.x整合Websocket(基于Spring Boot 2.x 前后端分离 iview admin vue 集成activiti工作流...
- 贷款被拒,因为你的手机有问题
- MySQL工作笔记-编写存储过程批量添加数据
- MySQL数据库学习笔记(三)----基本的SQL语句
- go语言 os.Rename() cannot move the file to a different disk drive 怎么办
- creator 静态属性_cocos creator 属性面板设置
- idea中下载插件超时_完美解决idea无法搜索下载插件的问题
- Hyper-v下安装网络流量监测图形分析工具 Cacti
- Android文件的读写
- 记住密码的实现的2个方案
- 没想到,快手成了“生产力”
- STM32F446高性能MCU芯片介绍
- 再不跳槽,应届毕业生拿的都比我多了!
- TensorFlow案例---概率学中的逆概率
- 对docker一些认知
- 微型计算机显示器的标准接口,显示器原理及接口显示器BIOS编程I(原理部分)
- 电子游戏销售数据分析(基于Python+Tableau)
- 如何迁移操作系统到ssd固态硬盘?