1. 介绍
1.1 什么是AOP
AOP (Aspect Orient Programming),直译过来就是面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
从该图可以很形象地看出,所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
1.2 为什么需要AOP
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
1.3 如何织入(Weaving)
在不改变原有代码的前提下,对功能进行增强。相当于定义了如何进行代理。
1.4 什么是切面(Aspect/Advisor)
切面 = 通知 + 切点。表示指定的时间点对指定的地方进行额外的操作。感觉就像切点与通知的封装类。
1.5 什么是切点(Point Cut)
指定的时间,可以理解进入通知这个大门的门票,也就是条件是什么。
1.6 什么是通知(Advice)
定义增强的功能的实现。
(1) Advice的时机
- Before——在方法调用之前调用通知
- After——在方法完成之后调用通知,无论方法执行成功与否
- After-returning——在方法执行成功之后调用通知
- After-throwing——在方法抛出异常后进行通知
- Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为
(2) Advice类图
2. 源码
2.1 通知如何实现
所有的通知实现都是继承这个钩子类。
// org.aopalliance.aop.Advice
public interface Advice {
}
2.2 切点如何实现
定义哪些类哪些方法可以执行
// org.springframework.aop.Pointcut
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
2.3 切面
可以看出切面就是切点和通知的组合。
// org.springframework.aop.Advisor
public interface Advisor {
Advice EMPTY_ADVICE = new Advice() {};
Advice getAdvice();
boolean isPerInstance();
}
// org.springframework.aop.PointcutAdvisor
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut();
}
2.4 如何织入
对类和方法进行增强就使用代理模式来增强,在JAVA中有JDK代理和Cglib代理。Spring作为框架,有两种时机可以进行织入。一是在启动容器初始化bean的时候就进行增强,然后容器中存放的不是bean的实例,而是bean的代理实例。二是在每一次使用bean的时候判断一次是否需要增强,需要就对其增强,然后返回bean的代理实例。这两种方法很明显第一种比较友好,只是让容器的启动时间稍微长了一点,而第二种在运行时判断,会使得用户的体验变差。
我们的框架初始化bean是在BeanFactory中进行,还包括bean的实例化,参数注入以及将bean放入容器中等。很明显对bean的增强应该是在bean实例化完成并在还没有放进容器中的时候。那么也就是在BeanFactory的doGetBean方法中了。这里有一个小问题在于,doGetBean方法做的事情已经够多了,继续往里加入代码无疑会使得代码大爆炸,很难维护也不易扩展。为了解决这个问题这里我们可以使用观察者模式来解决这一问题,将doGetBean方法中每一个过程都作为一个观察者存在,当我们需要添加功能时既可以添加一个观察者然后注入,这样不会对已有代码做出改变。
BeanFactory充当subject角色,BeanPostProcessor则充当observer的角色,BeanFactory监听BeanPostProcessor,我们可以将功能抽出为一个BeanPostProcessor,将其注册到BeanFactory中,这样既不会使得BeanFactory中代码过多,同时也比较容易做到了功能的解耦,假设我们不需要某一个功能,那么直接解除绑定即可而不需要任何其他操作
2.4.1 Spring AOP如何实现
(1) @EnableAspectJAutoProxy
@EnableAspectJAutoProxy主要的功能是给IOC容器中注册一个AnnotationAwareAspectJAutoProxyCreator的BeanDefinition,以便后续Bean实例化的时候注册Bean。
// org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} else {
// 注册BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", -2147483648);
beanDefinition.setRole(2);
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}
(2)BeanPostProcesor的设计
AnnotationAwareAspectJAutoProxyCreator继承InstantiationAwareBeanPostProcessor,其父类AbstractAutoProxyCreator实现了postProcessBeforeInstantiation方法(每个Bean在实例化之前都会执行),利用代理类增强了普通Bean。
(3)AOP实例化增强Bean的前置处理器
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
// 获取缓存key,其实就是 BeanName
Object cacheKey = getCacheKey(beanClass, beanName);
// targetSourcedBeans是自定义实现的Bean,AOP这里的化我觉得不是关键的地方吧
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
//判断当前bean是否在advisedBeans中(advisedBeans保存了所有需要增强的bean)
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
//1. 判断是不是 基础类型,也就说判断是不是 实现了这些接口 Advice、Pointcut、Advisor、AopInfrastructureBean、
//2. 还会判断是不是 有没有 @Aspect 注解
//3. 是否需要跳过----获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】,如果增强器是 AspectJPointcutAdvisor 类型的,则返回true(封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor类型).这里会遍历所有@Aspect下所有的方法,将其转换成切面Advisor(InstantiationModelAwarePointcutAdvisorImpl)。
// 这段逻辑是处理如果是基础类、切面类、原始类 都不会进行代理
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
// 这里其实是将 切面 Bean 的BeanName 缓存起来,代表这个 Bean 不进行 增强操作
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// ...
return null;
}
(4)AOP实例化增强Bean的后置处理器
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 需要的情况下进行包装
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
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;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 注解@Aspect的类,会将其下跟AOP相关的方法转换成切面类(PointCut),例如@Before、@After等等,@Before这些会转换成PointcutAdvisor。然后每个Bean会根据@PointCut的条件进行匹配
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
// 不为空的话,我们就需要增强这个Bean,创建代理类返回
this.advisedBeans.put(cacheKey, Boolean.TRUE);
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;
}
(5) 创建代理类
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
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);
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);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
// org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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.");
}
// 如果目标类是接口的话创建JDK代理类
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则创建CGlib代理类
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
(6) 方法调用
创建完代理类后,当遇到符合AOP方法调用的时候,JDK代理类会调用invoke方法,CGlib代理类会调用getProxy方法(最终还是利用MethodInterceptor调用invoke方法)。下面是以Cglib代理类为例,后面就开始套娃调用模式,开始执行已经排序完毕的Advisor。
// org.springframework.aop.framework.CglibAopProxy.FixedChainStaticTargetInterceptor#intercept
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
MethodInvocation invocation = new CglibMethodInvocation(
proxy, this.target, method, args, this.targetClass, this.adviceChain, methodProxy);
// If we get here, we need to create a MethodInvocation.
Object retVal = invocation.proceed();
retVal = processReturnType(proxy, this.target, method, retVal);
return retVal;
}
// org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 每次递归的时候,数组下标都会加1来遍历
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());
// 会根据@PointCut注解匹配条件,匹配到了就执行Advice通知
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.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
(7)以@After为例
通过try finally方式,递归调用。因为是@After注解,所以等后续Advisor执行完毕后,调用@After的通知方法来进行增强。
// org.springframework.aop.aspectj.AspectJAfterAdvice#invoke
public Object invoke(MethodInvocation mi) throws Throwable {
try {
return mi.proceed();
}
finally {
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}
3. FAQ
3.1 JDK代理和Cglib代理有什么区别
Java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理;而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。JDK动态代理只能对实现了接口的类生成代理,而不能针对类;CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法最好不要声明成final 。@EnableAspectJAutoProxy(proxyTargetClass = true)可以强制使用CGLIB实现AOP。
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP ;
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP ;
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换;
3.1.1 优缺点
(1) 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。
(2) 在对JDK动态代理与CGlib动态代理的代码实验中看,1W次执行下,JDK7及8的动态代理性能比CGlib要好20%左右。
3.2 这里创建代理类的过程有点疑问, @EnableAspectJAutoProxy(proxyTargetClass = true)已经标明创建代理类全部使用CGlib了,为什么这里没有这个判断
如果没有添加proxyTargetClass=true,则代理类全部使用JDK代理类。、该配置的设置导致config.isProxyTargetClass为true。方法快内表示目前是接口的话则使用JDK代理类,否则使用CGlib代理类。
// org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
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)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
3.3 没有加@EnableAspectJAutoProxy也能实现AOP功能
下面方法是应用启动的时候一定会执行的,只不过我们可以通过注解来获取额外的配置,这样配置会比较灵活。
// org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
4. 参考资料
【Spring的两种动态代理:Jdk和Cglib 的区别和实现】