文章目录
一、SpringAOP介绍
AOP就是面向切面编程的简称,最早是由AspectJ首创,它里面有一些概念来帮助理解。在SpringAOP中,对应的也有几个名词(和AspectJ有细微差别)。
- 切面(Aspect):在SpringAOP中指的是切面类,它管理着切点、通知
- 连接点(Join Point):可以被增强的业务方法
- 切点(Pointcut):被增强的业务方法,结合切点表达式进行实现
- 通知(Advice):需要增加到业务方法中的公共代码,通知可以在业务方法不同位置进行执行(前置通知、后置通知、异常通知、返回通知、环绕通知)
- 目标对象(Target Object):被增强的方法所在的对象
- 织入(Weaving):SpringAOP在运行期织入,织入是一个将通知加入切点的动作。AOP容器会为目标对象动态创建一个代理对象。
我觉得上面这些都不重要,记一下应付面试就行了。Spring的AOP我理解是利用动态代理增强方法,不侵入原来方法,在方法的执行前后增加逻辑,并把这一功能做到灵活配置。可用于权限认证、日志、事务处理等。
二、JDK动态代理与Cglib动态代理
SpringAOP有两种实现方式,JDK动态代理和Cglib动态代理,下面看看简单的使用案例
2.1 JDK动态代理
JDKInvocationHandler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {
final private Object target;//目标对象
public JDKInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before method");//公共逻辑代码,通知
Object result = method.invoke(target, args);
System.out.println("after method");
return result;
}
}
TargetInterFace
public interface TargetInterFace {
public void run();
}
TargetObject
public class TargetObject implements TargetInterFace {
@Override
public void run() {
System.out.println("run quickly!");
}
}
JDKProxtTest
public class JDKProxtTest {
public static void main(String[] args) {
TargetObject targetObject = new TargetObject();
//newProxyInstance第二个参数需要传入接口,因此jdk动态代理只能代理接口中的方法
TargetInterFace target = (TargetInterFace) Proxy.newProxyInstance(JDKProxtTest.class.getClassLoader(), new Class[]{TargetInterFace.class}
,new JDKInvocationHandler(targetObject));
target.run();
}
}
2.2 Cglib动态代理
CglibMethodInterceptor
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("after method");
return result;
}
}
CglibProxyTest
import org.springframework.cglib.proxy.Enhancer;
public class CglibProxyTest {
public static void main(String[] args) {
TargetObject targetObject = (TargetObject) Enhancer.create(TargetObject.class, new CglibMethodInterceptor());
System.out.println(targetObject.getClass().getName());
targetObject.run();
}
}
三、代码跟踪
3.1 环境准备
YexAspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
//切面
@Aspect
@Component
public class YexAspect {
//通知,引号里面是切点
@Before("execution(public void com.spring.study.service.UserService.run())")
public void yexBefore(JoinPoint joinPoint){
System.out.println("Before run");
}
}
AppConfig
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@ComponentScan("com.spring.study")
@EnableAspectJAutoProxy //开启AOP,将AnnotationAwareAspectJAutoProxyCreator注册到容器,后面用它进行AOP
public class AppConfig {
}
IUserService
public interface IUserService {
public void run();
}
UserService
import org.springframework.stereotype.Component;
@Component
public class UserService implements IUserService {
@Override
public void run() {
System.out.println("run run run quickly!");
}
}
TestSpring
import com.spring.study.service.IUserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class TestSpring {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
IUserService o = (IUserService) context.getBean("userService");
System.out.println(o.getClass());//class com.sun.proxy.$Proxy20 代理对象
o.run();
}
}
3.2 源码实现
先找到实现AOP的地方,在前面时深入分析过Bean的生命周期。AOP就是在bean初始化的最后阶段,处理BeanPostProcessor的postProcessAfterInitialization时进行的
进入
doCreateBean–>initializeBean(beanName, exposedObject, mbd)–>applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
到目前为止UserService还没被代理,AnnotationAwareAspectJAutoProxyCreator将会进行AOP。
3.2.1 AnnotationAwareAspectJAutoProxyCreator
翻译一下:
- AspectJAwareAdvisorAutoProxyCreator的子类,用于处理当前应用程序上下文中的所有AspectJ注 解,以及Spring Advisors。
- 任何AspectJ注解的类,如果可以被Spring AOP的代理模式代理,那么它们都将被自动识别,并且使用它们的通知。
- 如果使用aop:include元素,则只有名称与include模式匹配的@AspectJ bean才会被视为定义用于Spring自动代理的切面。
- Spring Advisors的处理遵循AbstractAdvisorAutoProxyCreator。
因此关于AOP的处理应该在AbstractAdvisorAutoProxyCreator里面
3.2.2 AbstractAdvisorAutoProxyCreator
看第一句就够了,通用自动代理创建者,基于检测到的每个bean的Advisors为特定bean构建AOP代理。就是说,通知和目标bean的匹配是在这个地方做的。AOP的入口postProcessAfterInitialization是在它的父类AbstractAutoProxyCreator里面实现的。
3.2.3 AbstractAutoProxyCreator
真正实现postProcessAfterInitialization方法的在这个类里面
进入wrapIfNecessary
这块能匹配到,说明需要被代理getAdvicesAndAdvisorsForBean是一个模板方法,真正的实现就在前面讲的AbstractAdvisorAutoProxyCreator里面。往下走,匹配到了我们自定义的advice
继续进入createProxy
这个方法先构建一个代理工厂,把目标对象和通知添加进去。再通过工厂得到代理对象。进入getProxy
getProxy–>createAopProxy()–>getAopProxyFactory().createAopProxy(this)
UserService实现了接口,所以采用JDK动态代理。返回一个JdkDynamicAopProxy,返回到
getProxy–>getProxy(classLoader)
进入到了JdkDynamicAopProxy的getProxy。最终找到了如JDK动态代理示例的代码:
Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
JdkDynamicAopProxy本身实现了InvocationHandler,由他自己来处理代理到目标方法之后的逻辑。
回到wrapIfNecessary
返回最后的代理对象,正确输出
四、SpringAOP与AspectJAOP区别
- SpringAOP是基于代理的,有接口的时候,就是基于JDK动态代理,无接口的时候是基于Cglib动态代理。这两种代理都只能对方法进行代理
- AspectJ可以对field、constructor的访问进行拦截
- SpringAOP的采用运行期间去生成目标对象的代理对象来实现,导致其只能在运行期工作
- AspectJ可以在编译期通过特殊的编译,就把切面逻辑,织入到class中,而且可以嵌入切面逻辑到任意地方,比如constructor、静态初始化块、field的set/get等
- SpringAOP依赖AspectJ,但是只用到了AspectJ的切点解析和匹配。@Aspect、@Before等这些注解都是AspectJ提供的