Spring系列 第五弹 AOP原理与AOP编程

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

好久没在公众号发文了,咱也不说什么,直接上干货。

想必基于AspectJ的Spring AOP大家都会用了,网上也有很多资料,因此关于AOP在使用层面的知识不再赘述,咱就深挖一下源码,专门讲一下Spring AOP的底层设计。

本文的有些知识(如Introduction)可能平时没见到过,请选择性忽略。


1.AOP基础


AOP,面向切面编程,是一种编程思想,是抽象,而AspectJ和Spring AOP这两个框架分别实现了AOP。
Aspect是更通用的AOP框架,其它语言也有对应的实现,如AspectC、AspectC++、Aspect.Net、AspectPHP等。
Spring AOP中引入AspectJ仅用于定义切面(基于@AspectJ的AOP支持),但运行时仍然是纯 Spring AOP(基于JDK动态代理和Cglib动态代理),并且不依赖于 AspectJ 编织器(ajc) 。
Advice和Advisor的区别:Advice是通知(如before通知、after通知等),Advisor 是通知器(增强器),Advisor接口源码如下所示,由此可见Advisor和Advice是一对一关系。


package org.springframework.aop;
import org.aopalliance.aop.Advice;
    
public interface Advisor {
    Advice EMPTY_ADVICE = new Advice() {};
    Advice getAdvice();
    boolean isPerInstance();
}


Tips:

  • 切入点匹配的连接点的概念是 AOP 的关键 。

  • Spring Aop仅支持方法拦截,不支持字段拦截

  • Spring Aop目的不是提供最完整的 AOP 实现(尽管 Spring AOP 非常有能力),而是提供 AOP 实现和 Spring IoC 之间的紧密集成,以帮助解决企业应用程序中的常见问题

  • jdk动态代理仅支持public方法,cglib虽然支持public、protected和package方法,但代理方法最好是public的


2.AOP概念


切面(Aspect ):有两种定义切面的方式:基于xml和基于@AspectJ

连接点(Join point):程序执行过程中的一个点,例如方法的执行或异常的处理。在Spring AOP中,一个连接点总是代表一个方法执行 

通知(Advice):切面在特定连接点上采取的操作

PointCut(切入点):匹配连接点的谓词。Advice 与切入点表达式相关联,并在与切入点匹配的任何连接点处运行(例如,执行具有特定名称的方法)。Spring 默认使用 AspectJ 切入点表达式语言 

目标对象

代理对象

编织(Weaving):可以在编译时(需要特殊的编译器,如AspectJ的ajc编译器)、类加载时(需要特殊的类加载器,如AspectJ5的加载时织入load-time weaving,LTW)和运行时完成(动态代理)


3.通知类型


before:方法执行前(不能出异常)

After returning :方法返回前运行(不能出异常)

After throwing:如果方法因抛出异常而退出,则运行

After(最终):方法正常运行完或抛出异常,都将运行

Around:这是最一般的建议,功能更强大。Spring官方建议尽量不要使用Around通知,例如将方法返回值做缓存,应优先选择After returning而不是Around


4.IntroductionAdvisor


IntroductionAdvisor和PointcutAdvisor一切,都是Advisor接口的子类,都是增强器,下面通过代码看看Introduction是如何使用的。


@Component("mailBean")
public class MailBean {
    public void sayHello(){
        System.out.println("--MailBean hello--");
    }
}
public interface IMailService {
    void send();
}
public class DefaultMailServiceImpl implements IMailService {
    @Override
    public void send() {
        System.out.println("-- MailServiceImp::send--");
    }
}
@Aspect
@Component
public class IntroductionAspect {
    // 重点在这里,这里会通过动态代理生成一个bean,该bean继承自MailBean并实现了IMailService接口,相当于给MailBean增加了一个send方法
    // 当执行send方法时,会被Introduction Advice拦截到,并执行DefaultMailServiceImpl的send方法
    @DeclareParents(value = "com.bobo.springbootdemo.introduction.MailBean", defaultImpl = DefaultMailServiceImpl.class)
    private IMailService iMailService;
}
@SpringBootTest
@RunWith(SpringRunner.class)
public class IntroductionTests {
    @Autowired
    private ApplicationContext context;
    @Test
    public void test(){
        IMailService mailService = context.getBean("mailBean", IMailService.class);
        MailBean mailBean = context.getBean("mailBean", MailBean.class);
        mailService.send(); // 输出-- MailServiceImp::send--
        mailBean.sayHello();// 输出--MailBean hello--
    }
}
// 动态生成的代理类声明如下
public class MailBean$$EnhancerBySpringCGLIB$$73ed774c extends MailBean implements IMailService, SpringProxy, Advised, Factory


最后说一下Spring官方的介绍:introduction代表为一个类添加的新字段或方法定义,Spring AOP允许我们为任何对象introduce(引入)新的接口实现,由此一目了然。


public interface IntroductionAdvisor extends Advisor, IntroductionInfo {
  // 不需要MethodMatcher,也不需要PointCut,只要ClassFilter过滤出符合的类即可
  ClassFilter getClassFilter();
  void validateInterfaces() throws IllegalArgumentException;
}


如何将IntroductionAdvisor和IntroductionInterceptor关联在一起呢?这里以DefaultIntroductionAdvisor和DelegatingIntroductionInterceptor为例,以编程的方式实现DeclareParents注解同等功能。


public interface IMailService {
    void send();
}
public class MailBean {
    public void sayHello(){
        System.out.println("--MailBean hello--");
    }
}
public class IntroductionApiInterceptor extends DelegatingIntroductionInterceptor implements IMailService{
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        // return mi.proceed(); // 不能这样用,因为原来的类MailBean没有send方法
        return super.invoke(mi); // 执行原来的类MailBean没有的send方法,这就是引入(Introduction)
    }
    @Override
    public void send() {
        System.out.println("-- IntroductionApiInterceptor::send--");
    }
}
public class IntroductionApiAdvisor extends DefaultIntroductionAdvisor {
    public IntroductionApiAdvisor(){
        super(new IntroductionApiInterceptor(), IMailService.class);
    }
}
@Test
public void test(){
  ProxyFactory factory = new ProxyFactory(new MailBean());
    factory.setProxyTargetClass(true);
    factory.addAdvisors(new IntroductionApiAdvisor());
    MailBean mailBean = (MailBean)factory.getProxy();
    IMailService mailService = (IMailService)proxy;
    mailBean.sayHello(); // 输出--MailBean hello--
    mailService.send(); // 输出-- MailServiceImp::send--
}
// 动态生成的代理类声明如下
public class MailBean$$EnhancerBySpringCGLIB$$550bae40 extends MailBean implements IMailService, SpringProxy, Advised, Factory


5.AOP编程


a.使用编程的方式基于AspectJ创建代理


AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// SecurityManager类必须打了@Aspect注解
factory.addAspect(SecurityManager.class);
// 也可以用一个实例,但是也要打了@Aspect注解
factory.addAspect(usageTracker);
// 生成代理对象
MyInterfaceType proxy = factory.getProxy();


b.顶层接口


// 增强器
public interface Advisor {
  Advice EMPTY_ADVICE = new Advice() {};
  Advice getAdvice();
  boolean isPerInstance();
}
// 通知
public interface Advice {
}
// 切入点增强器
public interface PointcutAdvisor extends Advisor {
  Pointcut getPointcut();
}
// 切入点
public interface Pointcut {
  // ClassFilter接口用于将切入点限制为给定的一组目标类
  ClassFilter getClassFilter();
    // 通常比ClassFilter更重要
  MethodMatcher getMethodMatcher();
  Pointcut TRUE = TruePointcut.INSTANCE;
}
public interface MethodMatcher {
    // 静态切入点只需要调用2参数的matches方法
    boolean matches(Method m, Class<?> targetClass);
    boolean isRuntime();
    // 对于动态切入点,当2参数matches方法和isRuntime都返回true时,每次执行方法都要调用一下3参数的matches方法(因为要考虑方法参数)
    boolean matches(Method m, Class<?> targetClass, Object... args);
}


静态切入点(只考虑类和方法):

  • 子类只需要实现StaticMethodMatcherPointcut 即可,如正则表达式切入点:JdkRegexpMethodPointcut。

动态切入点(还要考虑方法参数):

  • 子类要实现DynamicMethodMatcherPointcut


c.拦截和通知接口


public interface Interceptor extends Advice {
}
// Around通知
public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation invocation) throws Throwable;
}
public interface BeforeAdvice extends Advice {
}
// Before通知
public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method m, Object[] args, Object target) throws Throwable;
}
public interface AfterAdvice extends Advice {
}
// throw通知,throw通知不需要实现任何方法,因此需要自己添加方法:void afterThrowing(Method m, Object[] args, Object target, ServletException ex)
public interface ThrowsAdvice extends AfterAdvice {
}
// After Return通知
public interface AfterReturningAdvice extends AfterAdvice {
  void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}
// Introduction Advice,这是一种特殊的通知。IntroductionInterceptor的用法见2.3节
public interface IntroductionInterceptor extends MethodInterceptor, DynamicIntroductionAdvice {
}


d.使用编程的方式创建代理


ProxyFactory factory = new ProxyFactory(new MailBean());
factory.setProxyTargetClass(true);
factory.addAdvisors(new IntroductionApiAdvisor());
MailBean mailBean = (MailBean)factory.getProxy();
// 创建完代理后,仍然可以增删增强器和通知
Advised advised = (Advised) mailBean;
Advisor[] advisors = advised.getAdvisors();
advised.addAdvice(new DebugInterceptor());
advised.addAdvisor(new DefaultPointcutAdvisor(mySpecialPointcut, myAdvice));


ProxyConfig提供的属性:

  • proxyTargetClass:是否代理目标类

  • optimize:是否优化,如果不了解优化原理则不要轻易使用

  • frozen:如果为true则不允许更改配置。如果不希望调用者能够操纵代理(通过Advised 接口)则设为true,默认是false

  • exposeProxy:确定当前代理是否应该在ThreadLocal公开以便目标可以访问它(通过AopContext.currentProxy())


ProxyFactoryBean提供的属性:

  • proxyInterfaces:String接口名称数组。如果未提供,则使用目标类的CGLIB 代理

  • interceptorNames:Advisor、interceptor或其它Advice的String数组,这些名称是当前工厂中的 bean 名称,可使用*号

  • singleton :是否是单例,默认值为true


ProxyFactoryBean的例子:


// 定义目标类
@Component
public class MessageBean {
    public void sendMsg(){
        System.out.println("-- MessageBean sendMsg --");
    }
}
// 定义前置通知
@Component
public class MessageBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-- MessageBeforeAdvice before --");
    }
}
// 定义增强器,注入前置通知
@Component("messageAdvisor")
public class MessageAdvisor extends StaticMethodMatcherPointcutAdvisor {
    @Autowired
    private MessageBeforeAdvice messageBeforeAdvice;

    @PostConstruct
    public void init(){
        super.setAdvice(messageBeforeAdvice);
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        if(targetClass.getName().contains("MessageBean") && method.getName().contains("send")){
            return true;
        }
        return false;
    }
}
// 定义后置通知
@Component("messageAfterAdvice")
public class MessageAfterAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-- MessageAfterAdvice afterReturning --");
    }
}
// 配置ProxyFactoryBean
@Configuration
public class ProxyFactoryBeanConfig {
    @Autowired
    private MessageBean messageBean;
    @Bean
    public ProxyFactoryBean proxyFactoryBean(){
        ProxyFactoryBean bean = new ProxyFactoryBean();
        bean.setTarget(messageBean);
        bean.setProxyTargetClass(true);
        bean.setInterceptorNames("messageAdvisor","messageAfterAdvice");
        bean.setSingleton(true);
        return bean;
    }
}
// 单元测试
@Test
public void test3(){
    ProxyFactoryBean proxyFactoryBean = context.getBean(ProxyFactoryBean.class);
    MessageBean proxy = (MessageBean)proxyFactoryBean.getObject();
    proxy.sendMsg();
}
最后输出
// -- MessageBeforeAdvice before --
// -- MessageBeforeAdvice before --
// -- MessageBean sendMsg --
// -- MessageAfterAdvice afterReturning --


e.自动代理(auto-proxy)


前面都是通过ProxyFactory或ProxyFactoryBean来为目标类创建代理,但这种方式虽然灵活,但不利于管理,下面考虑使用自动代理功能。
自动代理建立在BeanPostProcessor的基础上,org.springframework.aop.framework.autoproxy包提供了一些现成的自动代理类
  • BeanNameAutoProxyCreator:通过beanName通配符匹配,如果匹配成功则创建代理

  • DefaultAdvisorAutoProxyCreator:更加通用和强大


6.动态代理


a.JDK动态代理


public interface IProductService {
    void update();
}
public class ProductServiceImpl implements IProductService {
    @Override
    public void update() {
        System.out.println("-- ProductServiceImpl update --");
    }
}
public class ProductHandler implements InvocationHandler {
    private Object target;
    public ProductHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before advice");
        // 这里必须要传入目标对象
        Object res = method.invoke(target,args);
        System.out.println("after advice");
        return res;
    }
}
public class JdkDynamicProxyTests {
    public static void main(String[] args) {
        Object obj = Proxy.newProxyInstance(JdkDynamicProxyTests.class.getClassLoader(),
                new Class[]{IProductService.class},
                new ProductHandler(new ProductServiceImpl()));
        IProductService productService = (IProductService)obj;
        productService.update();
    }
}
// 最后生成的代理类签名如下
// public final class $Proxy0 extends Proxy implements IProductService



b.Cglib动态代理


public class ProductServiceImpl implements IProductService {
    @Override
    public void update() {
        System.out.println("-- ProductServiceImpl update --");
    }
}
public class ProductCallback implements MethodInterceptor {
    // obj:生成的代理对象;method:目标方法;methodProxy:目标方法+代理方法
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before advice");
        Object res = methodProxy.invokeSuper(obj,args);
        System.out.println("after advice");
        return res;
    }
}
public class CglibDynamicProxyTests {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ProductServiceImpl.class);
        enhancer.setCallback(new ProductCallback());
        ProductServiceImpl service = (ProductServiceImpl)enhancer.create();
        service.update();
    }
}


注意:

  • 要区分org.aopalliance.intercept.MethodInterceptororg.springframework.cglib.proxy.MethodInterceptor,前者是Advice的子类,是通知,而后者是Callback的子类,用于Cglib生成代理类时被动态织入代理类中。


7.最后

以后发文以实用为主,深入到底层和源码级别,而一些使用层级的知识会很少分享了。
以后我会将学到的知识先总结到自己的笔记中,然后再将笔记中的知识点拆成几个大块分别发文,等到该笔记完成后,再将笔记分享出来。


原文始发于微信公众号(初心JAVA):Spring系列 第五弹 AOP原理与AOP编程