Spring AOP核心源码分析之不搞清楚这些别说你会Spring AOP

Spring AOP核心源码分析之不搞清楚这些别说你会Spring AOP,第1张

Spring AOP核心源码分析之不搞清楚这些别说你会Spring AOP

文章目录

1、Spring AOP概述2、Spring AOP术语3、增强器的一种:IntroductionAdvisor4、Spring AOP API

1、使用编程的方式基于AspectJ创建代理2、顶层接口3、拦截和通知接口4、使用编程的方式创建代理5、自动代理(auto-proxy) 5、Spring AOP底层原理-动态代理

1、JDK动态代理2、Cglib动态代理 6、如何查看动态生成的代理类源码?
本文标题1-5会用到标题6的知识:关于如何查看动态生成的代理类的源码的相关介绍,有兴趣可以看一下短视频!

1、Spring AOP概述

AOP,面向切面编程,是一种编程思想,是抽象,而AspectJ和Spring AOP这两个框架分别实现了AOP。

Aspect是更通用的AOP框架,像其它语言也有对应的实现,如AspectC、AspectC++、Aspect.Net、AspectPHP等。

Spring AOP中引入AspectJ仅用于定义切面(基于@AspectJ的AOP支持),但运行时仍然是纯 Spring AOP,并且不依赖于 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、Spring AOP术语

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

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

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

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

目标对象

代理对象

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

通知类型

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

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

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

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

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

3、增强器的一种: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$ed774c 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$0bae40 extends MailBean implements IMailService, SpringProxy, Advised, Factory 
4、Spring AOP API 1、使用编程的方式基于AspectJ创建代理
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// SecurityManager类必须打了@Aspect注解
factory.addAspect(SecurityManager.class);
// 也可以用一个实例,但是也要打了@Aspect注解
factory.addAspect(usageTracker);
// 生成代理对象
MyInterfaceType proxy = factory.getProxy();
2、顶层接口
// 增强器
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

3、拦截和通知接口
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 {
}
4、使用编程的方式创建代理
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 --
5、自动代理(auto-proxy)

前面都是通过ProxyFactory或ProxyFactoryBean来为目标类创建代理,但这种方式虽然灵活,但不利于管理,下面考虑使用自动代理功能。

自动代理建立在BeanPostProcessor的基础上,org.springframework.aop.framework.autoproxy包提供了一些现成的自动代理类

BeanNameAutoProxyCreator:通过beanName通配符匹配,如果匹配成功则创建代理DefaultAdvisorAutoProxyCreator:更加通用和强大 5、Spring AOP底层原理-动态代理 1、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 
2、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.MethodInterceptor和org.springframework.cglib.proxy.MethodInterceptor,前者是Advice的子类,是通知,而后者是Callback的子类,用于Cglib生成代理类时被动态织入代理类中。

6、如何查看动态生成的代理类源码?

如何查看动态代理类的源码

视频链接:https://www.bilibili.com/video/BV1fb4y1j7LP/

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5722246.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存