SpringAOP的朴素解释
Spring Framework是构建与IOC与AOP之上的。这篇文章与另一篇文章SpringIOC的朴素解释是互补的。AOP意为面向切面编程,默认Spring只提供了运行时的方法切面。
1.面向切面编程
面向切面编程(AOP)是面向对象编程(OOP)的补充,它将处理问题的粒度细化到了方法层,在面向对象编程中,最核心的单元是类。它包含以下几个标准,所谓AOP框架就是实现这些标准:
JoinPoint:这是一个运行时概念,JoinPoint包括了运行时该切点的一切信息,可以认为是一个PointCut的具体实现。
Pointcut:就是一个切点定义,代表一类可以织入的规则。
Advice(通知/增强):就是具体的增强逻辑。
Aspect(切面):由Pointcut与Advice组成,它代表一个切面组合。
AopProxy:代理类,这是结果,Aop增强后的结果就是产生一个链接了切点、切面、通知的代理类。
注:还有一些其他的定义这里就不扯了。
在我的很多篇文档都提到了这么一个理念:标准先行。这里的AOP标准就是一个非常非常好的例子。AOP的概念说简单可以简单,说复杂也可以复杂,现在有了标准就可以统一思想实现标准就行了。
代码有三个基本时态:编译时,加载时,运行时。这三个时间都可以人为干预然后增强你的类,比如Lombok就是在编译时增强的,它会在编译时插入增强代码。编译时是不灵活的,并且会让类膨胀。
纯java类库基本上都是在运行时对对象进行操作以达到增强的目的。基本过程是:通过目标对象得到一个增强后的代理对象,使用的时候直接使用代理对象,这个代理对象是与通知列表(Advice)链接在一起的,当调用代理对象的方法时,会调用与该方法(切点,PointCut)相关联的通知,从而达到AOP的目的。所以关键的一步是如何产生合适的代理对象。
2.动态代理与SpringAOP
SpringAOP是通过动态代理实现的,分为两种:JDK动态代理与Cglib代理。实现这一切的基础是在Java中Everything is a object(一切皆对象)。Method是一个对象所以在调用过程中可以被hook插入其他逻辑。Jdk只能对接口进行增强,Cglib则不限接口与类,以下是两个简单的例子:
jdk:
public class JdkProxyTest implements Runnable{ public static void main(String[] args) { JdkProxyTest o = new JdkProxyTest(); InvocationHandler handler = new TestInvocationHandler(o); //Class[]是你要代理的接口,这里也用来提供代理类的类型,同时它也提供需要代理的方法。 Runnable proxy = (Runnable)Proxy.newProxyInstance(JdkProxyTest.class.getClassLoader(), new Class<?>[] {Runnable.class}, handler); proxy.run(); } public static class TestInvocationHandler implements InvocationHandler{ private Object o; public TestInvocationHandler(Object o) { this.o = o; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-----------------pre invoke------------------"); //调用原来的方法,必须这样搞,不然一直调用代理类的代理方法会死循环。 Object result = method.invoke(o, args); System.out.println("-----------------post invoke--------------------"); return result; } } @Override public void run() { System.out.println("=========do something==============="); } }
注:Class<?>[]提供类型的同时也提供代理方法,这意味着不仅仅是接口中定义的方法,还有接口继承自Object的方法也会被代理。
Cglib:
public class CglibProxyTest { public static void main(String[] args) { Enhancer e = new Enhancer(); //父类 e.setSuperclass(CglibProxyTest.class); TestMethodInterceptor mInterceptor = new TestMethodInterceptor(); //实现接口 e.setInterfaces(new Class<?>[]{Runnable.class}); // e.setCallbackFilter(filter); //这是关键一步,是一个Callback回调 e.setCallback(mInterceptor); CglibProxyTest proxy = (CglibProxyTest)e.create(); proxy.doSome(); } public void doSome() { System.out.println("=========do something==============="); } public static class TestMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { System.out.println("-------------before intercept-----------------"); Object result = arg3.invokeSuper(arg0, arg2); System.out.println("-------------after intercept-----------------"); return result; } } }
注:你的Class是不一定要继承某个类,实现某个接口的,他们是用来提供类型与方法的,这一点是与jdk代理不一样的。Cglib依赖ASM库。
Spring对代理类进行了抽象:
AopProxy
├─JdkDynamicAopProxy
└─CglibAopProxy
└─ObjenesisCglibAopProxy
AopProxy接口只有两个方法:getProxy()和getProxy(ClassLoader)。也就是关键的一步,得到代理对象。而在Spring中能够优雅做到这一点的就是:BeanPostProcessor。BeanPostProcessor在Spring框架的扩展点中非常非常重要,它可以对bean创建进行hook,另外还有一个BeanFactoryPostProcessor也很重要,是对bean注册进行Hook。
使用:
//定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TestTarget {}
//应用注解
public class TestClass {
@TestTarget
public void doSomething() {
System.out.println("------------------------------doSomething-----------------------");
}
}
//切点、切面、通知声明
@Aspect
public class TestAspact {
@Pointcut("@annotation(com.example.demo.test.TestTarget)")
public void pointCut() {}
@Before("pointCut()")
public void advice(JoinPoint joinPoint) {
System.out.println("-------------advice----------------");
}
}
//配置
@Configuration
public class TestConfiguration {
@Bean
public TestClass testClass() {
return new TestClass();
}
@Bean
public TestAspact testAspact() {
return new TestAspact();
}
}
上面的所有被注解@TestTarget标识的bean的method都被增强了,增强内容是在控制台输出"-------------advice----------------"。
过程:
这里的过程是Spring使用BeanPostProcessor为bean产生代理对象的过程;也就是SpringAOP过程。这里以上面为的例子:
1.getBean(),这是梦开始的地方就是它调用的BeanPostProcessor方法。
2.AbstractAutoProxyCreator.postProcessAfterInitialization(Object,String),这里的具体实现类是AnnotationAwareAspectJAutoProxyCreator。就是这个BeanPostProcessor对Bean进行了AOP。
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
//得到cacheKey,普通bean就是beanName,FactoryBean则是BeanFactory.FACTORY_BEAN_PREFIX(&)+beanName;
//如果beanName为空(理论上不可能)则返回Class
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//如果必要则生成代理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
3.wrapIfNecessary(),判断是否符合AOP条件,如果符合则产生代理类。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
//已经是缓存过的不需要增强的bean
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
//Advice,Pointcut,Advisor,AopInfrastructureBean不必增强;
//beanName是.ORIGINAL结尾(这里需要自己看代码,条件必要但是不充分)的不必增强。
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 关键一步,找到合适的Advice,内部调用findEligibleAdvisors()
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
//缓存,这里可以看出代理类可以再次被增强
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//关键一步,找到Advice后产生代理类
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;
}
注:大概就是俩个关键步骤,a.根据bean信息找到合适的Advice,b.然后为这个bean应用这些advice产生代理对象并返回。
4.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(),这个方法会在所有可用的Advice中找到合适这个Class的advice。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//这一步在下面会讲到,它会处理切面
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//筛选出合适的advisor,
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
注:findAdvisorsThatCanApply()首先设置当前线程正在被代理的beanName,然后内部委托给AopUtils.findAdvisorsThatCanApply(List
注:Introduction用法:使用Introduction是一种非常非常不好的实践,在别人使用的时候会感觉有些方法是凭空产生的,如果你需要一个接口,那你直接实现就行了,如果这个接口有默认实现你提供接口方法默认实现就行了。当然也许是我没get到它的要点。
//需要引入的接口
public interface TestInterface {
void doRun();
}
//目标
@Component
public class TestClass {}
//切面
@Component
@Aspect
public class TestAspact {
//定义切点与默认实现
@DeclareParents(value = "com.example.demo.test.TestClass" , defaultImpl = DefaultTestInterface.class )
private TestInterface testInterface;
//接口默认实现
public static class DefaultTestInterface implements TestInterface{
@Override
public void doRun() {
System.out.println("-------doRun---------");
}
}
}
//使用
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args);
//使用
TestInterface i = (TestInterface)app.getBean("testClass");
i.doRun();
}
}
5.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);
}
}
//找到所有适合这个bean的Advisor,这里最重要的是会找到通用Advisor,
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 如果Bean类没有在本地加载,则使用原始的ClassLoader来覆盖类加载器。
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
}
//这里内部会构造AopProxy对象,具体是那个实现会根据proxyFactory设置选择
//然后通过这个AopProxy对象的getProxy(classLoader)方法得到代理对象
return proxyFactory.getProxy(classLoader);
}
注:如果目标类是接口或者目标类本身就是代理类型,则产生JdkDynamicAopProxy对象,否则产生ObjenesisCglibAopProxy对象。其还有其它情况(目标类为空或者只设置了接口)会根据ProxyFactory配置选择产生JdkDynamicAopProxy对象。得到AopProxy对象(这里类型是ObjenesisCglibAopProxy)后调用getProxy()得到代理对象,getProxy()如下:
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating CGLIB proxy: " + 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;
//如果已是代理对象,则拿其父类作为代理父类,其接口添加到接口列表中。
if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
proxySuperClass = rootClass.getSuperclass();
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// 对Class及父类的每个方法的限定符进行校验,只有public或者protected的非静态非final方法能通过校验。
// 未通过校验的方法会在debug日志级别环境下打印日志。
validateClassIfNecessary(proxySuperClass, classLoader);
// 终于到了这一步,构建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 ClassLoaderAwareGeneratorStrategy(classLoader));
//这里会找到全部的CallBack回调(实际上是临时new的,它需要根据AopProxy属性的不同而不同)。
//我们的advisor会放到DynamicAdvisedInterceptor的advisors里面。
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);
}
。。。。。。。。。。。。
}
大概过程就是上面这几个步骤。
这还只是如果生成代理对象的过程,还有一个处理切面的过程:
处理切面也是由AnnotationAwareAspectJAutoProxyCreator完成的,上文中提到的findCandidateAdvisors()会找到所有@Aspect bean用与自动代理。
protected List<Advisor> findCandidateAdvisors() {
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors();
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
//关键一步buildAspectJAdvisors会处理@Aspect以及其他和AOP相关的注解,找出所有advisor
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
注:buildAspectJAdvisors()第一次调用的时候的时候会将查找容器内所有的Aspect bean并将其处理成Advisor对象然后缓存起来。
注:从这里可以知道,一个Aspect必须要作为bean注入到容器才会生效。
注:具体实现请自行查看BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()源码。
到这一步SpringAOP的过程就完成了。总的来说它包括两个部分:处理切面和生成代理对象。
3.加载时织入(LoadTime-Weaving)
这部分文档在Spring加载时织入,详细的定义属于Aspectj的知识。这里不打算往下深究了,没多大意义,等我遇到需要加载时织入的时候再来补充,希望你不要这么倒霉有这样的需求。但是可以猜到的是,这里的ClassLoader会使用自定义的ClassLoader。
注:最开始我们提到Spring提供的时运行时的方法AOP,而如果启用了LoadTime-Weaving则可以实现更加精细的AOP。
4.API
Spring使用Advice、Pointcut和Advisor接口分别对通知(advice)、切点(pointcut)和切面(advice)进行抽象。使用如下:
//pointcut切点实现
public class TestPointCut implements Pointcut {
@Override
public ClassFilter getClassFilter() {
return new TestClassFilter();
}
@Override
public MethodMatcher getMethodMatcher() {
return new TestMethodMatcher();
}
public static class TestMethodMatcher extends DynamicMethodMatcher{
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return method.getAnnotation(TestTarget.class) != null;
}
}
public static class TestClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
return TestClass.class.equals(clazz);
}
}
}
//advice通知实现
public class TestMethodBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("----------------before-------------------");
}
}
//Advisor切面实现(也就是Aspect)
public class TestAdvisor extends AbstractPointcutAdvisor {
private static final long serialVersionUID = -5818866045520237089L;
@Autowired
private TestPointCut testPointCut;
@Autowired
private TestMethodBeforeAdvice testMethodBeforeAdvice;
@Override
public Pointcut getPointcut() {
return testPointCut;
}
@Override
public Advice getAdvice() {
return testMethodBeforeAdvice;
}
}
//目标
public class TestClass {
@TestTarget
public void doSomething() {
System.out.println("------------------------------doSomething-----------------------");
}
}
//配置
@Configuration
public class TestConfiguration {
@Bean
public TestClass testClass() {
return new TestClass();
}
@Bean
public TestMethodBeforeAdvice testMethodBeforeAdvice() {
return new TestMethodBeforeAdvice();
}
@Bean
public TestPointCut testPointCut() {
return new TestPointCut();
}
@Bean
public TestAdvisor testAdvisor() {
return new TestAdvisor();
}
}
//使用
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext app = SpringApplication.run(DemoApplication.class, args);
TestClass c = app.getBean(TestClass.class);
c.doSomething();
}
}
在绝大多数时候我们使用AspectJ提供的表达式就可以实现我们的需求,在不济你可以扩展JdkRegexpMethodPointcut、AspectJExpressionPointcut(AspectJ表达式切点实现)、StaticMethodMatcherPointcut(静态方法匹配切点)、DefaultPointcutAdvisor(默认的切面实现)、MethodXXXXAdvice(方法前置/后置/异常等等等通知,是个接口)等等去满足你要的功能;如果还不能满足再去实现接口。Spring提供了一些工具类比如Pointcuts来辅助完成功能。
还有一些偏门的情况是比如你想自己实现getObject()获取代理类的过程或者所想控制Advisor切面的顺序等等等等一大堆,你可以自己实现ProxyFactoryBean等等,回头遇到再来研究。不过理论上上面的得到代理对象的过程以及涵盖了这部分逻辑。
注:FactoryBean在Spring中是用来获取bean对象(可用的Object)的工厂,他是一个泛型接口。
5.总结
和IOC的文章一样,这篇文章的绝大多是知识是在实际生产中用不上的,也就是无用知识;我依然保持我的观点:最大的问题是如何高效合作,而不是技术过不过关。AOP是有其规范的,有规范就有其实现,会用就行。总的来说,SpringAOP默认提供运行时方法增强,支持AspactJ语法定义,可以实现加载时增强,但是我们确实不到万不得已不应该这么做;SpringAOP包括了俩个过程:切面处理与代理对象生成,这两个步骤都是通过BeanPostProcessor完成的,处理切面是在第一次寻找Advisor的时候完成的,而代理对象生成是在找到合适的Advisor后通过AopProxy得到的;Spring对AOP的概念都抽象了接口并且开放了相关的API。
注:转载请标明出处与作者。如果意见或建议请评论区留言。