前言
内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。
本文主要涉及书中第七章的部分,依照书中内容以及个人理解对Spring源码进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework
正式进入正文前,强烈推荐先阅读文末参考阅读第一篇:关于 Spring AOP (AspectJ) 你该知晓的一切。阅读完你可以了解清楚:
- 面向对象编程为什么需要面向切面编程(AOP),AOP又是什么?
- AspectJ 和 Spring AOP 又是什么关系?
- AOP 中一些基本概念。
- 帮助理解本文第一部分 动态AOP使用示例。
下面正式Spring源码 AOP部分的研究。
I. 动态AOP使用示例
为了在 spring-mytest 模块中使用上Spring AOP,需要额外引入 spring-aop 和 spring-aspects 两个模块, spring-aop 是可以正常引入的,而 spring-aspects 模块我们在编译源码时由于IDEA不识别所以我们当初去除了该模块,参考这篇文章的做法,我添加了打包好的 spring-aspects 模块。下面就是 spring-mytest 模块的 gradle 配置:
dependencies {
compile(project(":spring-beans"))
compile(project(":spring-context"))
compile(project(":spring-aop"))
compile group: 'org.springframework', name: 'spring-aspects', version: '5.0.7.RELEASE'
testCompile group: 'junit', name: 'junit', version: '4.12'
}
创建需要被拦截的bean
在实际工作中,一个 bean 可能是满足业务需要的核心逻辑。例如下面创建的 bean 中的 test()
方法中可能会封装着某个核心业务,这里就用打印语句简单代替一下。但是,如果我们想在 test()
方法内核心业务前后加入日志来跟踪测试,如果直接修改源码并不符合面向对象的设计方法,而且随意改动原有代码也会造成一定的风险,还好Spring AOP能够帮我们做到了这一点。
public class TestBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
public void test() {
// 核心业务
System.out.println("test");
}
}
创建Advisor
Spring中摒弃了最原始的繁杂配置方式而采用 @AspectJ
注解对 POJO 进行标注,使AOP的工作大大简化,例如,在 AspectJTest
类中,我们要做的就是在所有类的 test()
方法执行前在控制台打印 beforeTest 。而在所有类的 test()
方法执行后打印 afterTest,同时又使用环绕的方式在所有类的 test()
方法执行前后在此分别打印 before1 和 after1 。
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class AspectJTest {
@Pointcut("execution(* *.test(..))")
public void test() {
}
@Before("test()")
public void beforeTest() {
System.out.println("beforeTest");
}
@After("test()")
public void afterTest() {
System.out.println("afterTest");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("before1");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("after1");
return o;
}
}
创建配置文件
要在Spring中开启AOP功能,还需要在配置文件中作如下声明:
<?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: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">
<aop:aspectj-autoproxy />
<bean id="test" class="guo.ping.aop.usedemo.TestBean" />
<bean class="guo.ping.aop.usedemo.AspectJTest" />
</beans>
可以看到,开启AOP功能的是 <aop:aspectj-autoproxy />
这个自定义标签。
测试
首先,先编写测试方法:
public class AopDemoTest {
@Test
public void testAopDemo() {
ApplicationContext context = new ClassPathXmlApplicationContext("aopDemo-Test.xml");
TestBean testBean = (TestBean) context.getBean("test");
testBean.test();
}
}
测试结果:
Spring实现了对所有类的 test()
方法进行增强,使辅助功能可以独立于核心业务之外,方便与程序的扩展和解耦。这里的测试结果可以发现,先执行环绕通知的前后处理方法,然后再处理 @Before 和 @After 通知。
II. 动态AOP自定义标签
我们配置了 <aop:aspectj-autoproxy />
之后便可以开启Spring的AOP功能,而且该标签属于自定义标签类型,那么回顾之前介绍Spring对于自定义标签解析的流程,实现这一功能必然存在着某个继承自 NamespaceHandlerSupport
的实现类,在其 init()
方法中注册了针对 aspectj-autoproxy 的 BeanDefinitionParser
解析器。进入源码详细查看。
参照之前的几篇文章,我们debug进入到解析自定义标签 <aop:aspectj-autoproxy />
,可以看到即将进入解析自定义标签环节。继续深入。
根据命名空间获取 NamespaceHandlerSupport
。
获取结果为 AopNamespaceHandler
:
在 AopNamespaceHandler
类中的 init()
方法中可以清晰的看到,配置了关于 aspectj-autoproxy 的解析器类 AspectJAutoProxyBeanDefinitionParser
。
public class AopNamespaceHandler extends NamespaceHandlerSupport {
/**
* Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
* and '{@code scoped-proxy}' tags.
*/
@Override
public void init() {
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
这样,我们就已经知道Spring对于 <aop:aspectj-autoproxy />
标签的解析相关功能是用 AspectJAutoProxyBeanDefinitionParser
类实现的,一起研究一下这个类。
我们着重研究 AspectJAutoProxyBeanDefinitionParser
类的 parse()
方法。
/**
* 解析aspectj-autoproxy自定义标签
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process;
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* @return
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册AnnotationAwareAspectJAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 对于注解中子类的处理
extendBeanDefinition(element, parserContext);
return null;
}
因为我们该标签主要用于配置,不用于容器进行创建 bean,所以经过两个函数的设置最终返回了 null。我们着重研究两个函数具体做了什么。
注册AnnotationAwareAspectJAutoProxyCreator
首先研究 AopNamespaceUtils
的 registerAspectJAnnotationAutoProxyCreatorIfNecessary()
方法。
/**
* 注册AnnotationAwareAspectJAutoProxyCreator
* @param parserContext
* @param sourceElement
*/
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册或升级AutoProxyCreator定义beanName为org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 对于proxy-target-class以及expose-proxy属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 注册组件并通知,便于监听器进一步处理
// 其中beanDefinition的className为AnnotationAwareAspectJAutoProxyCreator
registerComponentIfNecessary(beanDefinition, parserContext);
}
这其中调用了三个函数,分别做了三件事情。
① 注册或升级AnnotationAwareAspectJAutoProxyCreator
查看 AopConfigUtils
中的 registerAspectJAnnotationAutoProxyCreatorIfNecessary()
方法,传入的 registry
参数实际就是 DefaultListableBeanFactory
的实例。
/**
* 注册或升级AutoProxyCreator定义beanName为org.Springframework.aop.config.internalAutoProxyCreator的BeanDefinition
* @param registry
* @param source
* @return
*/
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
@Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
继续深入:
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry,
@Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 如果已经存在了自动代理创建器且存在的自动代理创建器与现在的不一致那么需要根据优先级来判断到底需要使用哪个
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 是否存在org.springframework.aop.config.internalAutoProxyCreator名称的BeanDefinition
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 如果已经存在的beanDefinition类型不是AnnotationAwareAspectJAutoProxyCreator
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
// 如果AnnotationAwareAspectJAutoProxyCreator的优先级比原来的高,要进行更改
if (currentPriority < requiredPriority) {
// 改变bean最重要的就是改变bean所对应的className属性
apcDefinition.setBeanClassName(cls.getName());
}
}
// 如果已经存在自动代理创建器并且与将要创建的一致,那么无需再次创建
return null;
}
// 如果不存在org.springframework.aop.config.internalAutoProxyCreator名称的BeanDefinition,则创建并注册
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
// 标注该类的角色为基础设施
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
代码附上了详尽的注释,代码主要进行了确保容器注册了 AnnotationAwareAspectJAutoProxyCreator
,对应的 bean 的 name 为 org.springframework.aop.config.internalAutoProxyCreator
。如果已经存在就与 AnnotationAwareAspectJAutoProxyCreator
类型的优先级进行比较,如果现存的优先级低要进行替换。优先级的查询是通过 findPriorityForClass()
方法获得的。
private static int findPriorityForClass(Class<?> clazz) {
return APC_PRIORITY_LIST.indexOf(clazz);
}
private static int findPriorityForClass(@Nullable String className) {
for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {
Class<?> clazz = APC_PRIORITY_LIST.get(i);
if (clazz.getName().equals(className)) {
return i;
}
}
throw new IllegalArgumentException(
"Class name [" + className + "] is not a known auto-proxy creator class");
}
优先级存储在了 APC_PRIORITY_LIST
中,APC_PRIORITY_LIST
在 AopConfigUtils
的静态代码块中进行了初始化。
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
当然,如果容器中没有定义好的 beanDefinition,那么就新建一个并进行注册(添加至Spring容器的 beanDefinitionMap
中)。
② 处理proxy-target-class和expose-proxy属性
useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement)
函数主要对于自定义标签的 proxy-target-class
以及 expose-proxy
属性的处理,这两个属性设置方式如下:
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
我们进入函数体,方法 registry
参数还是那样,sourceElement
就是自定义标签:
/**
* 对于proxy-target-class以及expose-proxy属性的处理
* @param registry
* @param sourceElement
*/
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
if (sourceElement != null) {
// 实现了对proxy-target-class的处理
boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
// 当需要使用CGLIB代理和@AspectJ自动代理支持,设置proxy-target-class为true
if (proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 对expose-proxy的处理,设置为true主要是解决目标对象内部的自我调用
boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
代码主要分两块,分别对两个属性值存在与否以及是否为 true 进行了判断,从而决定是否进行相应的设置。我们来看一下 AopConfigUtils
中的两个方法。
/**
* 设置proxy-target-class为true
* @param registry
*/
public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
}
}
/**
* 设置expose-proxy为true
* @param registry
*/
public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
}
}
两个函数其实就是对第①步创建(或升级)的自动代理创建器 bean 的 beanDefinition 进行了属性的设置,强制使用的过程其实就是对 bean 的定义的修改过程。
那么这两个属性具体有什么作用呢?
proxy-target-class 和Spring的动态代理方式选择相关,如果设置为 true,会强制使用CGLIB动态代理。这一点在后面还会具体分析。
我们具体来看 expose-proxy 属性,顾名思义,其作用是是否暴露代理对象。关于这一点,参考文章中有相关的具体使用场景,这里给大家复现一个简单的案例。
我们简单的修改第一部分的案例。
增加一个 TestBean
的实现接口 ITest
:
public interface ITest {
void test();
void subTest();
}
然后修改 TestBean
,重点是 test()
方法内部调用了 subTest()
方法。
public class TestBean implements ITest{
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
@Override
public void test() {
System.out.println("test");
subTest();
}
@Override
public void subTest() {
System.out.println("subTest");
}
}
对两个方法定义增强,我们增加了一个切点,同时给 test()
方法进行前置和后置通知,给 subTest()
方法设置了环绕通知。
@Aspect
public class AspectJTest {
@Pointcut("execution(* guo.ping.aop.usedemo.TestBean.test(..))")
public void test() {
}
@Pointcut("execution(* guo.ping.aop.usedemo.TestBean.subTest(..))")
public void subTest() {
}
@Before("test()")
public void beforeTest() {
System.out.println("beforeTest");
}
@After("test()")
public void afterTest() {
System.out.println("afterTest");
}
@Around("subTest()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("=========beforeSubTest==========");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("=========afterSubTest==========");
return o;
}
}
运行测试代码,出现以下结果:
好像没什么问题,test()
方法增强成功了。但是,事情没有这么简单,当这两个方法都是需要开启事务的,结果 subTest()
方法没有开启,仅开启了 test()
方法的,这是有问题的。那么如何解决呢?这里就可以依靠 expose-proxy 属性,将代理对象暴露出去,其实是暴露至 ThreadLocal 中,可以通过 AopContext.currentProxy()
获取到代理对象,这在后文会细说。
我们测试一下。修改配置文件 expose-proxy 的属性为 true。
<?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: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">
<aop:aspectj-autoproxy expose-proxy="true"/>
<bean id="test" class="guo.ping.aop.usedemo.TestBean" />
<bean class="guo.ping.aop.usedemo.AspectJTest" />
</beans>
修改 test()
方法的调用方式,先获取代理对象,然后调用 subTest()
方法。
@Override
public void test() {
System.out.println("test");
ITest proxy = (ITest) AopContext.currentProxy();
proxy.subTest();
}
测试结果如下,完全OK。
③ 注册组件并通知
最后一步,是将前两步精心构造的 beanDefinition 进行组件的注册并通知,便于监听器进一步处理。
/**
* 注册组件并通知,便于监听器进一步处理
* @param beanDefinition
* @param parserContext
*/
private static void registerComponentIfNecessary(@Nullable BeanDefinition beanDefinition, ParserContext parserContext) {
if (beanDefinition != null) {
BeanComponentDefinition componentDefinition =
new BeanComponentDefinition(beanDefinition, AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
parserContext.registerComponent(componentDefinition);
}
}
首先用 BeanComponentDefinition
包装了 beanDefinition 和 beanName,然后向 eventListener
注册 componentDefinition
。
public void registerComponent(ComponentDefinition component) {
CompositeComponentDefinition containingComponent = getContainingComponent();
if (containingComponent != null) {
containingComponent.addNestedComponent(component);
}
else {
this.readerContext.fireComponentRegistered(component);
}
}
/**
* Fire an component-registered event.
*/
public void fireComponentRegistered(ComponentDefinition componentDefinition) {
this.eventListener.componentRegistered(componentDefinition);
}
处理子标签
extendBeanDefinition()
方法对自定义标签的子标签又进行了相应的解析并将其设置到 beanDefinition 中。
private void extendBeanDefinition(Element element, ParserContext parserContext) {
BeanDefinition beanDef =
parserContext.getRegistry().getBeanDefinition(AopConfigUtils.AUTO_PROXY_CREATOR_BEAN_NAME);
if (element.hasChildNodes()) {
addIncludePatterns(element, parserContext, beanDef);
}
}
如果存在子节点,那么便会调用 addIncludePatterns()
方法进行解析并设置。
private void addIncludePatterns(Element element, ParserContext parserContext, BeanDefinition beanDef) {
// 用于保存解析的属性
ManagedList<TypedStringValue> includePatterns = new ManagedList<>();
NodeList childNodes = element.getChildNodes();
// 遍历子节点
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node instanceof Element) {
Element includeElement = (Element) node;
TypedStringValue valueHolder = new TypedStringValue(includeElement.getAttribute("name"));
valueHolder.setSource(parserContext.extractSource(includeElement));
includePatterns.add(valueHolder);
}
}
if (!includePatterns.isEmpty()) {
includePatterns.setSource(parserContext.extractSource(element));
beanDef.getPropertyValues().add("includePatterns", includePatterns);
}
}
III. 创建AOP代理
上面已经介绍完关于 aspectj-autoproxy 自定义标签的解析器类 AspectJAutoProxyBeanDefinitionParser
中 parse()
方法中的三个步骤,主要就是注册一个名为 org.springframework.aop.config.internalAutoProxyCreator
类型为 AnnotationAwareAspectJAutoProxyCreator
的 beanDefinition。这个 beanDefinition 的定义是为了实例化 bean 所用,最终还是转化为一个 AnnotationAwareAspectJAutoProxyCreator
类型的实例对象。
那么问题来了,这个 AnnotationAwareAspectJAutoProxyCreator
自动代理创建器类到底有什么用?
对应AOP的实现,基本上就是靠该类去完成,它可以根据 @Point 注解定义的切点来自动代理相匹配的 bean。为了简便配置,Spring使用了在XML文件配置 <aop:aspectj-autoproxy />
自定义标签来帮助我们实现自动注册。
我们先看一下该类的继承结构。
我们可以看到,它实现了 BeanPostProcessor
接口,实现了该接口,当Spring依据 beanDefinition 去加载这个 bean 时会在实例化前调用其 postProcessAfterInitialization()
方法。我们分析AOP的逻辑也从这里出发。
AnnotationAwareAspectJAutoProxyCreator
对于该扩展方法的重写是在父类 AbstractAutoProxyCreator
中实现的:
/**
* Create a proxy with the configured interceptors if the bean is
* identified as one to proxy by the subclass.
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
// 根据给定的bean的class和name构建出个key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 如果它适合被代理,则需要封装指定bean
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
回顾 BeanPostProcessor
接口的作用是 bean 的后处理器,能够在 bean 构造函数构造初始化阶段的前后做一些扩展。那么当 AbstractAutoProxyCreator
发现需要动态代理的对象初始化完成后,我们可以对它进行代理增强,最终容器内存放的是代理 bean。 这就是Spring AOP的主要功能实现方式,也是自定义标签注册 AnnotationAwareAspectJAutoProxyCreator
类型实例的意义所在。
重写的 postProcessAfterInitialization()
方法中核心逻辑就是对目标对象,也就是传递来的形参 bean 进行增强并进行返回,方法为 wrapIfNecessary(bean, beanName, cacheKey)
。
其实本文主要的核心就是研究这个方法。
/**
* 包装bean,增强成功返回代理bean
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经处理过直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 如果无需增强也直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 给定的bean类是否代表一个基础设施类,基础设施类不应代理,或者配置了指定bean不需要自动代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 如果存在增强方法或增强器则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果获取到了增强则需要针对增强创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理bean,传入用SingletonTargetSource包装的原始bean
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;
}
其中包含了各种不需要代理直接返回的情况,分别进行判断并进行记录,旁枝末节我们就先略过,直接进入需要增强的部分。
// 如果存在增强方法或增强器则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果获取到了增强则需要针对增强创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理bean,传入用SingletonTargetSource包装的原始bean
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;
创建并返回代理对象的主要步骤为:
- 获取增强方法或增强器;
- 根据获取到的增强进行代理。
两个步骤分别代表着两个主要方法,分别是 getAdvicesAndAdvisorsForBean()
和 createProxy()
。其中获取增强器首先需要获取所有的,然后再过滤出能够用的,所以本部分会分三小节进行分析流程。
获取增强器
我们进入 getAdvicesAndAdvisorsForBean()
方法。
/**
* 获取增强方法或增强器
* @param beanClass the class of the bean to advise
* @param beanName the name of the bean
* @param targetSource
* @return
*/
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
方法传入的参数是目标对象的类型,bean 的名称。可以看到核心实现最终交给了 findEligibleAdvisors(beanClass, beanName)
方法,而 DO_NOT_PROXY
其实就是 null
。
/**
* 寻找所有合适的增强(增强器并不一定都适用于当前bean,要选出满足我们通配符的增强器)
* Find all eligible Advisors for auto-proxying this class.
* @param beanClass the clazz to find advisors for
* @param beanName the name of the currently proxied bean
* @return the empty List, not {@code null},
* if there are no pointcuts or interceptors
* @see #findCandidateAdvisors
* @see #sortAdvisors
* @see #extendAdvisors
*/
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有的增强
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 寻找所有增强中适用于bean的增强并应用
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 在advice链的开始添加ExposeInvocationInterceptor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
可以清晰的看到主要完成三件事,获取所有、过滤出适用于 bean 增强的然后排序,本节我们着重研究第一步,看看获取所有增强器的实现过程。
@Override
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
// 当使用注解方式配置AOP的时候并不是丢弃了对XML配置的支持。在这里调用父类方法加载配置文件中的AOP声明
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
// 获取bean的注解增强
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
findCandidateAdvisors()
方法是在 AnnotationAwareAspectJAutoProxyCreator
中,可以看到方法上有 @Override
注解,重写自 AbstractAdvisorAutoProxyCreator
的 findCandidateAdvisors()
方法。由于我们这里分析的是使用注解进行的AOP,所以调用的是 AnnotationAwareAspectJAutoProxyCreator
重写的方法,在实现时当使用注解方式配置AOP的时候并不是丢弃了对XML配置的支持,而是也调用父类方法加载配置文件中的AOP声明,然后再调用获取目标 Bean 的注解并增强。
先来看看父类的方法实现,主要就是从XML配置,也就是 beanFactory 容器 (this.cachedAdvisorBeanNames
) 中寻找,获取到所有增强器名称 beanName 后将其对应的实例添加至 list。
/**
* 从XML配置文件获取所有配置的增强
* Find all candidate Advisors to use in auto-proxying.
* @return the List of candidate Advisors
*/
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
/**
* 从XML配置文件中获取增强
* Find all eligible Advisor beans in the current bean factory,
* ignoring FactoryBeans and excluding beans that are currently in creation.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = null;
synchronized (this) {
// 从beanFactory中获取增强器
advisorNames = this.cachedAdvisorBeanNames;
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
}
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
// 获取到所有增强器名称后将其对应的实例添加至list
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
....
....
}
}
}
}
return advisors;
}
分析完父类方法,我们再来看看其特有的获取 bean 注解并增强的逻辑。
// 获取bean的注解增强
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
可以看到,主要是通过 this.aspectJAdvisorsBuilder
的 buildAspectJAdvisors()
方法实现的。我们需要从寻找有 @AspectJ 注解定义的类中的增强器,那么必然会根据容器中现有的 bean,依据它们的 class 然后过滤出用 @AspectJ 注解定义的类,对这些类进行增强器的提取。
总结一下:
- 获取所有 beanName,这一步骤中所有在 beanFactory 中注册的 Bean 都会被提取出来。
- 遍历所有 beanName,并找出声明 @AspectJ 注解的类,进行进一步的处理。
- 对标记为 @AspectJ 注解的类进行增强器的提取。
- 将提取结果加入缓存。
下面来看代码是如何实现这个逻辑的。
/**
* 获取bean的注解增强
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> buildAspectJAdvisors() {
// 记录保存了被增强的beanName
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 1. 获取的是所有的beanName,因为类型是Object
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 2. 循环遍历所有的beanName,过滤带有@AspectJ注解的类,找出对应的增强方法
for (String beanName : beanNames) {
// 不合法的bean则略过,由子类定义合法规则,默认返回true
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 获取对应beanName的bean的类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 如果bean的class类存在@AspectJ注解
if (this.advisorFactory.isAspect(beanType)) {
// 只要被注解的就记录下来
aspectNames.add(beanName);
// 利用AspectMetadata包装了关于该类(用@AspectJ标注的)的一些信息,包括类型,名称,AjType
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 创建了一个工厂,包含了beanFactory和AspectMetadata信息
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 3. 解析标记了@AspectJ注解的类中定义的增强器
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 4. 解析结果记录在缓存中
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
// 记录在缓存中
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
// 当aspectName不为空依据aspectName读取缓存返回
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
代码非常的长,基本涵盖了上面所说的四个步骤,代码注释也对应标注了步骤。
① 获取所有的beanName
这一步通过 BeanFactoryUtils
的 beanNamesForTypeIncludingAncestors()
方法进行获取。我们传入的类型参数为 Object
所有相当于获取所有的 beanName。
/**
* 根据类型获取所有符合的beanNames
*/
public static String[] beanNamesForTypeIncludingAncestors(
ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
// 调用ListableBeanFactory的方法
String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
// 递归调用
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
}
获取过程依赖了容器的 getBeanNamesForType(type, includeNonSingletons, allowEagerInit)
方法,其实这个方法在之前从配置文件中获取增强就已经调用过一次,只不过那一次传入的 type 类型为 Advisor
,这次是 Object
。容器的 getBeanNamesForType(type, includeNonSingletons, allowEagerInit)
方法主要是从容器的两个属性 this.beanDefinitionNames
和 this.manualSingletonNames
中进行遍历,寻找符合 type 的 beanName 进行返回,这里就相当于两个的内容合并进行返回。
② 过滤存在@AspectJ注解的类
判断类是否存在 @AspectJ 利用了 AbstractAspectJAdvisorFactory
中的 isAspect()
方法:
/**
* 判断是否存在@Aspect
*/
@Override
public boolean isAspect(Class<?> clazz) {
return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}
查看 AnnotationUtils
类的 findAnnotation()
方法:
@Nullable
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
return findAnnotation(clazz, annotationType, true);
}
传进的 clazz
参数是对应的 bean 的类型,annotationType
是注解类型,最终会调用重载函数。观察重载函数内部,有一句核心代码,还是调用 clazz.getDeclaredAnnotation(annotationType)
jdk的方法获取类上的注解,然后查看是否有 @AspectJ。
/**
* Perform the search algorithm for {@link #findAnnotation(Class, Class)},
* avoiding endless recursion by tracking which annotations have already
* been <em>visited</em>.
* @param clazz the class to look for annotations on
* @param annotationType the type of annotation to look for
* @param visited the set of annotations that have already been visited
* @return the first matching annotation, or {@code null} if not found
*/
@Nullable
private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
try {
// 获取class上面标注的AspectJ注解
A annotation = clazz.getDeclaredAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
for (Annotation declaredAnn : clazz.getDeclaredAnnotations()) {
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
annotation = findAnnotation(declaredType, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
}
catch (Throwable ex) {
handleIntrospectionFailure(clazz, ex);
return null;
}
// 获取接口,递归查看接口是否拥有@AspectJ注解
for (Class<?> ifc : clazz.getInterfaces()) {
A annotation = findAnnotation(ifc, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
// 获取父类,递归查看父类是否拥有@AspectJ注解
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || Object.class == superclass) {
return null;
}
return findAnnotation(superclass, annotationType, visited);
}
③ 提取@AspectJ类增强方法
其实就是获取 @AspectJ 类中定义的所有的增强器,也就是通知方法。而这一步在代码实现里交给了 advisorFactory.getAdvisors(factory)
方法去实现。在这之前,我们先看一下 AspectMetadata
和 MetadataAwareAspectInstanceFactory
类。
// 利用AspectMetadata包装了关于该类(用@AspectJ标注的)的一些信息,包括类型,名称,AjType
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 创建了一个工厂,包含了beanFactory和AspectMetadata信息
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
首先,AspectMetadata
定义了切面类元数据,主要存储了切面类的名字、切面类的 Class 类型、 AspectJ 中定义的 AjType 类型对象(能存储切面类相关信息)以及 @PointCut 中定义的切点表达式。
/**
* AspectMetadata 切面元数据类
*/
public class AspectMetadata implements Serializable {
/**
* 切面的名字 可能是类的全限定类名 也可能是Spring容器中bean的名字
*/
private final String aspectName;
/**
* 切面类 指带有切面注解的类
*/
private final Class<?> aspectClass;
/**
* 类的类型 这个是AspectJ中定义的类 存储了aspectClass类的类相关信息
* 实现类为 AjTypeImpl
*/
private transient AjType<?> ajType;
/**
* 存储切点表达式
*/
private final Pointcut perClausePointcut;
}
再看 MetadataAwareAspectInstanceFactory
类,这里创建的其实是 BeanFactoryAspectInstanceFactory
实例,主要包含了容器、beanName 以及 AspectMetadata
实例。
public class BeanFactoryAspectInstanceFactory implements MetadataAwareAspectInstanceFactory, Serializable {
private final BeanFactory beanFactory;
private final String name;
private final AspectMetadata aspectMetadata;
}
好了,我们回到 advisorFactory.getAdvisors(factory)
方法。
/**
* 获取@AspectJ标注的类中所有的advisors
* @param aspectInstanceFactory the aspect instance factory
* (not the aspect instance itself in order to avoid eager instantiation)
* @return
*/
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 获取标记为AspectJ类的类型
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
// 获取标记为AspectJ的beanName
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
// 验证class类型标注了AspectJ
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
// 遍历每一个类的方法,去getAdvisor
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// 如果寻找的增强器不为空而且又配置了增强延迟初始化那么需要在首位加入同步实例化增强器
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
// 获取DeclareParents注解
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
函数中首先去遍历类中的每一个方法,过滤出所有的增强方法;然后考虑到配置中可能会将增强配置成延迟初始化,则需要在首位加入同步实例化增强器来保证增强使用前进行了实例化;最后是对 DeclareParents 注解进行获取。
a. 普通增强器的获取
通过 getAdvisorMethods(aspectClass)
对AspectJ类中所有方法进行遍历,然后调用 getAdvisor()
方法获取增强器。getAdvisorMethods(aspectClass)
将获取到除了 @PointCut 标注所有的方法,包括父类的,如下图。
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
final List<Method> methods = new ArrayList<>();
ReflectionUtils.doWithMethods(aspectClass, method -> {
// 声明为Pointcut的方法不处理
if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
methods.add(method);
}
});
methods.sort(METHOD_COMPARATOR);
return methods;
}
然后遍历每一个方法,调用 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName)
方法。获取增强器首先对切点方法的注解的获取以及根据注解信息生成增强。
@Override
@Nullable
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
// 再次验证类是否是包含@AspectJ注解
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 从注解中进行切点信息的获取
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 根据切点信息生成增强器,所有的增强都由Advisor的实现类InstantiationModelAwarePointcutAdvisorImpl统一封装
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
对切点信息的获取是指获取指定注解的表达式信息,如 @Before(“test()”)。这一实现是通过 getPointcut()
方法进行实现。
@Nullable
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 获取方法上指定的注解
AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 使用AspectJExpressionPointcut实例封装获取的信息
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
// 提取得到的注解中的表达式如:@Before("test()")中的test()
ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
getPointcut()
方法中首先通过 findAspectJAnnotationOnMethod()
寻找 Method 上标注的注解进行查询,是否是增强型注解,符合的利用 AspectJAnnotation
进行封装。
/**
* Find and return the first AspectJ annotation on the given method
* (there <i>should</i> only be one anyway...).
*/
@SuppressWarnings("unchecked")
@Nullable
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
// 设置敏感的注解类
Class<?>[] classesToLookFor = new Class<?>[] {
Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
for (Class<?> c : classesToLookFor) {
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
/**
* 获取指定方法上的注解并使用AspectJAnnotation封装
* @param method
* @param toLookFor
* @param <A>
* @return
*/
@Nullable
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
// 返回的泛型其实是对应的注解类型,如org.aspectj.lang.annotation.Around
A result = AnnotationUtils.findAnnotation(method, toLookFor);
if (result != null) {
return new AspectJAnnotation<>(result);
}
else {
return null;
}
}
可以看到,对一个方法上注解的查询只查找 Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class
这五种,其他方法上的注解则忽略。查找出的注解类型(如 org.aspectj.lang.annotation.Around
)利用 AspectJAnnotation
进行封装。封装的过程中已经保存了注解中切点表达式,如:@Before(“test()”) 中的 test()。获取到切点表达式后再用 AspectJExpressionPointcut
进行封装并返回。这里说的是 getPointcut()
函数将包含方法上切点表达式的 AspectJExpressionPointcut
进行了返回,一定要理清逻辑,最好debug一下。
此外,需要注意的是,这里 getPointcut()
函数最后返回的仅仅只是拥有切点表达式的信息,至于是哪一种增强注解Spring并没有记录并返回,意味着后面还会继续调用该函数继续获取。
getPointcut()
函数执行完成,获取到切点表达式信息,然后利用切点信息生成增强器。所有的增强都是 Advisor 的实现类 InstantiationModelAwarePointcutAdvisorImpl
统一封装的。
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
可以看到在封装过程中只是简单的将信息封装在类的实例中,所有的信息单纯的进行赋值。在图中debug位置处,封装信息的最后一步中还完成了对于增强器(Advice)的初始化。因为不同的增强体现的逻辑是不同的,比如 @Before(“test()”) 和 @After(“test()”) 分别是在前后进行增强,那么就需要有不同的增强器来分别实现不同的逻辑。而根据注解中的信息这种方式初始化增强器就是在这里实现的。我们进入 instantiateAdvice()
函数:
/**
* 根据注解中的切点表达式信息初始化对应的增强器
* @param pointcut
* @return
*/
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
函数内调用了 getAdvice()
方法,传入了 Method 方法,切点表达式以及 aspectInstanceFactory
等。从函数逻辑上走,可以看出主要是依据方法去寻找其用何种增强注解进行了标记,将其解析出并创建出对应的 Advice。之前说过Spring没有记录下具体方法上的注解,仅仅记录了方法和切点表达式,所以这里需要再次获取。
/**
* 获取方法上的增强注解类型然后创建对应的Advice
* @param candidateAdviceMethod the candidate advice method
* @param expressionPointcut the AspectJ expression pointcut
* @param aspectInstanceFactory the aspect instance factory
* @param declarationOrder the declaration order within the aspect
* @param aspectName the name of the aspect
* @return
*/
@Override
@Nullable
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
// 获取对应的@AspectJ注解标记的类类型
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
validate(candidateAspectClass);
// Spring之前只保存了对应的方法以及对应的切点表达式,没有返回记录AspectJAnnotation,所以再次获取到是哪种增强注解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod);
}
AbstractAspectJAdvice springAdvice;
// 根据不同的注解类型进行封装不同的增强器
switch (aspectJAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtPointcut:
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice...
// 设置构造出的增强器Advice相关属性
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
代码中的 switch-case 语句块针对不同的注解类型创建了不同类型的 Advice 增强器实例。关于这些 Advice
类,都继承自 AbstractAspectJAdvice
类,主要记录了增强方法 Method、切点表达式以及 BeanFactoryAspectInstanceFactory
实例, Advice 拥有这些信息之后调用增强方法时会对切点表达式等进行匹配,这些后文再说。
public class AspectJAfterAdvice extends AbstractAspectJAdvice
implements MethodInterceptor, AfterAdvice, Serializable {
public AspectJAfterAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
····
}
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
}
这样,一个增强方法就能够创建出一个 Advice。最终,对 @AspectJ 注解类中每一个方法去遍历匹配这五种注解,最终返回所有的 Advice。
b. 增加同步实例化增强器
上一步已经找出了所有增强通知方法并创建出对应的 Advice 实例,我们再确认一下代码分析到哪里了。
/**
* 获取@AspectJ标注的类中所有的advisors
*/
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 获取标记为AspectJ类的类型
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
// 获取标记为AspectJ的beanName
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
// 验证class类型标注了AspectJ
validate(aspectClass);
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once.
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
// 遍历类的每一个方法,去getAdvisor
for (Method method : getAdvisorMethods(aspectClass)) {
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// ===========================分析到这============================
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// 如果寻找的增强器不为空而且又配置了增强延迟初始化那么需要在首位加入同步实例化增强器
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
// 获取DeclareParents注解
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
如果寻找的增强器不为空而且又配置了增强延迟初始化,那么需要在首位加入同步实例化增强器。同步实例化增强器 SyntheticInstantiationAdvisor
如下:
/**
* 同步实例化增强器
* Synthetic advisor that instantiates the aspect.
* Triggered by per-clause pointcut on non-singleton aspect.
* The advice has no effect.
*/
@SuppressWarnings("serial")
protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
// 目标方法前调用,类似@Before
super(aif.getAspectMetadata().getPerClausePointcut(), (MethodBeforeAdvice)
// 简单初始化aspect
(method, args, target) -> aif.getAspectInstance());
}
}
c. 获取DeclareParents注解
@DeclareParents 注解属于引介增强,在前面我们已经提到了前置、后置、环绕、最终、异常等增强形式,它们的增强对象都是针对方法级别的,而引介增强,则是对类级别的增强,我们可以通过引介增强为目标类添加新的属性和方法,更为诱人的是,这些新属性或方法是可以根据我们业务逻辑需求而动态变化的。@DeclareParents 注解的作用主要是给目标类添加新的方法,不仅仅是方法的增强了。具体用法可以参考文末文章推荐。
// Find introduction fields.
// 获取DeclareParents注解
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
首先需要获取用 @AspectJ 标注的类中所有的属性(之前我们是获取所有的方法),查看是否存在 @DeclareParents 注解,如果有则需要使用 DeclareParentsAdvisor
进行功能封装。
/**
* Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor}
* for the given introduction field.
* <p>Resulting Advisors will need to be evaluated for targets.
* @param introductionField the field to introspect
* @return the Advisor instance, or {@code null} if not an Advisor
*/
@Nullable
private Advisor getDeclareParentsAdvisor(Field introductionField) {
// 获取是否存在@DeclareParents注解
DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
if (declareParents == null) {
// Not an introduction field
return null;
}
if (DeclareParents.class == declareParents.defaultImpl()) {
throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
}
return new DeclareParentsAdvisor(
introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
}
④ 遍历每一个beanName
其实没有这一部分,但是为了提醒读者,上述代码分析流程只是说了一个 beanName 对应的 @AspectJ 的所有的增强器(增强方法)的获取,别忘了要扫描所有的 beanName。
/**
* 获取bean的注解增强
* Look for AspectJ-annotated aspect beans in the current bean factory,
* and return to a list of Spring AOP Advisors representing them.
* <p>Creates a Spring Advisor for each AspectJ advice method.
* @return the list of {@link org.springframework.aop.Advisor} beans
* @see #isEligibleBean
*/
public List<Advisor> buildAspectJAdvisors() {
// 记录保存了被增强的beanName
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 获取的是所有的beanName,因为类型是Object
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 循环遍历所有的beanName,过滤带有@AspectJ注解的类,找出对应的增强方法
for (String beanName : beanNames) {
// 不合法的bean则略过,由子类定义合法规则,默认返回true
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 获取对应beanName的bean的类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 如果bean的class类存在@AspectJ注解
if (this.advisorFactory.isAspect(beanType)) {
// 只要被注解的就记录下来,下次就可以直接使用
aspectNames.add(beanName);
// 利用AspectMetadata包装了关于该类(用@AspectJ标注的)的一些信息,包括类型,名称,AjType
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 创建了一个工厂,包含了beanFactory和AspectMetadata信息
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 解析标记了@AspectJ注解的类中定义的增强器
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 解析结果记录在缓存中
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
// 记录在缓存中
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
// 当aspectName不为空依据aspectName读取缓存返回
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
其次,别忘了,我们目的是在一个 bean 后处理方法中为其创建代理对象,所以主要是寻找所有的增强器然后找到匹配的然后生成代理对象,目的明确,那么下一步就是寻找匹配的增强器。
寻找匹配增强器
/**
* 寻找所有合适的增强(增强器并不一定都适用于当前bean,要选出满足我们通配符的增强器)
* Find all eligible Advisors for auto-proxying this class.
* @param beanClass the clazz to find advisors for
* @param beanName the name of the currently proxied bean
* @return the empty List, not {@code null},
* if there are no pointcuts or interceptors
* @see #findCandidateAdvisors
* @see #sortAdvisors
* @see #extendAdvisors
*/
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有的增强
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 寻找所有增强中适用于bean的增强并应用
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 在advice链的开始添加ExposeInvocationInterceptor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
并不是所有的增强器都适合,必须过滤出我们配置的通配符的增强器。实现逻辑在 findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName)
中,参数包括前面得到的所有 Advisors、目标 bean 的类型以及目标 bean 的名称。
/**
* 寻找所有增强中适用于目标bean的增强并应用
* Search the given candidate Advisors to find all Advisors that
* can apply to the specified bean.
* @param candidateAdvisors the candidate Advisors
* @param beanClass the target's bean class
* @param beanName the target's bean name
* @return the List of applicable Advisors
* @see ProxyCreationContext#getCurrentProxiedBeanName()
*/
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
// 设置当前要代理的beanName
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 过滤已经得到的advisors
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
// 清除标记
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
过滤时先添加目标 bean 正在代理的标记,无论过滤是否完成都清除标记。具体过滤细节进入 AopUtils
类的 findAdvisorsThatCanApply(candidateAdvisors, beanClass)
方法:
/**
* 主要功能是寻找所有增强器中适用于当前class的增强器
* Determine the sublist of the {@code candidateAdvisors} list
* that is applicable to the given class.
* @param candidateAdvisors the Advisors to evaluate
* @param clazz the target class
* @return sublist of Advisors that can apply to an object of the given class
* (may be the incoming List as-is)
*/
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
// 保存筛选的合适增强器
List<Advisor> eligibleAdvisors = new ArrayList<>();
// 首先处理引介增强
for (Advisor candidate : candidateAdvisors) {
// 核心匹配实现:canApply()
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// 引介增强已经处理
continue;
}
// 如果不是IntroductionAdvisor实例,对于普通的增强,调用canApply()重载方法
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
逻辑还是非常清楚的,对所有的增强器进行遍历,查看是否匹配。由于引介增强的处理和普通增强的处理是不一样的,所以需要分开处理。这里先将所有的引介增强匹配出来,然后再普通增强,由于存储于 list,就有了一定的有序性。我们来查看两个重载方法 canApply(candidate, clazz, hasIntroductions)
实现:
/**
* Can the given advisor apply at all on the given class?
* This is an important test as it can be used to optimize
* out a advisor for a class.
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass) {
return canApply(advisor, targetClass, false);
}
/**
* Can the given advisor apply at all on the given class?
* <p>This is an important test as it can be used to optimize out a advisor for a class.
* This version also takes into account introductions (for IntroductionAwareMethodMatchers).
* @param advisor the advisor to check
* @param targetClass class we're testing
* @param hasIntroductions whether or not the advisor chain for this bean includes
* any introductions
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
// 如果增强是引介增强,那么直接获取切点表达式去匹配targetClass即可
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
// 如果属于PointcutAdvisor,继续调用重载方法
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
// 其余的默认为符合条件
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
/**
* Can the given pointcut apply at all on the given class?
* <p>This is an important test as it can be used to optimize
* out a pointcut for a class.
* @param pc the static or dynamic pointcut to check
* @param targetClass the class to test
* @param hasIntroductions whether or not the advisor chain
* for this bean includes any introductions
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 获取切点表达式,并判断是否能够匹配上目标类中的方法
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
// 然后继续寻找匹配类中哪个方法
// pc.getMethodMatcher()和pc.getClassFilter()一样都能够获得切点表达式,然后去匹配
MethodMatcher methodMatcher = pc.getMethodMatcher();
if (methodMatcher == MethodMatcher.TRUE) {
// No need to iterate the methods if we're matching any method anyway...
return true;
}
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class<?>> classes = new LinkedHashSet<>();
// 如果是代理类,则需要返回原始类
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
// 遍历类中的所有方法
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
在处理逻辑上,其实这一段函数需要做的事情还是很多的。首先,需要根据当前的增强器,获取它所配置的切点表达式是什么;然后,需要根据解析切点表达式,才能去判断目标类是否符合切点表达式的增强范围;最后,再继续根据表达式判断目标类中的哪一个方法要被增强。
以文章开头的实例为例,我们先要获取 "execution( .test(…))" 切点表达式,这一点是依靠 pc.getClassFilter()
或者 pc.getMethodMatcher()
去实现的,简单看一下这两个函数实现,它们调用了公共的方法 obtainPointcutExpression()
。该方法最后得到的切点表达式会保存在 this.pointcutExpression
中。
@Override
public ClassFilter getClassFilter() {
// 获取切点表达式
obtainPointcutExpression();
return this;
}
@Override
public MethodMatcher getMethodMatcher() {
obtainPointcutExpression();
return this;
}
/**
* Check whether this pointcut is ready to match,
* lazily building the underlying AspectJ pointcut expression.
*/
private PointcutExpression obtainPointcutExpression() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutClassLoader = determinePointcutClassLoader();
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
return this.pointcutExpression;
}
获取到切点表达式之后,我们调用 matches(targetClass)
方法去匹配切点表达式的范围是否包含了 targetClass
,核心实现Spring利用了 AspectJ 中的 couldMatchJoinPointsInType
方法。
/**
* 判断切点表达式指定的范围是否包含了目标类
* @param targetClass
* @return
*/
@Override
public boolean matches(Class<?> targetClass) {
PointcutExpression pointcutExpression = obtainPointcutExpression();
try {
try {
return pointcutExpression.couldMatchJoinPointsInType(targetClass);
}
catch (ReflectionWorldException ex) {
logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
if (fallbackExpression != null) {
return fallbackExpression.couldMatchJoinPointsInType(targetClass);
}
}
}
catch (Throwable ex) {
logger.debug("PointcutExpression matching rejected target class", ex);
}
return false;
}
确认该类是切点表达式中定义范围内,那么接下来就要继续解析表达式,匹配类中的具体哪些方法。主要是通过重载方法 matches(method, targetClass)
实现,多传了一个参数具体什么方法。
@Override
public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
obtainPointcutExpression();
ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);
// Special handling for this, target, @this, @target, @annotation
// in Spring - we can optimize since we know we have exactly this class,
// and there will never be matching subclass at runtime.
if (shadowMatch.alwaysMatches()) {
return true;
}
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
if (hasIntroductions) {
return true;
}
// A match test returned maybe - if there are any subtype sensitive variables
// involved in the test (this, target, at_this, at_target, at_annotation) then
// we say this is not a match as in Spring there will never be a different
// runtime subtype.
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
}
}
这样将所有的增强器全部验证一遍,返回所有的符合的增强器。
创建代理
寻找到合适的增强器后,可以进行代理对象的创建了。在这之前,findEligibleAdvisors()
方法中还调用了 extendAdvisors(eligibleAdvisors)
方法,在 advice 链的开始添加 ExposeInvocationInterceptor
。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 获取所有的增强
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 寻找所有增强中适用于bean的增强并应用
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 在advice链的开始添加ExposeInvocationInterceptor
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
查看该方法:
/**
* 在advice链的开始添加ExposeInvocationInterceptor
* Adds an {@link ExposeInvocationInterceptor} to the beginning of the advice chain.
* These additional advices are needed when using AspectJ expression pointcuts
* and when using AspectJ-style advice.
*/
@Override
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
继续进入 AspectJProxyUtils
:
/**
* Add special advisors if necessary to work with a proxy chain that contains AspectJ advisors.
* This will expose the current Spring AOP invocation (necessary for some AspectJ pointcut matching)
* and make available the current AspectJ JoinPoint. The call will have no effect if there are no
* AspectJ advisors in the advisor chain.
* @param advisors the advisors available
* @return {@code true} if any special {@link Advisor Advisors} were added, otherwise {@code false}
*/
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not required
if (!advisors.isEmpty()) {
boolean foundAspectJAdvice = false;
for (Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as
// this might eagerly instantiate a non-singleton AspectJ aspect
if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true;
}
}
// 在Advisors中添加一个ExposeInvocationInterceptor
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true;
}
}
return false;
}
当匹配的增强器 list 不为空时,如果不存在 ExposeInvocationInterceptor
,那么在集合第一个位置添加一个单例的 ExposeInvocationInterceptor.ADVISOR
实例。
/**
* Singleton advisor for this class. Use in preference to INSTANCE when using
* Spring AOP, as it prevents the need to create a new Advisor to wrap the instance.
*/
public static final Advisor ADVISOR = new DefaultPointcutAdvisor(INSTANCE) {
@Override
public String toString() {
return ExposeInvocationInterceptor.class.getName() +".ADVISOR";
}
};
这样返回的匹配增强器集合中就多包涵一个元素,如下图所示:
我们记录一下,我们分析完上面的代码已经到了哪一步了。
/**
* 包装bean,增强成功返回代理bean
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经处理过直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 如果无需增强也直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 给定的bean类是否代表一个基础设施类,基础设施类不应代理,或者配置了指定bean不需要自动代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 如果存在增强方法或增强器则创建代理
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// ===========================到这里结束==========================
// 如果获取到了增强则需要针对增强创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理bean,传入用SingletonTargetSource包装的原始bean
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;
}
我们获取匹配的增强器集合转化为数组存储于 specificInterceptors
,如果不为空,那么就进行利用增强器创建代理 bean。进入 createProxy()
方法:
/**
* 为原始bean创建AOP代理对象,主要先对ProxyFactory做初始化,便于委托其进行真正创建代理
* Create an AOP proxy for the given bean.
* @param beanClass the class of the bean
* @param beanName the name of the bean
* @param specificInterceptors the set of interceptors that is
* specific to this bean (may be empty, but not null)
* @param targetSource the TargetSource for the proxy,
* already pre-configured to access the bean
* @return the AOP proxy for the bean
* @see #buildAdvisors
*/
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
// 获取当前类中的相关属性
proxyFactory.copyFrom(this);
// 决定对于给定的原始bean是否应该使用targetClass而不是他的接口代理
// 检查proxyTargetClass设置以及preserveTargetClass属性
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
// 加入增强器
proxyFactory.addAdvisors(advisors);
// 定制代理
proxyFactory.setTargetSource(targetSource);
// 定制代理,留作子类扩展
customizeProxyFactory(proxyFactory);
// 用来控制代理工厂被配置后,是否还允许修改通知
// 缺省值为false(即在代理被配置之后,不允许修改代理的配置)
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 委托ProxyFactory去真正创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
对于代理类的创建以及处理,Spring委托了 ProxyFactory
去处理。所以,在此函数中主要是对 ProxyFactory
进行的初始化操作,进而委托其创建代理。对 ProxyFactory
的初始化包括如下:
- 获取当前类中的属性赋给
ProxyFactory
; - 添加代理接口;
- 封装 Advisor 并加入到
ProxyFactory
中; - 设置要代理的类;
- 当然Spring为子类提供了定制初始化
ProxyFactory
的函数customizeProxyFactory(proxyFactory)
,子类可以在此函数中进行对ProxyFactory
的进一步封装; - 委托
ProxyFactory
创建代理操作。
① 创建初始化ProxyFactory
首先先创建了一个 ProxyFactory
实例,然后对其属性进行赋值,利用 proxyFactory.copyFrom(this)
方法:
/**
* Copy configuration from the other config object.
* @param other object to copy configuration from
*/
public void copyFrom(ProxyConfig other) {
Assert.notNull(other, "Other ProxyConfig object must not be null");
this.proxyTargetClass = other.proxyTargetClass;
this.optimize = other.optimize;
this.exposeProxy = other.exposeProxy;
this.frozen = other.frozen;
this.opaque = other.opaque;
}
传参传了 this 对象,AbstractAutoProxyCreator
继承自 ProxyConfig
。
② 判断是否直接对目标类代理
然后进行判断是否直接对目标对象进行代理,如果不是的话,那么需要进一步获取目标对象类的所有接口,尝试寻找合适的接口进行创建代理;如果找不到合适的接口可供代理,那么还是认为是直接对原始目标 bean 的类进行创建代理。
关于实现逻辑,下面函数中已经给出了详细的注释。
// 决定对于给定的原始bean是否直接对它的类型(targetClass)进行代理而不是对它实现的接口进行代理
// 检查proxyFactory的proxyTargetClass属性设置以及目标bean的beanDefinition中preserveTargetClass属性设置
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
// 如果beanDefinition中preserveTargetClass属性为true,那么将proxyTargetClass属性设置true
proxyFactory.setProxyTargetClass(true);
}
else {
// 试图寻找合适的接口进行代理,如果没有则还是将proxyTargetClass属性设置true
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
/**
* 返回是否直接代理目标类以及任何接口
* Return whether to proxy the target class directly as well as any interfaces.
*/
public boolean isProxyTargetClass() {
return this.proxyTargetClass;
}
/**
* 根据beanDefinition中preserveTargetClass属性配置判断是否应该代理
* Determine whether the given bean should be proxied with its target
* class rather than its interfaces. Checks the
* {@link #PRESERVE_TARGET_CLASS_ATTRIBUTE "preserveTargetClass" attribute}
* of the corresponding bean definition.
* @param beanFactory the containing ConfigurableListableBeanFactory
* @param beanName the name of the bean
* @return whether the given bean should be proxied with its target class
*/
public static boolean shouldProxyTargetClass(ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
// beanDefinition中是否定义了preserveTargetClass属性为true
return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
}
return false;
}
/**
* Check the interfaces on the given bean class and apply them to the {@link ProxyFactory},
* if appropriate.
* <p>Calls {@link #isConfigurationCallbackInterface} and {@link #isInternalLanguageInterface}
* to filter for reasonable proxy interfaces, falling back to a target-class proxy otherwise.
* @param beanClass the class of the bean
* @param proxyFactory the ProxyFactory for the bean
*/
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
// 获取目标bean类实现的所有接口
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
// 遍历所有的接口
for (Class<?> ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
// Must allow for introductions; can't just set interfaces to the target's interfaces only.
for (Class<?> ifc : targetInterfaces) {
// 添加代理接口
proxyFactory.addInterface(ifc);
}
}
else {
// 如果没有合适的接口可以代理,最终还是将proxyTargetClass属性设置为true
proxyFactory.setProxyTargetClass(true);
}
}
③ 封装所有增强器、拦截器
下一步通过 buildAdvisors(beanName, specificInterceptors)
方法封装了 Advisors
并加入到 ProxyFactory
中,通过 ProxyFactory
提供的 addAdvisors(advisors)
方法直接将增强器置入代理创建工厂中。
/**
* 由于Spring中涉及过多的拦截器、增强器、增强方法等方式进行逻辑增强,有必要将他们统一封装成Advisor来进行代理的创建,完成增强的封装过程
* Determine the advisors for the given bean, including the specific interceptors
* as well as the common interceptor, all adapted to the Advisor interface.
* @param beanName the name of the bean
* @param specificInterceptors the set of interceptors that is
* specific to this bean (may be empty, but not null)
* @return the list of Advisors for the given bean
*/
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
// Handle prototypes correctly...
// 解析注册的所有interceptorName
Advisor[] commonInterceptors = resolveInterceptorNames();
// 容纳所有的拦截器、增强器等,稍后进行遍历全部封装为Advisor实例
List<Object> allInterceptors = new ArrayList<>();
if (specificInterceptors != null) {
// 加入之前获取到的所有合适的增强器和后添加拦截器
allInterceptors.addAll(Arrays.asList(specificInterceptors));
// 再添加注册的所有interceptorName
if (commonInterceptors.length > 0) {
if (this.applyCommonInterceptorsFirst) {
allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
}
else {
allInterceptors.addAll(Arrays.asList(commonInterceptors));
}
}
}
if (logger.isDebugEnabled()) {
int nrOfCommonInterceptors = commonInterceptors.length;
int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
logger.debug("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
" common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
}
// 容纳封装好的Advisor
Advisor[] advisors = new Advisor[allInterceptors.size()];
for (int i = 0; i < allInterceptors.size(); i++) {
// 拦截器进行封装转化为Advisor
advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
}
return advisors;
}
由于Spring中涉及过多的拦截器、增强器、增强方法等方式进行逻辑增强,有必要将他们统一封装成 Advisor 实例来进行代理的创建,完成增强的封装过程。
首先获取容器中注册的所有 interceptorName,存放在 this.interceptorNames
中,实例化它们然后用适配器进行包装成 Advisor 实例。
/**
* Resolves the specified interceptor names to Advisor objects.
* @see #setInterceptorNames
*/
private Advisor[] resolveInterceptorNames() {
BeanFactory bf = this.beanFactory;
ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
List<Advisor> advisors = new ArrayList<>();
// 遍历容器的this.interceptorNames属性,实例化对应的bean,用适配器进行包装
for (String beanName : this.interceptorNames) {
if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
Assert.state(bf != null, "BeanFactory required for resolving interceptor names");
Object next = bf.getBean(beanName);
advisors.add(this.advisorAdapterRegistry.wrap(next));
}
}
return advisors.toArray(new Advisor[0]);
}
解析出的拦截器将会和我们之前花了大力气寻找出的匹配的增强器集合一同进行统一的封装,使用 DefaultAdvisorAdapterRegistry
的 wrap()
方法,方法逻辑注释已经基本涵盖清晰。
@Override
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
// 如果要封装的对象本身直接是Advisor类型则无需再过多处理直接返回
if (adviceObject instanceof Advisor) {
return (Advisor) adviceObject;
}
// 因为此封装方法只对Advisor和Advice两种类型的数据有效,如果不是这两种就不会封装,抛出异常
if (!(adviceObject instanceof Advice)) {
throw new UnknownAdviceTypeException(adviceObject);
}
// 强转成Advice类型对象
Advice advice = (Advice) adviceObject;
// 如果属于MethodInterceptor拦截器,返回DefaultPointcutAdvisor进行包装的advice
if (advice instanceof MethodInterceptor) {
// So well-known it doesn't even need an adapter.
return new DefaultPointcutAdvisor(advice);
}
// 如果存在Advisor的适配器那么也同样需要进行封装
for (AdvisorAdapter adapter : this.adapters) {
// 查看此advice是否是MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter所支持的
if (adapter.supportsAdvice(advice)) {
return new DefaultPointcutAdvisor(advice);
}
}
throw new UnknownAdviceTypeException(advice);
}
④ ProxyFactory创建代理对象
创建代理对象是通过下方函数实现的,我们具体深入查看。
/**
* 根据ProxyFactory的设置创建代理对象
* Create a new proxy according to the settings in this factory.
* <p>Can be called repeatedly. Effect will vary if we've added
* or removed interfaces. Can add and remove interceptors.
* <p>Uses the given class loader (if necessary for proxy creation).
* @param classLoader the class loader to create the proxy with
* (or {@code null} for the low-level proxy facility's default)
* @return the proxy object
*/
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
a. 创建代理
我们先来研究 createAopProxy()
创建代理部分:
/**
* Subclasses should call this to get a new AOP proxy. They should <b>not</b>
* create an AOP proxy with {@code this} as an argument.
*/
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 创建代理
return getAopProxyFactory().createAopProxy(this);
}
/**
* Return the AopProxyFactory that this ProxyConfig uses.
*/
public AopProxyFactory getAopProxyFactory() {
return this.aopProxyFactory;
}
继续查看 DefaultAopProxyFactory
中的 createAopProxy(AdvisedSupport config)
方法:
/**
* 创建动态代理的真正地方:JDK和CGlib
* @param config the AOP configuration in the form of an
* AdvisedSupport object
* @return
* @throws AopConfigException
*/
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// optimize用来控制通过CGlib创建的代理是否使用激进的优化策略
// proxyTargetClass为true时,目标类本身被代理而不是目标类的接口,设置方式为<aop:aspectj-autoproxy proxy-target-class="true"/>
// hasNoUserSuppliedProxyInterfaces:是否存在代理接口,否为true
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// JDK动态代理
return new JdkDynamicAopProxy(config);
}
// CGlib动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
// JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
我们终于看到了Spring AOP常说的原理JDK、CGLIB动态代理的部分了。Spring究竟如何选择是用哪种方式创建动态代理的呢? 仔细察看源码判断逻辑。
- optimize:用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理,对于JDK动态代理(缺省代理)无效;
- proxyTargetClass:这个属性为 true 时,目标类本身将被代理而不是目标类的接口,CGLIB代理将被创建。手动强制设置可以在配置文件中配置 <aop:aspectj-autoproxy proxy-target-class=“true” > 。除了手动强制设置之外,之前在本节第②部分判断是否直接对目标类代理内容中,也对这个属性做了设置,需要使用CGLIB代理时统统都设置为 true,读者可回头再看。
- hasNoUserSuppliedProxyInterfaces:是否存在代理接口,否为 true。即使你未声明 proxy-target-class=“true” ,但运行类没有继承接口,Spring也会自动使用CGLIB代理。
所以Spring在目标类有实现的接口情况下,默认希望使用JDK动态代理,除非用户手动强制使用CGLIB。当然,如果目标类没有实现任何接口,二话不说上CGLIB。
分析清楚Spring选择动态代理方式的逻辑后,我们就要分别去看JDK和CGLIB是如何实现的,分别对应 new JdkDynamicAopProxy(config)
和 new ObjenesisCglibAopProxy(config)
。
先来回顾两种动态代理的原理。
b. JDK、CGLIB动态代理原理
关于JDK动态代理原理,推荐阅读这篇文章。
首先我们创建一个接口,定义了在数据库插入、查询用户的操作。
/**
* @description: User的数据库持久层
*/
public interface UserDao {
int saveUser(User user);
User getUserByName(String userName);
}
创建一个实现类。
/**
* @description: UserDao的实现类
*/
public class UserDaoImpl implements UserDao {
@Override
public int saveUser(User user) {
System.out.println("插入成功:" + user);
return 1;
}
@Override
public User getUserByName(String userName) {
User user = new User(userName, "[email protected]");
System.out.println("查询成功:" + user);
return user;
}
}
使用JDK动态代理需要创建一个 InvocationHandler
接口的实现类,实现其中的 invoke()
方法,该方法定义了我们代理对象有什么样的增强行为,这里我是模拟在目标方法前后开启和关闭事务。
/**
* @description: 实现InvocationHandler接口
*/
public class UserDaoInvocationHandler implements InvocationHandler {
private Object raw;
public UserDaoInvocationHandler(Object raw) {
this.raw = raw;
}
/**
* @param proxy 动态生成的代理类的实例,就是$Proxy4的实例
* @param method 对应的method方法
* @param args method需要的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 保存动态生成的字节码文件
// byte[] b = ProxyGenerator.generateProxyClass(proxy.getClass().getSimpleName(), proxy.getClass().getInterfaces());
// String currentUrl = "E:\\spring_src\\spring-framework\\spring-mytest\\src\\main\\java\\guo\\ping\\aop\\jdk\\";
// FileOutputStream out = new FileOutputStream(currentUrl + proxy.getClass().getSimpleName() + ".class");
// out.write(b);
// out.flush();
// out.close();
System.out.println("》》》》开启事务《《《《");
System.out.println(proxy.getClass());
Object proxyObject = method.invoke(raw, args);
System.out.println("》》》》关闭事务《《《《");
return proxyObject;
}
/**
* 获取代理对象,利用Proxy的newProxyInstance方法
* @return
*/
public Object getProxy() {
return Proxy.newProxyInstance(raw.getClass().getClassLoader(), raw.getClass().getInterfaces(), this);
}
}
测试代码与结果如下:
@Test
public void testUserDaoJDKProxy() {
// 目标对象
UserDao userDao = new UserDaoImpl();
System.out.println("========普通对象调用方法========");
userDao.getUserByName("小明");
userDao.saveUser(new User("小花", "[email protected]"));
System.out.println("========代理对象增强方法========");
UserDaoInvocationHandler userDaoInvocationHandler = new UserDaoInvocationHandler(userDao);
// 创建代理对象
UserDao proxy = (UserDao) userDaoInvocationHandler.getProxy();
proxy.getUserByName("小明");
proxy.saveUser(new User("小花", "[email protected]"));
}
仔细的分析代码逻辑,我们首先创建了一个 UserDaoInvocationHandler
实例,通过构造函数我们将目标对象 userDao 传入,随后获取代理对象我们是通过自定义的 getProxy()
方法,而本质还是调用 Proxy
类的 newProxyInstance()
方法。继续深入就是JDK的动态代理源码实现了,这部分分析详见推荐阅读文章。其实主要就是动态生成代理类(Class字节码文件),然后获取构造器创建实例并返回。我们假设你已经看到反编译代理类的字节码文件了,那么下面就是我们反编译出的代理类。
import guo.ping.aop.jdk.User;
import guo.ping.aop.jdk.UserDao;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy4 extends Proxy implements UserDao {
private static Method m1;
private static Method m3;
private static Method m4;
private static Method m2;
private static Method m0;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("guo.ping.aop.jdk.UserDao").getMethod("saveUser", Class.forName("guo.ping.aop.jdk.User"));
m4 = Class.forName("guo.ping.aop.jdk.UserDao").getMethod("getUserByName", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public $Proxy4(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int saveUser(User var1) throws {
try {
return (Integer)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final User getUserByName(String var1) throws {
try {
return (User)super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
首先代理类继承了 Proxy
实现了我们定义的 UserDao
接口,这也是JDK动态代理只能代理接口的原因,不能多继承。代理类中静态代码块中有5个方法,m3 和 m4是我们目标接口中定义的方法。代理类重新实现了我们的目标接口中的方法,当我们调用代理类中方法时,实质都是在调用 super.h.invoke()
方法,那么 super.h
是什么呢?我们查看JDK的 Proxy
类中的 h。这样就清晰了,实际上调用 super.h.invoke()
方法就是调用我们自己实现的 UserDaoInvocationHandler
中的 invoke()
方法。
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
}
我们再来看看CGLIB的实现原理如何,再推荐这篇文章。
CGLIB的动态代理使用相对简单一些,毕竟它不要求目标类实现接口。那么我们直接创建一个方法拦截器,类似于JDK中的 InvocationHandler
中的 invoke()
方法定义增强行为。
/**
* @description: 利用CGlib动态生成字节码文件
*/
public class UserDaoMethodInterceptor implements MethodInterceptor {
/**
*
* @param o 动态生成的代理类的实例,就是UserDaoImpl$$EnhancerByCGLIB$$d820d840的实例
* @param method 对应的method方法
* @param args method需要的参数
* @param methodProxy 增强的代理对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println(">>>>>>开启事务<<<<<<");
Object proxyObject = methodProxy.invokeSuper(o, args);
System.out.println(">>>>>>关闭事务<<<<<<");
return proxyObject;
}
}
测试方法与测试结果:
@Test
public void testUserDaoGlibProxy() {
// 该设置用于输出cglib动态代理产生的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "你自己的保存路径");
UserDao userDao = new UserDaoImpl();
System.out.println("========普通对象调用方法========");
userDao.getUserByName("小明");
userDao.saveUser(new User("小花", "[email protected]"));
System.out.println("========代理对象增强方法========");
Enhancer enhancer = new Enhancer();
// 继承被代理类
enhancer.setSuperclass(UserDaoImpl.class);
// 设置回调
enhancer.setCallback(new UserDaoMethodInterceptor());
// 创建代理类实例
UserDao proxy = (UserDao) enhancer.create();
// 创建代理对象
proxy.getUserByName("小明");
proxy.saveUser(new User("小花", "[email protected]"));
}
我们假设你跟着推荐文章的分析逻辑在走,那么这里我们得到的反编译代理类内容如下:
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class UserDaoImpl$$EnhancerByCGLIB$$d820d840 extends UserDaoImpl implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$getUserByName$0$Method;
private static final MethodProxy CGLIB$getUserByName$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$saveUser$1$Method;
private static final MethodProxy CGLIB$saveUser$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static {
CGLIB$STATICHOOK1();
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("guo.ping.aop.jdk.UserDaoImpl$$EnhancerByCGLIB$$d820d840");
Class var1;
Method[] var10000 = ReflectUtils.findMethods(new String[]{"getUserByName", "(Ljava/lang/String;)Lguo/ping/aop/jdk/User;", "saveUser", "(Lguo/ping/aop/jdk/User;)I"}, (var1 = Class.forName("guo.ping.aop.jdk.UserDaoImpl")).getDeclaredMethods());
CGLIB$getUserByName$0$Method = var10000[0];
CGLIB$getUserByName$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)Lguo/ping/aop/jdk/User;", "getUserByName", "CGLIB$getUserByName$0");
CGLIB$saveUser$1$Method = var10000[1];
CGLIB$saveUser$1$Proxy = MethodProxy.create(var1, var0, "(Lguo/ping/aop/jdk/User;)I", "saveUser", "CGLIB$saveUser$1");
var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$equals$2$Method = var10000[0];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[1];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[2];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[3];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
final User CGLIB$getUserByName$0(String var1) {
return super.getUserByName(var1);
}
public final User getUserByName(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (User)var10000.intercept(this, CGLIB$getUserByName$0$Method, new Object[]{var1}, CGLIB$getUserByName$0$Proxy) : super.getUserByName(var1);
}
final int CGLIB$saveUser$1(User var1) {
return super.saveUser(var1);
}
public final int saveUser(User var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$saveUser$1$Method, new Object[]{var1}, CGLIB$saveUser$1$Proxy);
return var2 == null ? 0 : ((Number)var2).intValue();
} else {
return super.saveUser(var1);
}
}
final boolean CGLIB$equals$2(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$3() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();
}
final int CGLIB$hashCode$4() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$5() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();
}
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$5$Proxy;
}
break;
case -109645912:
if (var10000.equals("getUserByName(Ljava/lang/String;)Lguo/ping/aop/jdk/User;")) {
return CGLIB$getUserByName$0$Proxy;
}
break;
case 839774274:
if (var10000.equals("saveUser(Lguo/ping/aop/jdk/User;)I")) {
return CGLIB$saveUser$1$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$2$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$3$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
public UserDaoImpl$$EnhancerByCGLIB$$d820d840() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
UserDaoImpl$$EnhancerByCGLIB$$d820d840 var1 = (UserDaoImpl$$EnhancerByCGLIB$$d820d840)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
UserDaoImpl$$EnhancerByCGLIB$$d820d840 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$d820d840();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
UserDaoImpl$$EnhancerByCGLIB$$d820d840 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$d820d840();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
UserDaoImpl$$EnhancerByCGLIB$$d820d840 var10000 = new UserDaoImpl$$EnhancerByCGLIB$$d820d840;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
}
首先,代理类直接继承自 UserDaoImpl
类,这也就是为什么CGLIB动态代理没法对 final 修饰的类进行增强。动态代理类根据我们目标类的每一个方法都生成了2个代理方法,第一个代理方法直接调用父类(目标类)的方法,第二个方法也就是代理类真正调用的方法是经过封装的,他会去判断是否实现了 MethodInterceptor
的 intercept()
方法,如果实现了则会调用 intercept()
方法。例如:
final User CGLIB$getUserByName$0(String var1) {
// 直接调用目标类的getUserByName方法
return super.getUserByName(var1);
}
public final User getUserByName(String var1) {
// this.CGLIB$CALLBACK_0是通过setCallback方法传进来的,我们在测试代码中调用了enhancer.setCallback(new UserDaoMethodInterceptor())
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
// 如果MethodInterceptor不为空就执行其intercept方法,否则直接调用父类的
return var10000 != null ? (User)var10000.intercept(this, CGLIB$getUserByName$0$Method, new Object[]{var1}, CGLIB$getUserByName$0$Proxy) : super.getUserByName(var1);
}
c. JdkDynamicAopProxy
之前了解了JDK动态代理的原理,我们查看Spring AOP是如何利用它的。我们之前调用的是 createAopProxy()
方法,返回的是 AopProxy
实例,其实就是 new JdkDynamicAopProxy()
。不要忘记了,我们获取代理对象调用的是 createAopProxy().getProxy(classLoader)
方法,所以我们进入 JdkDynamicAopProxy
查看其 getProxy()
方法。
/**
* JDK动态代理获取代理增强对象
* @return
*/
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
/**
* JDK动态代理获取代理增强对象
* @return
*/
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
// 获取接口
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 该类实现了InvocationHandler接口,所以传this
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
可以看到熟悉 Proxy.newProxyInstance()
代码,其中 InvocationHandler
实现类参数传入的 this,因为 JdkDynamicAopProxy
同时也实现了 InvocationHandler
接口,我们不由自主的进入 invoke()
方法:
/**
* 实现InvocationHandler的invoke方法
* Implementation of {@code InvocationHandler.invoke}.
* <p>Callers will see exactly the exception thrown by the target,
* unless a hook method throws an exception.
*/
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
// 包含了原始类对象信息
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 对equals方法的处理
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
// 对hashcode方法的处理
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// Class类的isAssignableFrom(Class cls)方法:如果调用这个方法的class或接口 与 参数cls表示的类或接口相同,
// 或者是参数cls表示的类或接口的父类,则返回true。例如:
// System.out.println(ArrayList.class.isAssignableFrom(Object.class)); --> false
// System.out.println(Object.class.isAssignableFrom(ArrayList.class)); --> true
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// 有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理至ThreadLocal中
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
// 将代理类对象保存到ThreadLocal中
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);
// 获取当前方法的拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
// 如果没有发现任何拦截器那么直接调用切点方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// We need to create a method invocation...
// 将拦截器封装在ReflectiveMethodInvocation,以便于使用其proceed进行链接表用拦截器
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 执行拦截器链中每个拦截器的invoke方法
retVal = invocation.proceed();
}
// Massage return value if necessary.
// 返回结果
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
这一长串代码包含了对传入的 method 方法参数做了具体的逻辑判断与处理。首先,对于 equals()
、hashCode()
方法进行了单独的处理,然后利用 this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
方法获取当前 method 的拦截器链,这个拦截器链就是我们之前花很大力气获得的增强器还统一封装成 Advisors。
如果调用链为空,则直接调用原始的切点方法。AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse)
方法如下:
/**
* 通过反射直接调用切点方法,就是目标对象原来的方法
* Invoke the given target via reflection, as part of an AOP method invocation.
* @param target the target object
* @param method the method to invoke
* @param args the arguments for the method
* @return the invocation result, if any
* @throws Throwable if thrown by the target method
* @throws org.springframework.aop.AopInvocationException in case of a reflection error
*/
@Nullable
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
如果拦截器链不为空,就利用 ReflectiveMethodInvocation
对链进行封装,在 ReflectiveMethodInvocation
类中 proceed()
方法中实现了拦截器的逐一调用。先来看看 ReflectiveMethodInvocation
类:
查看其 proceed()
方法如何实现前置增强方法在目标方法之前执行、后置增强在目标方法之后执行。
/**
* 实现拦截器的逐一调用
* @return
* @throws Throwable
*/
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 执行完成所有增强方法后执行切点方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 获取下一个要执行的拦截器
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have been evaluated and found to match.
// 动态匹配
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed. Skip this interceptor and invoke the next in the chain.
// 不匹配不执行
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
/* 普通拦截器,直接调用拦截器,比如:ExposeInvocationInterceptor、DelegatePerTargetObjectIntroductionInterceptor、
MethodBeforeAdviceInterceptor、AspectJAroundAdvice、AspectJAfterAdvice */
// 将this作为参数传递以保证当前实例中调用链的执行
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
在 proceed()
方法中,代码逻辑没有想的那么复杂,ReflectiveMethodInvocation
维护了一个链接调用的计数器,记录着当前调用链接的位置,以便链可以有序的进行下去。在方法中并没有我们想的那样维护各种增强的顺序,而是将此工作分散给了各个具体的增强器,在各个具体的增强器的内部实现。我们以最开始的用例简单的分析一下。
调用链集合一共有四个元素,之前的图已经展示。我们先调用了第一个普通拦截器 ExposeInvocationInterceptor
的 invoke(this)
方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}
随后,再次调用 proceed()
方法,此时获取第二个拦截器 AspectJAfterAdvice
,执行 AspectJAfterAdvice
的 invoke(this)
方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
继续执行 proceed()
方法,执行第三个拦截器 AspectJAroundAdvice
的 invoke(this)
方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
JoinPointMatch jpm = getJoinPointMatch(pmi);
return invokeAdviceMethod(pjp, jpm, null, null);
}
对于 AspectJAroundAdvice
拦截器来说,不能继续像之前一样直接再次进入 proceed()
方法了,因为当调用到 AspectJAroundAdvice
拦截器时意味着目标方法肯定要执行了,所以之后经过大量的调用,最终执行了我们自定义的环绕通知方法。
@Around("test()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("before1");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("after1");
return o;
}
先打印出 before1,然后继续执行 proceed()
方法,调用下一个拦截器 MethodBeforeAdviceInterceptor
的 invoke(this)
方法:
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
这里直接执行了 before()
方法,执行了前置通知定义的方法,控制台打印出了 beforeTest,然后再次执行 proceed()
方法,此时所有的拦截器全部调用完,直接执行切点方法。
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 执行完成所有增强方法后执行切点方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
····
此时控制台打印出 test,然后,不断的返回,直到环绕通知中的 proceedingJoinPoint.proceed()
方法执行完毕,然后运行环绕通知的打印 after1。到此,环绕通知执行完毕,返回结果后继续执行后置增强。
@Around("test()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("before1");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("after1");
return o;
}
d. ObjenesisCglibAopProxy
同样,我们去查看 ObjenesisCglibAopProxy
类,它继承自 CglibAopProxy
类,查看其中的 getProxy()
方法,方法内实现了对 Enhancer
的创建及接口封装。
@Override
public Object getProxy() {
return getProxy(null);
}
/**
* 完成Enhancer的创建以及接口封装
* @param classLoader the class loader to create the proxy with
* (or {@code null} for the low-level proxy facility's default)
* @return
*/
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
Class<?> proxySuperClass = rootClass;
// 校验targetClass是否本身就是一个CGLIB代理类
if (ClassUtils.isCglibProxyClass(rootClass)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// Validate the class, writing log messages as necessary.
// 验证class
validateClassIfNecessary(proxySuperClass, classLoader);
// 创建及配置 CGLIB Enhancer...
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
// 设置拦截器链
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// fixedInterceptorMap only populated at this point, after getCallbacks call above
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 生成代理类以及创建代理
return createProxyClassAndInstance(enhancer, callbacks);
}
catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
": Common causes of this problem include using a final class or a non-visible class",
ex);
}
catch (Throwable ex) {
// TargetSource.getTarget() failed
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
Spring首先创建了 Enhancer
,进行了一些属性设置后通过 getCallbacks(rootClass)
进行拦截器链的设置:
/**
* 设置拦截器链
* @param rootClass
* @return
* @throws Exception
*/
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
// 对于expose-proxy属性的处理
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
// 将拦截器封装在DynamicAdvisedInterceptor中,DynamicAdvisedInterceptor继承自MethodInterceptor,
// 后面加入callback后,在再次调用代理时会直接调用DynamicAdvisedInterceptor的intercept方法
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor;
if (exposeProxy) {
targetInterceptor = (isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
}
else {
targetInterceptor = (isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
}
// Choose a "direct to target" dispatcher (used for
// unadvised calls to static targets that cannot return this).
Callback targetDispatcher = (isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
Callback[] mainCallbacks = new Callback[] {
// 将拦截器链加入Callback中
aopInterceptor, // for normal advice
targetInterceptor, // invoke target without considering advice, if optimized
new SerializableNoOp(), // no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
// If the target is a static one and the advice chain is frozen,
// then we can make some optimizations by sending the AOP calls
// direct to the target using the fixed chain for that method.
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<>(methods.length);
// TODO: small memory optimization here (can skip creation for methods with no advice)
for (int x = 0; x < methods.length; x++) {
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
}
// Now copy both the callbacks from mainCallbacks
// and fixedCallbacks into the callbacks array.
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
this.fixedInterceptorOffset = mainCallbacks.length;
}
else {
callbacks = mainCallbacks;
}
return callbacks;
}
在 getCallback()
中Spring考虑了很多情况,但是对于我们来说,只需要理解最常用的就可以了,比如将 advised 属性封装在 DynamicAdvisedInterceptor
并加入在 callbacks 中,这么做的目的是什么呢,如何调用呢?在前面的CGLIB示例中,可以看到CGLIB中对于方法的拦截是通过将自定义的拦截器(实现 MethodInterceptor
接口)加入到 Callback
中并在调用代理时直接激活拦截器中的 intercept()
方法来实现的,那么在 getCallback()
中正是实现了这样一个目的。DynamicAdvisedlnterceptor
继承自 MethodInterceptor
,加人 Callback
中后,在再次调用代理时会直接调用 DynamicAdvisedInterceptor
中的 intercept()
方法,由此推断,对于CGLIB方式实现的代理,其核心逻辑必然在 DynamicAdvisedInterceptor
中的 intercept()
方法中。
/**
* DynamicAdvisedInterceptor重写了intercept方法,与JDK动态代理的invoke类似,首先构造链,然后封装此链进行串联调用。
* JDK封装链是ReflectiveMethodInvocation,而在CGlib是用CglibMethodInvocation,
* CglibMethodInvocation继承了ReflectiveMethodInvocation,但没有重写proceed()方法
* @param proxy
* @param method
* @param args
* @param methodProxy
* @return
* @throws Throwable
*/
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
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);
// 获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
// 如果拦截器链为空则直接激活原方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
// 进入链
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
上述的实现与JDK方式实现代理中的 invoke()
方法大同小异,都是首先构造链,然后封装此链进行串联调用,稍有些区别就是在JDK中直接构造 ReflectiveMethodInvocation
,而在CGLIB中使用 CglibMethodInvocation
。CglibMethodInvocation
继承自 ReflectiveMethodInvocation
,但是 proceed()
方法并没有重写。
IV. 静态AOP使用示例
在Java中,从织入切面的方式上来看,存在三种织入方式:编译期织入、类加载期织入和运行期织入。
- 编译期织入:是指在Java编译期,采用特殊的编译器,将切面织入到Java类中。例如,AspectJ 利用 ajc 编译器进行编译器织入切面;
- 类加载期织入:指通过特殊的类加载器,在类字节码加载到JVM时,织入切面,也称为 LWT (Load Time Weaving )。AspectJ LWT这一部分我们将重点研究。
- 运行时织入:采用CGLib工具或JDK动态代理进行切面的织入。就是之前我们大幅笔墨叙述的。
Spring对于AspectJ LWT有着更细粒度的控制,我们简单示例一下如何使用静态AOP。
Spring配置LWT
在Spring配置文件中加入 LWT 开关,写入 <context:load-time-weaver />。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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">
<aop:aspectj-autoproxy />
<bean id="test" class="guo.ping.aop.ltw.TestLTWBean" />
<bean class="guo.ping.aop.ltw.AspectJLTWTest" />
<context:load-time-weaver />
</beans>
加入aop.xml
在 resources/META-INF/ 目录下新建 aop.xml,内容如下。主要是告诉AspectJ需要对哪个包进行织入,并使用哪些增强器。
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
<weaver>
<!--only weave classes in our application-specific packages-->
<include within="guo.ping.aop.ltw.*" />
</weaver>
<aspects>
<!--weave in just this aspect-->
<aspect name="guo.ping.aop.ltw.AspectJLTWTest" />
</aspects>
</aspectj>
创建Advisor和目标类
Advisor切点以及通知的定义。
package guo.ping.aop.ltw;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class AspectJLTWTest {
@Pointcut("execution(* guo.ping.aop.ltw.TestLTWBean.test(..))")
public void test() {
}
@Before("test()")
public void beforeTest() {
System.out.println("beforeTest");
}
@After("test()")
public void afterTest() {
System.out.println("afterTest");
}
@Around("test()")
public Object aroundTest(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("before1");
Object o = null;
try {
o = proceedingJoinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("after1");
return o;
}
}
目标 bean 定义。
package guo.ping.aop.ltw;
public class TestLTWBean {
private String testStr = "testStr";
public String getTestStr() {
return testStr;
}
public void setTestStr(String testStr) {
this.testStr = testStr;
}
public void test() {
System.out.println("test");
}
}
配置JVM启动参数
需要配置JVM启动参数,-javaagent:e:\spring_src\spring-framework\spring-mytest\file\spring-instrument-5.1.0.RELEASE.jar,依赖的 spring-instrument jar包需要额外下载。
测试
编写测试函数。
@Test
public void testAopLTWDemo() {
ApplicationContext context = new ClassPathXmlApplicationContext("aopDemoStatic-Test.xml");
TestLTWBean testBean = (TestLTWBean) context.getBean("test");
testBean.test();
}
测试结果:
V. 创建AOP静态代理
AOP的静态代理主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强,它与动态代理相比具有更高的效率,因为在动态代理调用的过程中,还需要一个动态创建代理类并代理目标对象的步骤,而静态代理则是在启动时便完成了字节码增强,当系统再次调用目标类时与调用正常的类并无差别,所以在效率上会相对高些。
Instrumentation使用
Java在1.5引入 java.lang.instrument,由此我们可以实现一个Java agent,通过此 agent 来修改类的字节码即改变一个类。本节通过 Java Instrument 实现一个简单的 profiler。当然 insrument 并不限于 profiler,它可以做很多事情,类似于一种更加低级、更松耦合的AOP,可以从底层来改变一个类的行为。
下面我们实现一个自动监控方法执行时间的案例。正常情况想要统计一个方法执行时间,你需要在方法执行前和结束后加上计时然后计算得出,我们不得不在每一个想监控时间的方法中有侵入的加上相同的计时代码。可以使用 AOP来做这件事,但是总感觉别扭,这种 profiler 的代码还是要打包在项目中。但是Java Instrument 使得这一切更干净。
① 创建案例工程
我们创建一个 maven 工程,先贴上我们的 pom 文件。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>guo.ping</groupId>
<artifactId>instrument-learn</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>guo.ping.instrument.PerfMonAgent</Premain-Class>
<Boot-Class-Path>e:/maven_repo/org/javassist/javassist/3.20.0-GA/javassist-3.23.1-GA.jar</Boot-Class-Path>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
② 编写ClassFileTransformer类
public class PerfMonXformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] transformed = null;
System.out.println("Transforming " + className + " ....");
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = null;
try {
ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
if (!ctClass.isInterface() && ctClass.getName().startsWith("guo.ping")) {
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
if (!method.isEmpty()) {
// 修改method字节码
doMethod(method);
}
}
transformed = ctClass.toBytecode();
}
} catch (IOException e) {
// ommit
} catch (CannotCompileException e) {
// ommit
} finally {
if (ctClass != null) {
ctClass.detach();
}
}
return transformed;
}
private void doMethod(CtMethod method) throws CannotCompileException {
method.addLocalVariable("time", CtClass.longType);
method.insertBefore("long time = System.nanoTime();");
method.insertAfter("System.out.println(\"run " + method.getName() + "() method use time: \" + (System.nanoTime() - time) / 1000);");
}
}
③ 编写agent类
public class PerfMonAgent {
private static Instrumentation instrumentation = null;
public static void premain(String agentArgs, Instrumentation _inst) {
System.out.println("PrefMonAgent.premain() was called.");
instrumentation = _inst;
ClassFileTransformer transformer = new PerfMonXformer();
System.out.println("Adding a PerfMonXformer instance to the JVM.");
instrumentation.addTransformer(transformer);
}
}
上面两个类就是 agent 的核心了,JVM启动时在应用加载前会调用 PerfMonAgent
类中的 premain()
方法, 然后 premain()
方法中实例化了一个定制的 ClassFileTransformer
,即 PerfMonXformer
并通过 instrumentation.addTransformer(transformer)
把 PerfMonXformer
的实例加人 instrumentation
实例(instrumentation
实例由 JVM 传人),这就使得应用中的类加载时,PerfMonXformer
中的 transform()
方法都会被调用,所以我们在此方法中可以改变加载的类。可以使用 JBoss的 Javassist 来改变类的字节码,在上面的方法中通过改变类的字节码,在每个类的方法入口中加入了 long time= System. nanoTime()
在方法的出口加入了 System.out.println("run " + method.getName() + "() method use time: " + (System.nanoTime() - time) / 1000);
方法。需要注意的是,一定要添加一个 long 类型的 time 变量,书上包括很多资料都没有添加,导致报错。
④ 打包agent
打包 agent 时,需要配置 maven 的 build 配置。
- Jar的 META-INF/MAINFEST.MF 加入 Premain-class:xx,xx 在此语境中就是我们的 agent 类,即 guo.ping.instrument.PerfMonAgent;
- 如果编写的 Agent 类引入了第三方包,需要使用 Boot-Class-Path:xx,xx 在语境中就是之前提到的 JBoss 的 javassist 所在路径,我的是 C:\Users\ccoder.m2\repository\org\javassist\javassist\3.23.1-GA\javassist-3.23.1-GA.jar。
利用 maven 插件进行编译打包。
⑤ 测试
编写测试函数:
public class Test {
public static void main(String[] args) {
new Test().test();
}
private void test() {
System.out.println("Hello World!");
}
}
在JVM启动参数中设置:-javaagent:E:\IDEA\instrument-learn\instrument-learn\file\agent.jar。Java通过此选项加载 agent,由 agent 来监控 classpath 下的应用。
运行结果:
通过案例可以感受到Spring中的静态AOP直接使用了AspectJ提供的方法,而AspectJ又是在 Instrument 基础上进行的封装。就上面的例子来看,至少在AspectJ中会有如下功能。
- 读取 META-INF/aop.xml;
- 将 aop.xml 中定义的增强器通过自定义的 ClassFileTransformer 织入对应的类中。
那么Spring具体怎么委托 AspectJ 去帮忙完成这些的呢?下面具体研究一下。
自定义标签解析
想要使用静态AOP,配置文件中必须加入 <context:load-time-weaver />。通过之前的经验,我们轻易的可以摸清方向,先找到 load-time-weaver 命名空间对应的 NameSpaceHandler
。然后查看其对应的解析类中 parse()
方法。
利用全局搜索定位到 ContextNamespaceHandler
:
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
// 注册类加载器静态代理LWT的自定义标签解析
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
进入 LoadTimeWeaverBeanDefinitionParser
类中 doParse()
方法。
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 判断aspectj-weaving的属性是on、off还是autodetect
if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
// 是否存在org.springframework.context.config.internalAspectJWeavingEnabler名称的beanDefinition
if (!parserContext.getRegistry().containsBeanDefinition(ASPECTJ_WEAVING_ENABLER_BEAN_NAME)) {
// 不存在就新建并注册,bean的class为org.springframework.context.weaving.AspectJWeavingEnabler
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);
}
}
}
有了之前分析动态AOP自定义标签的经验之后,我们知道程序的目的就是在容器中创建一个 beanDefinition,这个 bean 的 class 为 org.springframework.context.weaving.AspectJWeavingEnabler
,名称为 org.springframework.context.config.internalAspectJWeavingEnabler
。其创建流程如下。
① 是否开启AspectJ
之前反复提及在配置文件中配置 <context:load-time-weaver /> 便相当于加入 AspectJ 开关。但是,并不是配置了该标签就一定会开启 AspectJ 功能,这个标签还有一个属性 aspectj-weaving,有三种备选值,on、off 以及 autodetect,默认为 autodetect。所以,仅配置了 <context:load-time-weaver /> 只是让Spring去检测是否可以使用 AspectJ 功能,检测的依据是调用 isAspectJWeavingEnabled()
方法查看 classpath/META-INF/ 目录下是否存在 aop.xml 文件。
/**
* 判断AspectJ织入是否enable。默认为autodetect,检测依据为META-INF/aop.xml(固定写死了)文件是否存在
* @param value
* @param parserContext
* @return
*/
protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
if ("on".equals(value)) {
return true;
}
else if ("off".equals(value)) {
return false;
}
else {
// 检查是否存在META-INF/aop.xml文件
ClassLoader cl = parserContext.getReaderContext().getBeanClassLoader();
return (cl != null && cl.getResource(AspectJWeavingEnabler.ASPECTJ_AOP_XML_RESOURCE) != null);
}
}
② 创建beanDefinition
当通过 AspectJ 功能验证后便可以进行 AspectJWeavingEnabler
的注册了,注册的方式很简单,无非是将全限定类名注册在新初始化的 RootBeanDefinition
中,在 RootBeanDefinition
的获取时会转换成对应的 class。
尽管在 dopParse()
方法中注册了 AspectJWeavingEnabler
,但是对于标签本身 Spring也会以 bean 的形式保存,也就是当Spring解析到 <context:load-time-weaver /> 标签的时候也会产生一个 bean 注册到容器中。而这个 bean 中的信息是什么呢?
在 LoadTimeWeaverBeanDefinitionParser
类中还有一个方法。
@Override
protected String getBeanClassName(Element element) {
// WEAVER_CLASS_ATTRIBUTE=weaver-class
if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
}
// org.springframework.context.weaving.DefaultContextLoadTimeWeaver
return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
}
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
// 只有有了loadTimeWeaver这个bean,才会激活AspectJ
return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;
}
凭借以上信息,我们可以推断这个 bean 的 id 通过 resolveId()
方法获取到为 loadTimeWeaver,class 的全限定类名为 org.springframework.context.weaving.DefaultContextLoadTimeWeaver
,这一过程相当于完成了对 DefaultContextLoadTimeWeaver
的注册。
完成上面这个 bean 注册,不代表就已经完成了,能够使用上AspectJ了,我们还有一个重要的步骤没有做。还记得上一篇文章的第VI部分对 beanFactory 的扩展中,有一段对AspectJ的支持代码,截取如下。
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 增加对AspectJ的支持
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
如果容器中存在 loadTimeWeaver
这个 bean,那么就会添加一个 bean 的后处理器 LoadTimeWeaverAwareProcessor
。在 AbstractApplicationContext
中的 prepareBeanFactory()
函数是在容器初始化时候调用的,也就是说只有注册了 LoadTimeWeaverAwareProcessor
才会激活整个 AspectJ 的功能。
织入
当我们开启了AspectJ功能后便可以开始进行织入功能的分析了,首先肯定是去查看这个 bean 的后处理器 LoadTimeWeaverAwareProcessor
的功能,进入其关键的重写后处理器的方法。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 仅仅对LoadTimeWeaverAware类型的bean进行处理
if (bean instanceof LoadTimeWeaverAware) {
LoadTimeWeaver ltw = this.loadTimeWeaver;
if (ltw == null) {
Assert.state(this.beanFactory != null,
"BeanFactory required if no LoadTimeWeaver explicitly specified");
// 获取LoadTimeWeaver实例并赋属性给目标bean
ltw = this.beanFactory.getBean(
ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
}
((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String name) {
return bean;
}
在 LoadTimeWeaverAwareProcessor
中的 postProcessBeforeInitialization()
方法中,因为最开始的 if 判断注定这个后处理器只对 LoadTimeWeaverAware
类型的 bean 起作用。查看实现 LoadTimeWeaverAware
接口的类。
我们看到认识的就是之前准备工作 doParse()
中注册的 AspectJWeavingEnabler
。当在Spring中调用 AspectWeavingEnabler
时,this.loadTimeWeaver
尚未被初始化,那么会直接调用 this.beanFactory.getBean()
方法获取对应的 DefaultContextLoadTimeWeaver
类型的 bean,并将其设置为 AspectJWeavingEnabler
类型 bean 的 loadTimeWeaver
属性中。
当然 AspectJWeavingEnabler
同样实现了 BeanClassLoaderAware
以及 Ordered
接口,实现 BeanClassLoaderAware
接口保证了在 bean 初始化的时候调用 AbstractAutowireCapableBeanFactory
的 invokeAwareMethods
的时候将 beanClassloader
赋值给当前类。而实现 Ordered
接口则保证在实例化 bean 时当前 bean 会被最先初始化。
/**
* 对三种Aware调用了setter方法,给bean传入对应的东西
* @param beanName
* @param bean
*/
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
而 DefaultContextLoadTimeWeaver
类又同时实现了 LoadTimeWeaver
、BeanClassLoaderAware
以及 DisposableBean
。其中 DisposableBean
接口保证在 bean 销毁时会调用 destroy 方法进行 bean 的清理,而 BeanClassLoaderAware
接口则保证在 bean 的初始化调用 AbstractAutowireCapableBeanFactory
的 invokeAwareMethods()
时调用 setBeanClassLoader()
方法。
/**
* 重写设置classLoader方法
* @param classLoader the owning class loader
*/
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);
if (serverSpecificLoadTimeWeaver != null) {
if (logger.isDebugEnabled()) {
logger.debug("Determined server-specific load-time weaver: " +
serverSpecificLoadTimeWeaver.getClass().getName());
}
this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
}
else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
// 检查当前虚拟机中的Instrumentation实例是否可用
logger.debug("Found Spring's JVM agent for instrumentation");
// 实例化一个InstrumentationLoadTimeWeaver对象,此外对其instrumentation属性做了初始化
this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
}
else {
try {
this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Using reflective load-time weaver for class loader: " +
this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
}
}
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");
}
}
}
上面的函数 this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader)
代码非常关键且容易忽略,其不仅仅实例化了一个 InstrumentationLoadTimeWeaver
实例,而且在实例化过程中还做了一些额外的操作。
/**
* Create a new InstrumentationLoadTimeWeaver for the given ClassLoader.
* @param classLoader the ClassLoader that registered transformers are supposed to apply to
*/
public InstrumentationLoadTimeWeaver(@Nullable ClassLoader classLoader) {
this.classLoader = classLoader;
this.instrumentation = getInstrumentation();
}
在实例化的过程中会对当前的 this.instrumentation
属性进行初始化,而初始化通过 getInstrumentation()
获得,也就是说在 InstrumentationLoadTimeWeaver
实例化后其属性 instrumentation
已经被初始化完代表着当前虚拟机的实例了。综合之前的例子,对于注册转换器,如 addTransform()
函数等,便可以直接使用此属性进行操作了。
经过一大堆各种 bean 的注册赋值逻辑,我们来总结一下。
- 首先,配置了 <context:load-time-weaver /> 标签,就会注册有一个
DefaultContextLoadTimeWeaver
类型的bean; - 其次,如果确认开启AspectJ功能,则会注册一个
AspectJWeavingEnabler
类型的bean; - 然后想要完整启用AspectJ功能,需要再注册
LoadTimeWeaverAwareProcessor
类型的bean;
其实,我们来来回回设计到的 bean 主要就是这三个,AspectJWeavingEnabler
中的 loadTimeWeaver
属性被初始化为 DefaultContextLoadTimeWeaver
类型的 bean;DefaultContextLoadTimeWeaver
中的 loadTimeWeaver
属性被初始化为 InstrumentationLoadTimeWeaver
。
因为 AspectJWeavingEnabler
类同样实现了 BeanFactoryPostProcessor
,所以当所有 beanDefinition 解析结束后会调用其
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
enableAspectJWeaving(this.loadTimeWeaver, this.beanClassLoader);
}
/**
* 开启AspectJ LWT织入功能
* Enable AspectJ weaving with the given {@link LoadTimeWeaver}.
* @param weaverToUse the LoadTimeWeaver to apply to (or {@code null} for a default weaver)
* @param beanClassLoader the class loader to create a default weaver for (if necessary)
*/
public static void enableAspectJWeaving(
@Nullable LoadTimeWeaver weaverToUse, @Nullable ClassLoader beanClassLoader) {
// 此时已经被初始化为DefaultContextLoadTimeWeaver
if (weaverToUse == null) {
if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
weaverToUse = new InstrumentationLoadTimeWeaver(beanClassLoader);
}
else {
throw new IllegalStateException("No LoadTimeWeaver available");
}
}
// 使用DefaultContextLoadTimeWeaver类型的bean中的loadTimeWeaver属性注册转换器
weaverToUse.addTransformer(
new AspectJClassBypassingClassFileTransformer(new ClassPreProcessorAgentAdapter()));
}
AspectJClassBypassingClassFileTransformer
的作用仅仅是告诉 AspectJ 以 org.aspectj 或者 org/aspectj 开头的类不进行处理。
/**
* 告诉 AspectJ 以 org.aspectj 或者 org/aspectj 开头的类不进行处理
* ClassFileTransformer decorator that suppresses processing of AspectJ
* classes in order to avoid potential LinkageErrors.
* @see org.springframework.context.annotation.LoadTimeWeavingConfiguration
*/
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);
}
}
VI. 总结
Spring AOP主要基于AspectJ,又分为动态代理和静态代理(LWT),本文分别对两种方式进行了简要的分析,说实话大部分步骤也算过了一遍,但很多细节还是值得继续深思,好好debug的,有时间再回来继续研究吧。
又过去了接近半个月,日子过得越来越快。Spring的IOC与AOP总算结束了,下面继续看一些应用部分的了。