Spring依赖注入时,创建代理bean和普通bean详解

news2024/11/15 18:39:20

问题来源

 以前一直有个疑惑,为什么我创建的controller中注入的service类有时候是代理类,有时候是普通javabean,当时能力不够,现在已经有了点经验就大胆跟了跟源码,看看到底咋回事。

首先看看问题现象:

a1:service是代理类,并且是CGLIB类型代理

 

a2:service是代理类,并且是jdk 动态代理

 b:serivce不是代理类,而是普通类 

问题分析

 我对service类进行了以下的测试:(前提开启事务注解<tx:annotation-driven/>)

  1)service方法添加@Transactional注解或者加入其它的aop拦截配置,没有实现任何接口。   对应问题现状 a1

  2)service方法添加@Transactional注解或者加入其它的aop拦截配置,实现了接口。              对应问题现状a2

  3)serice方法没有添加@Transactional注解或者其它的aop拦截配置。                                      对应问题现状b

 看来出现这种问题的原因就是spring的问题,因为这个类是它创建的,这就需要我们来看下spring创建bean的代码,由于spring太庞大了

我们只看最关键的部分,在创建bean是都会调用getBean()方法,

 @SuppressWarnings("unchecked")
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
      return createBean(beanName, mbd, args);
    }

      经过不断的流转会进入AbstractAutowireCapableBeanFactory的createBean方法

@Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {try {
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isDebugEnabled()) {
                logger.debug("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }

    }

然后调用doCreateBean方法

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {
if (instanceWrapper == null) {       
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        final Object bean = instanceWrapper.getWrappedInstance();
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }// Initialize the bean instance.
        Object exposedObject = bean;
        try {
            populateBean(beanName, mbd, instanceWrapper);
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }return exposedObject;
    }

然后进入核心的createBeanInstance方法,省去了不相关方法

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// No special handling: simply use no-arg constructor.
        return instantiateBean(beanName, mbd);
    }

然后调用instantiateBean进行bea的实例化

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
        try {
            Object beanInstance;
            final BeanFactory parent = this;
            if (System.getSecurityManager() != null) {
                beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
                        getInstantiationStrategy().instantiate(mbd, beanName, parent),
                        getAccessControlContext());
            }
            else {
                beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
            BeanWrapper bw = new BeanWrapperImpl(beanInstance);
            initBeanWrapper(bw);
            return bw;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
        }
    }

实例化时会调用SimpleInstantiationStrategy的instantiate方法

@Override
    public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
        // Don't override the class with CGLIB if no overrides.
        if (!bd.hasMethodOverrides()) {
            Constructor<?> constructorToUse;
            synchronized (bd.constructorArgumentLock) {
                constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
                if (constructorToUse == null) {
                    final Class<?> clazz = bd.getBeanClass();
                    if (clazz.isInterface()) {
                        throw new BeanInstantiationException(clazz, "Specified class is an interface");
                    }
                    try {
                        if (System.getSecurityManager() != null) {
                            constructorToUse = AccessController.doPrivileged(
                                    (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
                        }
                        else {
                            constructorToUse =    clazz.getDeclaredConstructor();
                        }
                        bd.resolvedConstructorOrFactoryMethod = constructorToUse;
                    }
                    catch (Throwable ex) {
                        throw new BeanInstantiationException(clazz, "No default constructor found", ex);
                    }
                }
            }
            return BeanUtils.instantiateClass(constructorToUse);
        }
        else {
            // Must generate CGLIB subclass.
            return instantiateWithMethodInjection(bd, beanName, owner);
        }
    }

该方法就是真正的实例化bean,根据不同情况通过CGLIB的方式

instantiateWithMethodInjection(bd, beanName, owner)

或者java的反射方式

BeanUtils.instantiateClass(constructorToUse)

实例化一个bean,这是时候都是一个纯洁无瑕的javabean,那每个bean的额外加工,例如为某个bean添加事务支持,

添加aop配置,还有就是将springmvc的controller进行url和handler的映射,等等这些都是在spring的扩展点完成的,回到

上面的doCreateBean方法

执行完实例化bean后执行

populateBean(beanName, mbd, instanceWrapper);

initializeBean(beanName, exposedObject, mbd);

其中的populateBean是为了给生成的bean装配属性,这不是我们这次讨论的重点,关键是initializebean方法

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

 这个方法就是对生成的bean进行一些扩展处理,主要是这个方法就,会调用我们自定义的扩展点

applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

可以看到这里是获取所有的beanProcessor,调用postProcessAfterInitialization方法,我们要关注是的一个叫InfrastructureAdvisorAutoProxyCreator的扩展类。

/**
 * Auto-proxy creator that considers infrastructure Advisor beans only,
 * ignoring any application-defined Advisors.
 *
 * @author Juergen Hoeller
 * @since 2.0.7
 */
@SuppressWarnings("serial")
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {}

看下这个类的注释可以发现这个类是为配置了aop配置(包括注解和xml配置两种方式)的类,生成代理类。核心方法是下面这个方法wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            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;
    }

下面解析下这个函数

首先看下getAdvicesAndAdvisorsForBean这个方法:名字很明显用来获取当前bean的advisor和adices的,这些都是生成代理类时需要的信息。

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

然后调用findEligibleAdvisors,获取配置的advisor信息

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

来看下findCandidateAdvisors方法,最终调BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // Do not initialize FactoryBeans here: We need to leave all regular beans
                // uninitialized to let the auto-proxy creator apply to them!
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<>();
        }

        List<Advisor> advisors = new LinkedList<>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    
                }
                else {
                    try {
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                      throw ex;
                    }
                }
            }
        }
        return advisors;
    }

1)首先获取spring管理的Advisor类型的类名称。

2)通过beanFactory获取该bean对应的实体类,并装入advisors。

生成的这个advisor可是相当复杂,这里我们以事务advisor为例说明

可以看到这个advisor包含了advice(aop中的通知),pointcut(aop中的切入点),

advice是TransactionInterceptor,这个通知是用来管理spring的事务的可以看到包含事务的管理器等管理事务的属性,具体的方法见TransactionAspectSupport.invokeWithinTransaction
pointcut是TransactionAttributeSourcePointcut。

public boolean matches(Method method, @Nullable Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

这个是pointcut的核心方法,用来匹配某个类是否符合事务管理的aop拦截要求。
ok,回到之前的wrapIfNecessary方法

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            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;
    }

我们之前分析道getAdvicesAndAdvisorsForBean方法,可以看到如果得到的结果是DO_NOT_PROXY,就会将这个bean直接返回,

如果不是DO_NOT_PROXY,(其实DO_NOT_PROXY就是null,但是使用DO_NOT_PROXY会使得代码逻辑更加清晰),就会执行

createProxy方法,创建一个代理类,然后返回一个代理类,ok,现在我们就清楚了问题分析中的 第3)和第 1) 2) 区别,那就是

service类是否配置了相关的aop拦截配置,无论是注解还是xml形式,目前我们还不清楚第1)和 第2)的区别,就是为什么有时候

生成jdk代理,有时候生成cglib代理,这就需要继续向下看creatProxy方法了,最终会进入一个DefaultAopProxyFactory的createAopProxy

方法:

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            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.");
            }
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

如果目标类是接口就一定会使用jdk代理,如果目标类没有可以代理的接口就一定会使用Cglib代理。

问题总结

这个问题我们现在知道了,那他有什么意义呢,换句话说,我们为什么要知道这个,即使不知道原理,我们也可以去搜搜去解决,在我看来

把他弄明白的过程学会了很多知识,而且我们如果在工作过程中遇到了需要扩展的地方,我们可以很容易的去解决。

最后欢迎大家在评论区留言,有什么想法说出来,共同进步。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/150452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux nfs umount报错:device is busy

执行nfs卸载命令umount /mnt&#xff0c;报错target is busy. 或device is busy可以按以下步骤检查&#xff1a;退出要卸载挂载的目录&#xff0c;再执行卸载挂载cd ../umount /mnt找出占用目录的端口&#xff0c;kill端口fuser -m /mnt/kill -9 端口umount /mnt停止nfs服务&am…

PCA 主成分分析-清晰详细又易懂

PCA&#xff08;Principal Component Analysis&#xff09;通过线性变换将原始数据变换为一组各维度线性无关的表示&#xff0c;可用于提取数据的主要特征分量&#xff0c;常用于高维数据的降维。 当然我并不打算把文章写成纯数学文章&#xff0c;而是希望用直观和易懂的方式叙…

Java char[]数组转成String类型(char to String)详细介绍

前言 string toCharArray() 方法将给定的字符串转换为字符序列 Java中字符串转换为字符数组的方法在之前的博客已经介绍了&#xff01; 今天介绍char[]数组转成String 方法有4种&#xff1a; 使用 String 类的 valueOf() 方法使用字符串连接使用 Character 类的 toString() 方…

图形编辑器:场景坐标、视口坐标以及它们之间的转换

大家好&#xff0c;我是前端西瓜哥。 图形编辑器的坐标系有两种。 一个是场景&#xff08;scene&#xff09;坐标系&#xff0c;一个是 视口&#xff08;viewport&#xff09;坐标系。视口就是场景的一个子区域。 假设我们的视口的原点&#xff0c;离场景原点的坐标水平和垂直…

C2芯片一ESP32-C2开发板

C2是一个芯片采用4毫米x 4毫米封装&#xff0c;与272 kB内存。它运行框架&#xff0c;例如ESP-Jumpstart和ESP造雨者&#xff0c;同时它也运行ESP-IDF。ESP-IDF是Espressif面向嵌入式物联网设备的开源实时操作系统&#xff0c;受到了全球用户的信赖。它由支持Espressif以及所有…

月报总结|Moonbeam 12月份大事一览

本月&#xff0c;针对生态和项目&#xff0c;Moonbeam基金会启动首期Accelerator Program孵化计划&#xff0c;将针对入选团队提供一系列扶持资源&#xff0c;申请仍在开放中。对于开发者&#xff0c;Moonbuilders Academy上线了关于构建跨链应用的新课程。 社区活动方面&…

【案例分析】汽车制造行业电能质量治理方案分析

摘要&#xff1a;现如今的汽车制造行业&#xff0c;使用的机械设备越来越精密&#xff0c;制造技术与自动化水平也越来越高&#xff0c;为此对供电系统的电能质量要求更高更苛刻&#xff0c;同时对不能满足现有生产工艺需求的供电质量进行治理。通过分析汽车制造过程中冲压工艺…

连续三年!Fortinet再次位列《 Gartner 企业级有线和无线局域网基础设施魔力象限报告》“远见者”

网络安全领导者Fortinet&#xff08;NASDAQ&#xff1a;FTNT&#xff09;&#xff0c;近日宣布连续第三年入围《Gartner企业级有线和无线局域网基础设施魔力象限报告》“远见者”象限。这一殊荣源于Fortinet 旗下FortiSwitch 和 FortiAP等有线和无线局域网产品组合的强劲实力&a…

激光焊接薄板时需要注意的一些问题

焊接是目前工业制造中必要的工艺技术&#xff0c;焊接金属时需要注意很多方面的问题&#xff0c;本文主要针对金属薄板焊接中的一些工艺问题进行展开讨论。例如&#xff1a;焊缝的牢固程度、焊缝的平整度、焊缝直线度的控制、焊缝高度和宽度的控制以及焊接的均匀性等方面。 一、…

程序员们有什么好的编程习惯?

优良的代码显然不是制作优秀软件的唯一要素&#xff0c;但是主要的要素之一。我们可能拥有世界上最好的产品和营销团队&#xff0c;部署了最好的平台&#xff0c;并以最好的框架来构建软件&#xff0c;但归根结底&#xff0c;一款软件所做的一切&#xff0c;都是因为有人写了一…

LeetCode 49 字母异位词分组 | 解题思路分享

原题链接&#xff1a;49. 字母异位词分组 - 力扣&#xff08;LeetCode&#xff09; 题目难度&#xff1a;中等 题目描述 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的字母得到的一个新单词…

分析快、易操作的数据分析工具推荐

数据分析工具发展这么多年&#xff0c;该有的技术功能都有了&#xff0c;该提高的数据分析效率、数据分析量等也都提高了&#xff0c;但很多长期奋战在一线的数据分析人员却总是抱怨数据分析工具响应慢、分析慢、越来越容易崩溃。为什么要找一款分析快、易操作的数据分析工具还…

Git入门学习笔记(10分钟速通)

一、Git概述 官网翻译&#xff1a; Git 是一个免费的开源 分布式版本控制系统&#xff0c;旨在快速高效地处理从小型项目到大型项目的所有内容。 Git易于学习&#xff0c; 体积小&#xff0c;性能快如闪电。它超越了 Subversion、CVS、Perforce 和 ClearCase 等 SCM 工具&am…

13_2、Java的IO流概述

一、Java IO原理1、I/O是Input/Output的缩写&#xff0c; I/O技术是非常实用的技术&#xff0c;用于处理设备之间的数据传输。如读/写文件&#xff0c;网络通讯等。2、Java程序中&#xff0c;对于数据的输入/输出操作以“流(stream)” 的方式进行。3、java.io包下提供了各种“流…

kaggle平台学习复习笔记 | XGBoost、LightGBM and Catboost

这里写目录1.XGBoost官方文档介绍与使用2.LightGBM官方文档介绍与使用3.CatBoost官方文档介绍与使用对比数据预处理如下&#xff0c;下文不再重复 import lightgbm as lgb import xgboost as xgb from catboost import CatBoostRegressorfrom sklearn.model_selection import …

C#入门级——泛型、泛型类、泛型接口、泛型方法和泛型委托

目录 一、泛型&#xff08;generic&#xff09; 二、为什么需要泛型类 类型膨胀 成员膨胀 使用object类 三、泛型的定义 定义泛型类 使用泛型类 泛型接口​​​​​​​ 两种泛型接口的实现方法 泛型方法 成员膨胀 使用泛型 泛型委托 Action委托——只能引用没有…

有效的需求管理,需遵循四大原则。

1、需求管理必须与需求工程活动相整合 需求管理必须与需求工程的其他活动紧密整合&#xff0c;进行需求管理一定不能脱离需求工程&#xff0c;需求工程包括了需求获取、需求分析、需求描述、需求验证、需求管理&#xff0c;因而需求管理必须与前面的几个需求阶段保持密切相关。…

2023/1/9 Vue学习笔记-5-TodoList案例

1 静态组件 App.vue <template><div class"todo-container"><div class"todo-wrap"><UserHeader/><UserList/><UserFooter/></div></div> </template> <script>import UserHeader from &qu…

【计组】FPGA和ASIC--《深入浅出计算机组成原理》(七)

课程链接&#xff1a;深入浅出计算机组成原理_组成原理_计算机基础-极客时间 目录 一、FPGA &#xff08;一&#xff09;FPGA 的解决方案步骤 1、用存储换功能实现组合逻辑 2、对于需要实现的时序逻辑电路&#xff0c;在 FPGA 里面直接放上 D 触发器&#xff0c;作为寄存…

工业清洗企业资质证书

工业清洗在美国、日本、新加坡、西欧等国发展较早&#xff0c;已经建立起专业化程度很高的化学清洗体系。我国的工业清洗发展很快&#xff0c;目前已经初步形成了新兴的清洗产业网络&#xff0c;清洗技术也已达到国际先进水平&#xff0c;具备了清洗大型设备的能力和经验。 工业…