9. AOP源码深度剖析
概述
-
AOP(Aspect Orient Programming):面向切面编程;
-
用途:用于系统中的横切关注点,比如日志管理,事务管理;
-
实现:利用代理模式,通过代理对象对被代理的对象增加功能。
所以,关键在于AOP框架自动创建AOP代理对象,代理模式分为静态代理和动态代理;
-
框架: AspectJ使用静态代理,编译时增强,在编译期生成代理对象;
SpringAOP使用动态代理,运行时增强,在运行时,动态生成代理对象;
Spring AOP的前世今生
目前 Spring AOP 一共有三种配置方式,Spring 做到了很好地向下兼容,所以可以放心使用。
- Spring 1.2 基于接口的配置:最早的 Spring AOP 是完全基于几个接口的
- Spring 2.0 schema-based 配置:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间
<aop />
- Spring 2.0 @AspectJ 配置:使用注解的方式来配置,这种方式感觉是最方便的,还有,这里虽然叫做
@AspectJ
,但是这个和 AspectJ 其实没啥关系。
要说明的是,这里介绍的 Spring AOP 是纯的 Spring 代码,和 AspectJ 没什么关系,但是 Spring 延用了 AspectJ 中的概念,包括使用了 AspectJ 提供的 jar 包中的注解,但是不依赖于其实现功能。
如 @Aspect、@Pointcut、@Before、@After 等注解都是来自于 AspectJ,但是功能的实现是纯 Spring AOP 自己实现的。
实现机制
Spring AOP 底层实现机制目前有两种:JDK 动态代理、CGLIB 动态字节码生成。在阅读源码前对这两种机制的使用有个认识,有利于更好的理解源码。
JDK 动态代理
public class MyInvocationHandler implements InvocationHandler {
private Object origin;
public MyInvocationHandler(Object origin) {
this.origin = origin;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke start");
Object result = method.invoke(origin, args);
System.out.println("invoke end");
return result;
}
}
public class JdkProxyTest {
public static void main(String[] args) {
UserService proxy = (UserService) Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(),
new Class[]{UserService.class}, new MyInvocationHandler(new UserServiceImpl()));
proxy.doSomething();
}
}
CGLIB 代理
public class CglibInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("intercept start");
Object result = proxy.invokeSuper(obj, args);
System.out.println("intercept end");
return result;
}
}
public class CglibProxyTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CglibObject.class);
enhancer.setCallback(new CglibInterceptor());
CglibObject proxy = (CglibObject) enhancer.create();
proxy.doSomething();
}
}
AOP实例
UserService接口:
public interface UserService {
public void findAll();
}
UserServiceImpl实现类:
@Service
public class UserServiceImpl implements UserService{
public void findAll(){
System.out.println("findAll...");
}
}
AopAspect切面类:
@Component
@Aspect
public class AopAspect {
@Pointcut("execution(* com.itheima.service..*.*(..))")
public void pointcut() {
}
@Before("pointcut()")
public void before() {
System.out.println("before");
}
@After("pointcut()")
public void after() {
System.out.println("after advice");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws InterruptedException {
System.out.println("around advice start");
try {
Object result = proceedingJoinPoint.proceed();
System.out.println("result: " + result);
System.out.println("around advice end");
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
}
applicationContext-aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.itheima"/>
<aop:aspectj-autoproxy />
</beans>
源码剖析-AOP 注解的解析
当使用 <aop:aspectj-autoproxy /> 注解开启 AOP 功能时,解析aop标签会进行到parseCustomElement(root);
parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 1.默认命名空间的处理
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
// 遍历root的子节点列表
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />
parseDefaultElement(ele, delegate);
}
else {
// 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
delegate.parseCustomElement(ele);
}
}
}
} else {
// 2.自定义命名空间的处理**** <aop:aspectj-autoproxy />进行解析
delegate.parseCustomElement(root);
}
}
parseCustomElement
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
// 1.拿到节点ele的命名空间,例如常见的:
// <context> 节点对应命名空间: http://www.springframework.org/schema/context
// <aop> 节点对应命名空间: http://www.springframework.org/schema/aop
String namespaceUri = getNamespaceURI(ele);
// 2.拿到命名空间对应的的handler, 例如:http://www.springframework.org/schema/context 对应 ContextNameSpaceHandler
// 2.1 getNamespaceHandlerResolver: 拿到namespaceHandlerResolver
// 2.2 resolve: 使用namespaceHandlerResolver解析namespaceUri, 拿到namespaceUri对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 3.使用拿到的handler解析节点(ParserContext用于存放解析需要的一些上下文信息)
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
Spring会从“META-INF/spring.handlers” 配置文件中拿到该注解对应的 NamespaceHandlerSupport:AopNamespaceHandler
在 AopNamespaceHandler 的 init 方法会给该注解注册对应的解析器,aspectj-autoproxy 对应的解析器是:AspectJAutoProxyBeanDefinitionParser。
当解析到 <aop:aspectj-autoproxy /> 注解时,会调用 AspectJAutoProxyBeanDefinitionParser 的 parse方法。
AspectJAutoProxyBeanDefinitionParser#parse
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 1.注册AspectJAnnotationAutoProxyCreator
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
// 2.对于注解中子节点的处理
extendBeanDefinition(element, parserContext);
return null;
}
1.注册 AspectJAnnotationAutoProxyCreator,见代码块1。
代码块1:AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 1.注册AspectJAnnotationAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 2.对于proxy-target-class以及expose-proxy属性的处理
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
// 3.注册组件并通知,便于监听器做进一步处理
registerComponentIfNecessary(beanDefinition, parserContext);
}
1.注册 AspectJAnnotationAutoProxyCreator,见代码块2
2.对于 proxy-target-class 以及 expose-proxy 属性的处理,见代码块3
代码块2:AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 1.如果注册表中已经存在beanName=org.springframework.aop.config.internalAutoProxyCreator的bean,则按优先级进行选择。
// beanName=org.springframework.aop.config.internalAutoProxyCreator,可能存在的beanClass有三种,按优先级排序如下:
// InfrastructureAdvisorAutoProxyCreator、AspectJAwareAdvisorAutoProxyCreator、AnnotationAwareAspectJAutoProxyCreator
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
// 拿到已经存在的bean定义
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
// 如果已经存在的bean的className与当前要注册的bean的className不相同,则按优先级进行选择
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 拿到已经存在的bean的优先级
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
// 拿到当前要注册的bean的优先级
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
// 如果当前要注册的bean的优先级大于已经存在的bean的优先级,则将bean的className替换为当前要注册的bean的className,
apcDefinition.setBeanClassName(cls.getName());
}
// 如果小于,则不做处理
}
// 如果已经存在的bean的className与当前要注册的bean的className相同,则无需进行任何处理
return null;
}
// 2.如果注册表中还不存在,则新建一个Bean定义,并添加到注册表中
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
// 设置了order为最高优先级
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注册BeanDefinition,beanName为org.springframework.aop.config.internalAutoProxyCreator
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
org.springframework.aop.config.internalAutoProxyCreator 是内部管理的自动代理创建者的 bean 名称,可能对应的 beanClassName 有三种,对应的注解如下:
InfrastructureAdvisorAutoProxyCreator:<tx:annotation-driven />
AspectJAwareAdvisorAutoProxyCreator:<aop:config />
AnnotationAwareAspectJAutoProxyCreator:<aop:aspectj-autoproxy />
当同时存在多个注解时,会使用优先级最高的 beanClassName 来作为 org.springframework.aop.config.internalAutoProxyCreator 的 beanClassName。现在暂不考虑同时存在其他注解的情况,所以在这边会注册的 beanClassName 为:AnnotationAwareAspectJAutoProxyCreator。
代码块3:useClassProxyingIfNecessary
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) {
if (sourceElement != null) {
boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
if (proxyTargetClass) {
// 如果节点设置了proxy-target-class=true,则给beanName为org.springframework.aop.config.internalAutoProxyCreator
// 的BeanDefinition添加proxyTargetClass=true的属性,之后创建代理的时候将强制使用Cglib代理
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
boolean exposeProxy = Boolean.valueOf(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
if (exposeProxy) {
// 如果节点设置了expose-proxy=true,则给beanName为org.springframework.aop.config.internalAutoProxyCreator
// 的BeanDefinition添加exposeProxy=true的属性,之后创建拦截器时会根据该属性选择是否暴露代理类
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
总结
该部分最重要的内容就是注册了内部管理的自动代理创建者的 bean:AnnotationAwareAspectJAutoProxyCreator