Spring系列之AOP分析之代理对象的创建(六)

简介:

我们在之前的文章中说了Advisor的创建过程,Advice的创建过程以及为目标类挑选合适的Advisor的过程。通过之前的分析我们知道,SpringAOP将切面类中的通知方法都封装成了一个个的Advisor,这样就统一了拦截方法的调用过程。我们在这一篇文章中说一下SpringAOP中代理对象的创建过程。先看下面的一张图:
AopProxy
在SpringAOP中提供了两种创建代理对象的方式,一种是JDK自带的方式创建代理对象,另一种是使用Cglib的方式创建代理对象。所以在SpringAOP中抽象了一个AopProxy接口,这个接口有两个实现类:JDKDynamicAopProxy和CglibAopProxy。从名字我们应该能看出来这两个类的作用了吧。在为目标类创建代理对象的时候,根据我们的目标类型和AOP的配置信息选择不同的创建代理对象的方式。在SpringAOP中创建代理对象没有直接依赖AopProxy,而是又抽象了一个AopProxyFactory的接口,通过这个接口(工厂模式)来创建代理对象。下面我们来看看具体的创建过程。在开篇的文章中我们写了这样的一段代码来获取代理对象:

 //aspectJProxyFactory是AspectJProxyFactory实例
 AspectJService proxyService = aspectJProxyFactory.getProxy();
 //从这段代码中我们可以看到这里又调用了createAopProxy()的方法
 //通过调用createAopProxy()生成的对象调用getProxy()方法生成代理对象
 public <T> T getProxy() {
        return (T) createAopProxy().getProxy();
}
//这是一个同步方法  ProxyCreatorSupport#createAopProxy
protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
        //这里会监听调用AdvisedSupportListener实现类的activated方法
        activate();
    }
    //获取AopProxyFactory
    //调用createAopProxy的时候传入了this对象
    return getAopProxyFactory().createAopProxy(this);
}
//在SpringAOP中 AopProxyFactory只有一个实现类,这个实现类就是DefaultAopProxyFactory
public AopProxyFactory getAopProxyFactory() {
    return this.aopProxyFactory;
}    

我们先看看一个关于Advised的UML类图:
Advised
从上面的图中我们可以看到AdvisedSupport继承了ProxyConfig类实现了Advised接口。如果你去翻看这两个类的代码的话,会发现在Advised中定义了一些列的方法,而在ProxyConfig中是对这些接口方法的一个实现,但是Advised和ProxyConfig却是互相独立的两个类。但是SpringAOP通过AdvisedSupport将他们适配到了一起。AdvisedSupport只有一个子类,这个子类就是ProxyCreatorSupport。从这两个类的名字我们可以看到他们的作用:一个是为Advised提供支持的类,一个是为代理对象的创建提供支持的类。还记得在Advised中有什么内容吗?
我们去DefaultAopProxyFactory中看一下调用createAopProxy(this)这个方法的时候发生了什么:

    //注意 这里传入了一个参数 AdvisedSupport   
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        //这段代码用来判断选择哪种创建代理对象的方式
        //config.isOptimize()   是否对代理类的生成使用策略优化 其作用是和isProxyTargetClass是一样的 默认为false
        //config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象 默认为false
        //hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是
        //SpringProxy类型 
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            //上面的三个方法有一个为true的话,则进入到这里
            //从AdvisedSupport中获取目标类 类对象
            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的方式生成代理对象
            //如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            //配置了使用Cglib进行动态代理  或者目标类没有接口 那么使用Cglib的方式创建代理对象
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            //上面的三个方法没有一个为true 那使用JDK的提供的代理方式生成代理对象
            return new JdkDynamicAopProxy(config);
        }
    }

我们先看JdkDynamicAopProxy生成代理对象的方法。在上面的代理中创建JdkDynamicAopProxy对象的时候,传入了AdvisedSupport对象。

    public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
        Assert.notNull(config, "AdvisedSupport must not be null");
        //如果不存在Advisor或者目标对象为空的话 抛出异常
        if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
            throw new AopConfigException("No advisors and no TargetSource specified");
        }
        this.advised = config;
    }
    //这两个方法的区别是否传入类加载器
    @Override
    public Object getProxy() {
        //使用默认的类加载器
        return getProxy(ClassUtils.getDefaultClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
        }
        //获取AdvisedSupport类型对象的所有接口
        Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
        //接口是否定义了 equals和hashcode方法 正常是没有的
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        //创建代理对象 this是JdkDynamicAopProxy  
        //JdkDynamicAopProxy 同时实现了InvocationHandler 接口
        //这里我们生成的代理对象可以向上造型为 任意 proxiedInterfaces 中的类型
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    
    //AopProxyUtils#completeProxiedInterfaces
    //这个方法主要是获取目标类上的接口 并且判断是否需要添加SpringProxy  Advised DecoratingProxy 接口
    static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
        //获取AdvisedSupport 类型中  目标类的接口
        Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
        //如果目标类没有实现接口的话,
        if (specifiedInterfaces.length == 0) {
            // No user-specified interfaces: check whether target class is an interface.
            //获取目标类
            Class<?> targetClass = advised.getTargetClass();
            if (targetClass != null) {
                //如果目标类是接口 则把目标类添加到 AdvisedSupport的接口集合中
                if (targetClass.isInterface()) {
                    advised.setInterfaces(targetClass);
                }
                //如果是Proxy类型
                else if (Proxy.isProxyClass(targetClass)) {
                    advised.setInterfaces(targetClass.getInterfaces());
                }
                //重新获取接口
                specifiedInterfaces = advised.getProxiedInterfaces();
            }
        }
        //接口中有没有 SpringProxy类型的接口
        //是否需要添加 SpringProxy接口
        boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
        //isOpaque 代表生成的代理是否避免转化为Advised类型 默认为false  如果目标类没有实现 Advised接口
        //是否需要添加 Advised接口
        boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
        //是否需要添加 DecoratingProxy接口
        boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
        int nonUserIfcCount = 0;
        if (addSpringProxy) {
            nonUserIfcCount++;
        }
        if (addAdvised) {
            nonUserIfcCount++;
        }
        if (addDecoratingProxy) {
            nonUserIfcCount++;
        }
        Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
        //扩展接口数组
        System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
        int index = specifiedInterfaces.length;
        if (addSpringProxy) {
            //为目标对象接口中添加 SpringProxy接口
            proxiedInterfaces[index] = SpringProxy.class;
            index++;
        }
        if (addAdvised) {
            //为目标对象接口中添加 Advised接口
            proxiedInterfaces[index] = Advised.class;
            index++;
        }
        if (addDecoratingProxy) {
            //为目标对象接口中添加 DecoratingProxy接口
            proxiedInterfaces[index] = DecoratingProxy.class;
        }
        return proxiedInterfaces;
    }
相关文章
|
30天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
37 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
416 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
38 1
|
1月前
|
前端开发 Java Spring
【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
【Spring】“请求“ 之传递单个参数、传递多个参数和传递对象
110 2
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
168 2
|
1月前
|
存储 Java 程序员
SpringIOC和DI的代码实现,Spring如何存取对象?@Controller、@Service、@Repository、@Component、@Configuration、@Bean DI详解
本文详细讲解了Spring框架中IOC容器如何存储和取出Bean对象,包括五大类注解(@Controller、@Service、@Repository、@Component、@Configuration)和方法注解@Bean的用法,以及DI(依赖注入)的三种注入方式:属性注入、构造方法注入和Setter注入,并分析了它们的优缺点。
28 0
SpringIOC和DI的代码实现,Spring如何存取对象?@Controller、@Service、@Repository、@Component、@Configuration、@Bean DI详解
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
102 5
|
2月前
Micronaut AOP与代理机制:实现应用功能增强,无需侵入式编程的秘诀
AOP(面向切面编程)能够帮助我们在不修改现有代码的前提下,为应用程序添加新的功能或行为。Micronaut框架中的AOP模块通过动态代理机制实现了这一目标。AOP将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,提高模块化程度。在Micronaut中,带有特定注解的类会在启动时生成代理对象,在运行时拦截方法调用并执行额外逻辑。例如,可以通过创建切面类并在目标类上添加注解来记录方法调用信息,从而在不侵入原有代码的情况下增强应用功能,提高代码的可维护性和可扩展性。
62 1
|
17天前
|
安全 Java 编译器
什么是AOP面向切面编程?怎么简单理解?
本文介绍了面向切面编程(AOP)的基本概念和原理,解释了如何通过分离横切关注点(如日志、事务管理等)来增强代码的模块化和可维护性。AOP的核心概念包括切面、连接点、切入点、通知和织入。文章还提供了一个使用Spring AOP的简单示例,展示了如何定义和应用切面。
52 1
什么是AOP面向切面编程?怎么简单理解?