aop是spring中非常有趣的一个功能。如果应用得当会大有用处。现在从源码角度分析一下
Spring aop的实现原理。
还是从上篇中提到的
<aop:config>
<aop:advisor>....</aop:advisor>
....
</aop:config>
这些配置信息的解析入手。spring中aop namespace的handler是AopNamespaceHandler。
其初始化代码如下(init方法调用的时机在上一篇分析事务时已经分析过了):
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());
}
可以看到,aop:config标签的解析类是:ConfigBeanDefinitionParser(解析类的调用时机在上一篇分析事务时也介绍过),其parse方法如下:
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
NodeList childNodes = element.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node node = childNodes.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
String localName = parserContext.getDelegate().getLocalName(node);
if (POINTCUT.equals(localName)) {
parsePointcut((Element) node, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor((Element) node, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect((Element) node, parserContext);
}
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
它的功能大致有两块:1 . 注册一个AspectJAwareAdvisorAutoProxyCreator类型的bean。 2. 解析主标签下面的
advisor标签,并且注册advisor.
下面逐一分析一下这两个方面。
1. AspectJAwareAdvisorAutoProxyCreator 类型bean 的注册。
稍微从
configureAutoProxyCreator(parserContext, element);
这一句跟踪一下很容易就发现最后到了AopConfigUtils的如下方法:
private static BeanDefinition registerOrEscalateApcAsRequired(Class cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
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;
}
注意参数里面的cls是AspectJAwareAdvisorAutoProxyCreator.class,这个是在前面把调用委托过来的时候直接写死的。这段代码注册了一个名为AUTO_PROXY_CREATOR_BEAN_NAME(org.springframework.aop.config.internalAutoProxyCreator)的bean。bean的类型是AspectJAwareAdvisorAutoProxyCreator。
AspectJAwareAdvisorAutoProxyCreator到底有什么玄机呢?看了一下继承关系,赫然发现实现了InstantiationAwareBeanPostProcessor接口!这个接口是BeanPostProcessor的一个子类,在bean初始化的时候
调用。此接口的实现在AbstractAutoProxyCreator中(其他与本次分析无关的方法的实现在此没有列出):
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!this.targetSourcedBeans.contains(cacheKey)) {
if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.nonAdvisedBeans.add(cacheKey);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
这段代码的作用就是每当bean初始化前,检查是否需要生成代理对象。如果需要,就生成代理。
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
上面这段代码负责找到与当前bean相关联的Advisor(s).感兴趣的朋友可以继续追踪一下代码了解细节。大致思路就是
先找到所有实现了Advisor接口的bean,然后根据配置文件中的advisor配置从中挑出能cut到当前bean的advisor.
接下来就是生成proxy了。代码如下:
protected Object createProxy(
Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
proxyFactory.copyFrom(this);
if (!shouldProxyTargetClass(beanClass, beanName)) {
// Must allow for introductions; can't just set interfaces to
// the target's interfaces only.
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);
for (Class<?> targetInterface : targetInterfaces) {
proxyFactory.addInterface(targetInterface);
}
}
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
for (Advisor advisor : advisors) {
proxyFactory.addAdvisor(advisor);
}
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(this.proxyClassLoader);
}
通过读这段代码,也就明白了当在配置bean的时候proxyTargetClass属性时如果需要生成代理使用cglib的原因了。
因为如果配置了这个属性,那么生成代理的时候会掠过对bean接口的解析,从而只能使用cglib代理。
OK.第一个方面分析基本完成了。
下面分析一下文章开头提到的第二个方面:
aop:advisor的解析。
这个解析是在ConfigBeanDefinitionParse里面完成的:
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
String id = advisorElement.getAttribute(ID);
try {
this.parseState.push(new AdvisorEntry(id));
String advisorBeanName = id;
if (StringUtils.hasText(advisorBeanName)) {
parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
}
else {
advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
}
Object pointcut = parsePointcutProperty(advisorElement, parserContext);
if (pointcut instanceof BeanDefinition) {
advisorDef.getPropertyValues().add(POINTCUT, pointcut);
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
}
else if (pointcut instanceof String) {
advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef));
}
}
finally {
this.parseState.pop();
}
}
这段代码实际上就是生成了一个beanclass 为DefaultBeanFactoryPointcutAdvisor的advisor.
我们在分析第一个方面的时候,曾经提到过bean初始化前会调用
postProcessBeforeInstantiation
而这个方法会找到所有跟需要实例化的bean关联的advisor,然后产生proxy.
OK.终于明白了Advisor是如何产生作用的了!
Spring AOP的原理大致如下:
配置一个实现了InstantiationAwareBeanPostProcessor接口的bean。在每次bean初始化的时候找到所有advisor,根据pointcut 判断是不是需要为将实例化的bean生成代理,如果需要,就把advice编制在代理对象里面。
分享到:
相关推荐
《Spring AOP 源码分析》 在深入探讨Spring AOP之前,我们先要理解AOP(面向切面编程)的基本概念。AOP是一种编程范式,它将关注点分离,使得我们可以将横切关注点(如日志、事务管理、安全检查等)与业务逻辑解耦...
《Spring源码分析——BeanFactory》 在Java的IoC(Inversion of Control)和DI(Dependency Injection)领域,Spring框架扮演着至关重要的角色。BeanFactory是Spring的核心组件之一,它是容器的基石,负责管理应用...
《Spring源码分析——ApplicationContext》 在Java世界中,Spring框架是不可或缺的一部分,它以其强大的IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented Programming,面向切面编程)特性,极大地...
分析Spring源码有助于深入理解其动态代理的工作原理。例如,可以查看`org.springframework.aop.framework.JdkDynamicAopProxy`和`org.springframework.aop.framework.CglibAopProxy`这两个类,它们分别实现了JDK和...
在深入理解 Spring AOP 的源码时,需要熟悉 Spring IoC 的工作原理,以及 AOP 相关的概念,如切点表达式、通知类型等。了解这些基础知识可以帮助我们更好地掌握 Spring AOP 的实现细节。在分析源码时,可以参考作者...
总的来说,《Spring5 源码分析(第 2 版)》这本书不仅涵盖了Spring5的主要特性和组件,还深入探讨了其实现原理,是Java开发者深入理解Spring框架、提升技术水平的宝贵资源。通过对书中源码的阅读和理解,读者可以更好...
在源码分析的过程中,读者会深入理解Spring的内部工作机制,例如如何解析配置、如何创建bean实例、如何实现AOP代理等。这将有助于开发者编写更高效、更健壮的代码,也能为参与Spring的扩展或定制打下坚实基础。 总...
《Spring5 源码分析(第 2 版)》是某Tom老师精心编写的深度解析文档,旨在帮助读者全面理解Spring5的核心机制和设计理念。Spring作为Java领域最为广泛应用的框架之一,其源码的深入理解对于开发者来说至关重要。这篇...
解决这些问题通常需要对Spring AOP的原理有深入理解,同时也需要熟悉Struts2的工作方式。查看日志,使用调试工具,以及查阅官方文档都是定位和解决问题的有效方法。 最后,对于给出的文件名"test",可能是测试代码...
Spring作为Java领域最广泛应用的框架之一,其核心组件包括依赖注入(Dependency Injection,DI)、AOP(面向切面编程)、资源管理、事件处理以及bean的生命周期管理等。这些组件构成了Spring应用程序的基础,使得...
本压缩包“Spring源码解析”提供了对Spring框架核心组件——IOC(Inversion of Control,控制反转)、AOP(Aspect Oriented Programming,面向切面编程)以及Transaction(事务管理)的源码分析,帮助开发者更全面地...
### Spring 源码分析——设计模式篇 #### 一、引言 Spring框架作为Java企业级开发领域中不可或缺的一部分,其内部集成了多种设计模式,不仅有助于提高系统的可维护性和扩展性,还能够帮助开发者更好地理解和应用...
7. **源码分析** 对于深入理解Spring Security的工作原理,查看源码是十分有帮助的。例如,`AccessDecisionManager`的实现类,如`AffirmativeBased`和`UnanimousBased`,以及`AccessDecisionVoter`接口,它们共同...
总的来说,《Spring高级源码分析》将带你深入Spring的各个模块,从IoC容器到AOP,从MVC到Data访问,再到Boot的自动化配置。通过对源码的深度探索,你将能够更好地理解和利用Spring框架,提升自己的开发技能。
《Spring框架源码分析——基于UML图的解读》 在深入探讨Spring框架源码之前,我们首先要理解什么是UML(统一建模语言)。UML是一种标准的图形化建模语言,用于软件设计和系统分析,它通过图表来表示系统的结构、...
在本篇博文中,我们将深入探讨Spring AOP(面向切面编程)中的一个重要概念——AfterReturning通知,这是Spring框架提供的一种强大的功能,允许我们在方法成功执行并返回后执行额外的操作。我们将通过源码分析和实际...
本篇文章将深入探讨如何使用Spring AOP实现性能监控器,并通过源码分析来理解其工作原理。 首先,我们要了解AOP的核心概念——切面(Aspect)、通知(Advice)、连接点(Join Point)、切入点(Pointcut)和织入...
《Spring IOC源码解析(一)——整体介绍》 在深入理解Spring框架的过程中,源码分析是不可或缺的一环。本文将对Spring的IOC(Inversion of Control,控制反转)...希望本文能为你开启Spring源码探索之旅的第一步。
6. **Spring源码分析**:通过阅读源码,理解Spring框架的工作原理,增强对框架的深入理解。 接下来,我们转向MyBatis,这是一个轻量级的持久层框架,它提供了灵活的SQL映射机制,使得数据库操作变得简单。"MyBatis3...
在本资源中,我们提供了可以直接导入Eclipse的Spring源码的第五部分,版本为4.3.18。这一版本的Spring包含了众多改进和修复,对于理解Spring的工作原理以及进行自定义开发非常有帮助。 首先,让我们深入了解一下...