精华内容
下载资源
问答
  • spring源码系列(二)——毁三观的spring自动注入

    万次阅读 多人点赞 2019-10-11 13:37:42
    比如提到spring自动注入作为一个java程序员肯定自信无比了解;但是笔者要说的自动注入可能会和你理解有很大出入。 首先搞明白什么是自动注入自动注入也可以叫做自动装配(springboot也有一个自动装配但是我认为...

    比如提到spring的自动注入作为一个java程序员肯定自信无比了解;但是笔者要说的自动注入可能会和你理解有很大出入。
    首先搞明白什么是自动注入,自动注入也可以叫做自动装配(springboot也有一个自动装配但是我认为翻译的不够准确,springboot的应该叫做自动配置和这里说的自动注入是两回事,笔者不是什么大牛或者权威;所以读者如果你坚持认为springboot也叫自动装配那也无可厚非,只是在这篇文章里面所谓的自动注入就是自动装配,关于springboot的自动配置我以后更新文章再来说);
    自动注入需要相对于手动装配来说;在spring应用程序当中假设你的A类依赖了B类,需要在A类当中提供一个B类的属性,再加上setter,继而在xml当中配置、描述一下这两个类之间的依赖关系。如果做完当容器初始化过程中会实例化A,在实例化A的过程中会填充属性,由于在xml中已经配置、描述好两者的关系,故而spring会把B给A装配上;这种由程序员自己配置、描述好依赖关系的写法叫做手动装配;看个例子吧;

    package com.luban.app;
    
    public class A {
    	B b;
    	public void setB(B b) {
    		this.b = b;
    	}
    }
    
    
    
    package com.luban.app;
    
    public class B {
    
    }
    
    
    
    
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    		<bean id="a" class="com.luban.app.A" >
    			<!-- 由程序员手动指定的依赖关系 称为手动装配-->
    			<property name="b">
    				<ref bean="b" />
    			</property>
    		</bean>
    
    		<bean id="b"  class="com.luban.app.B">
    		</bean>
    </beans>
    
    

    但是实际开发中手动装配的场景比较少(比如在缺少源码的情况下可能会使用这种手动装配情况);
    关于依赖注入的资料可以参考官网
    https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-dependencies
    这一章节提到了一个非常重要的知识点,也是一个常见的spring面试题目。spring有几种依赖注入方式?那么这个问题应该怎么回答呢?

    在这里插入图片描述

    官网的意思是DI(依赖注入)一共有两种主要的变体(注意会考),分别是基于构造方法的依赖注入和基于setter(setXxxx(…))的依赖注入,不管是手动装配还是自动装配都是基于这两种方式或者变体方式来的;但是这里一定要回答到主要和变体两个名词,因为有的注入方式就不是这两种,而是这两种其中一种的变体方式;比如在一个类的属性上面加@Autowired,这种方式注入属性的方式就是利用了java的反射知识,field.set(value,targetObject);关于这个我在后面的文章中对spring源码解析的时候会说明@Autowired的原理;所以@Autowired这种注入的方式是setter注入方式的一种变体
    但是这里需要说明的是所谓的setter其实和属性无关,什么意思呢?一般的setter方法会对应一个属性,但是spring的基于setter的注入方式是不需要属性的,仅仅只需要一个setter方法,下面这个例子来说明这个问题

    
    B.java
    
    public class B {
    
    }
    
    
    A.java
    
    public class A {
    	public void setXxx(B b) {
    		System.out.println("spring 找到符合的setter");
    		System.out.println("和属性无关,甚至可以不要属性");
    		System.out.println("可以直接调用,这个A里面就没有任何属性");
    	}
    }
    
    xml配置文件
    
    <bean id="a" class="com.luban.app.A" >
    	<!-- 由程序员手动指定的依赖关系 称为手动装配-->
    	<property name="xxx">
    		<ref bean="b" />
    	</property>
    </bean>
    
    <bean id="b"  class="com.luban.app.B">
    </bean>
    

    运行上面的代码可以看到spring也会调用这个setXxx方法,如果仔细观察调用栈可以看到这个方法是在spring容器初始化的时候实例化A,完成A的注入功能时候调用过来的,下图是笔者运行结果的截图

    在这里插入图片描述

    可能有的读者会认为上面的xml配置文件中已经手动装配了B给A,肯定会调用setXxx方法,其实不然,即是我使用自动装配也还是会调用的(关于什么是自动装配,下文会详细介绍),因为前文说过,注入方式与手动注入、自动注入无关。笔者改一下代码,把A的注入模型改成自动注入来看看结果

    <!-- 程序员不指定装配的具体参数,容器自动查询后装配-->
    <bean id="a" class="com.luban.app.A" autowire="byType">
    	
    </bean>
    
    <bean id="b"  class="com.luban.app.B">
    </bean>
    

    把代码改成上面这样自动装配结果是一样的,A类当中的setXxx方法还是会调用,不需要提供任何属性,至于原理笔者后面更新到spring源码的时候再来详细说道;可能有读者会说如果使用注解呢?比如如下代码

    A.java
    
    @Component
    public class A {
    	@Autowired
    	B b;
    	public void setB(B b) {
    		System.out.println("如果你使用注解,这个setter变得毫无意义");
    		System.out.println("这个方法甚至都不会调用");
    		System.out.println("因为前文说过这种方法用的是field.set");
    	}
    }
    
    B.java
    
    import org.springframework.stereotype.Component;
    @Component
    public class B {
    
    }
    
    

    上面代码同样会注入b,但是是调用field.set去完成注入的,不是通过setter,当然再次说明一下这是setter的一种变体;上面代码直到A完成注入B后setter方法也不会调用;

    重点来了,要考。笔者看过很多资料说@Autowired也算自动装配,关于这点笔者不敢苟同,在一个属性上面加@Autowired注解应该属于手动装配,我会通过大量源码和例子来证明笔者的这个理论。
    之所以会有人把@Autowired也理解为自动装配的原因是因为bytype引起的,因为spring官网有说明自动装配有四种模型分表是no、bytype、byname、constructor;参考官网资料,而现在流行着这么一种说法:@Autowired就是通过bytype来完成注入的。如果你也认为这种说法成立,那么就是默认了@Autowired是自动装配且装配模型是bytype。笔者见过很多程序员和很多资料都支持这种说法,其实严格意义上来讲这句话大错特错,甚至有误人子弟的嫌疑。因为bytype仅仅是一种自动注入模型而已,这种模型有其固有的技术手段,而@Autowired是一个注解,这个注解会被spring的后置处理器解析,和处理bytype不是同一回事。如果需要讲清楚他们的区别和证明@Autowired不是自动装配则首先要搞明白什么自动装配。笔者接下来会花一定篇幅来解释自动装配的知识,然后回过头来讲他们的区别和证明@Autowired属性手动装配。

    如果现在已经理解了手动装配也叫手动注入,也已经理解了注入方式(setter和构造方法),那么接下来讨论自动注入或者叫自动装配;自动注入的出现是因为手动装配过于麻烦,比如某个类X当中依赖了10个其他类那么配置文件将会变的特别冗余和臃肿,spring的做法是可以为这个X类提供一种叫做自动装配的模型,无需程序员去手动配置X类的依赖关系。有读者会疑问,用注解不也是可以解决这个xml臃肿的问题?确实用注解可以解决,但是我们现在讨论的是自动装配的问题,就不能用注解;为什么不能用注解来讨论自动装配的问题呢?因为在不配置BeanFactoryPostProcessor和修改beanDefinition的情况下注解的类是不支持自动装配的(关于BeanFactoryPostProcessor和beanDefinition的知识笔者以后有时间更新);这也是证明@Autowired默认不是自动装配的一个证据,那又如何证明注解类是默认不支持自动装配呢?下文我会解释一个注解类默认是不支持自动装配的。也就是说如果讨论自动装配最好是用xml形式来配置spring容器才会有意义;当然读者如果对spring比较精通其实通过修改注解类的beanDefinition来说明自动装配的问题,鉴于大部分读者对spring不精通故而笔者还是用xml来讨论吧;比如下面这个例子

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd"
    		default-autowire="byType">
    		<!--default-autowire="byType"-->
    		<!-- 程序员不指定装配的具体参数,容器自动查询后装配-->
    		<!--当然除了这种写法还可以直接在bean标签上为特定类指定自动装配模型-->
    		<bean id="a" class="com.luban.app.A">
    
    		</bean>
    
    		<bean id="b"  class="com.luban.app.B">
    		</bean>
    </beans>
    
    
    A.java
    
    
    
    public class A {
    	B b;
    	public void setB(B b) {
    		System.out.println("在不考虑注解的情况下");
    		System.out.println("如果配置了自动装配则不需要手动配置");
    	}
    }
    
    B.java
    
    public class B {
    
    }
    
    
    

    上面代码运行起来,A能注入B,但是在xml配置文件中并没有去手动维护、描述他们之间的依赖关系,而是在xml的根标签上面写了一行default-autowire=“byType”,其实关于自动注入的歧义或者被人误解的地方就是这个default-autowire="byType"引起的;那么这行代码表示什么意思呢?表示所在配置在当前xml当中的bean都以bytype这种模式自动装配(如果没有特殊指定,因为bean还可以单独配置装配模式的);这需要注意笔者的措辞,笔者说的bytype这自动装配模式,是一种模式,这个不是笔者信口开河,因为在官网文档里面spring也是这么定义的
    首先官网介绍beanDefinition这一章节的时候有提到bean的Autowiring mode

    在这里插入图片描述

    再就是spring官网在介绍spring自动注入知识的章节也提到过这个名词

    在这里插入图片描述

    Autowiring mode 笔者姑且翻译为自动注入模型吧,如果有211、雅思读者可以给读者留言更合适的翻译
    那为什么要锱铢在这个名词上呢?笔者觉得真的非常重要,很多初学spring的程序员都是粗学spring,忽略甚至混淆了很多关键的名词定义,导致很多观念根深蒂固后面想深入学习spring源码的时候就比较困难
    这个名字叫做自动注入模型和前面提到的依赖注入方式(setter和构造方法)是两回事,简而言之:依赖注入是一个过程,主要通过setter和构造方法以及一些变体的方式完成把对象依赖、或者填充上的这个过程叫做依赖注入,不管手动装配还是自动装配都有这个过程;而自动装配模型是一种完成自动装配依赖的手段体现,每一种模型都使用了不同的技术去查找和填充bean;而从spring官网上面可以看到spring只提出了4中自动装配模型(严格意义上是三种、因为第一种是no,表示不使用自动装配、使用),这四个模型分别用一个整形来表示,存在spring的beanDefinition当中,任何一个类默认是no这个装配模型,也就是一个被注解的类默认的装配模型是no也就是手动装配;其中no用0来表示;bytype用2来表示;如果某个类X,假设X的bean对应的beanDefinition当中的autowireMode=2则表示这个类X的自动装配模型为bytype;如果autowireMode=1则表示为byname装配模型;需要注意的是官网上面说的四种注入模型其中并没有我们熟悉的@Autowired,这也再一次说明@Autowired不是自动装配;可能有读者会提出假设我在A类的某个属性上面加上@Autowired之后这个A类就会不会成了自动装配呢?@Autowired是不是会改变这个类A当中的autowireMode呢?我们可以写一个例子来证明一下

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd"
    	   default-autowire="byType">
    
    		<bean id="a" class="com.luban.app.A">
    		</bean>
    		<bean id="b"  class="com.luban.app.B">
    		</bean>
    </beans>
    
    xml配置了A 和B 都是自动装配模型为bytype讲道理要实现autowireMode=2
    
    A.java
    public class A {
    	
    }
    
    B.java
    public class B {
    	
    }
    
    
    
    提供一个后置处理器来获取A的自动装配模型
    ZiluBeanFactoryPostprocessor.java
    @Component
    public class ZiluBeanFactoryPostprocessor implements BeanFactoryPostProcessor {
    	@Override
    	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    		GenericBeanDefinition a = (GenericBeanDefinition)
    				beanFactory.getBeanDefinition("a");
    		//打印A 的注入模型
    		System.out.println("a mode="+a.getAutowireMode());
    
    	}
    }
    
    
    

    讲道理由于是bytype所以应该打印出来2; 结果如图

    在这里插入图片描述

    如果笔者把注入模型改成byname则结果应该会改变

    在这里插入图片描述

    接下来笔者验证一个通过注解配置的类加上@Autowried后的注入模型的值

    在这里插入图片描述

    可以看到结果为0,说明这个A类不是自动装配,其实这已经能证明@Autowried不是自动装配了,但是还有更直接的证据证明他不是自动装配,就是通过spring源码当中处理@Autowried的代码可以看出,首先我们写一个bytype的例子看看spring如何处理的

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd"
    	   default-autowire="byType">
    		<!--自动装配-->
    		<bean id="a" class="com.luban.app.A">
    		</bean>
    		<bean id="b"  class="com.luban.app.B">
    		</bean>
    </beans>
    
    
    
    A.java
    
    public class A {
    
    	B b;
    
    	/**
    	 * 如果是自动装配需要提供setter
    	 * 或者提供构造方法
    	 */
    	public void setB(B b) {
    		this.b = b;
    	}
    }
    
    B.java
    public class B {
    
    }
    
    
    

    上面代码运行起来,调试spring源码当中的org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean方法,这个方法主要就是完成属性填充的,也就是大家说的注入

    在这里插入图片描述

    可以看上图1399行有一个判断,判断当前类的注入模式是否bynane或者bytype如果是其中一种则会进入1401行代码执行自动装配的逻辑;因为当前代码我在xml当中配置了自动注入模型为bytype所以这里一定会进入,从上图debug的结果我们可以得知确实也进入了1401行,那我们再看看@Autowried到底是否是一样的呢?好吧作为良心笔者特意录了一个gif

    在这里插入图片描述

    从上面那个笔者良心录制的gif可以看到当我们使用@Autowired注解去注入一个属性的时候spring在完成属性注入的过程中和自动注入(byname、bytype)的过程不同,spring注解跳过了那个判断,因为不成立,而是用后面的代码去完成属性注入;这也是能说明@Autowired不是自动装配的证据、更是直接打脸@Autowired是先bytype的这种说法;当然除了这个证据还有更加直接的证据,先看代码

    @Component
    public class A {
    	B b;
    	public  A(B b){
    		this.b=b;
    		System.out.println(b);
    		System.out.println("这个b能正常打印,那这算不算自动装配呢?");
    		System.out.println("看起来像是constructor这种自动装配模型");
    		System.out.println("但是实际呢?我们通过源码来说明");
    		System.out.println("这个构造方法没有加任何注解");
    		System.out.println("这里主要来说明,一个注解类是不是自动装配");
    
    	}
    }
    

    说明一下这个A类的构造方法注入的B是可以注入的,这样算不算注解类的自动注入呢?看起来像,其实本质上他用的也是自动注入的代码实现的,但是笔者还是认为他不是;为什么呢?我们先来分析一下这个b能被注入的原因是什么,同样给一个gif来说明
    在这里插入图片描述

    其实最关键的就是这行代码
    if (ctors != null || mbd.getResolvedAutowireMode() ==
    AUTOWIRE_CONSTRUCTOR ||
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args);
    }
    这里有个判断 首先判断 ctors != null 然后判断类的自动注入模型是不是等于AUTOWIRE_CONSTRUCTOR也是3;分两种情况来分析;

    第一种情况:假设你指定了类的自动注入模型为constructor那么这个if一定成立因为他是||判断只要一个成立整个if成立;很显然我们这里不是这种情况,笔者并没有指定A类的自动注入模型为3,上面的那个gif里面我给读者展示了,后面那个判断不成立,因为mbd.getResolvedAutowireMode()=0;再一次说明了一个注解类默认的自动注入模型为no也就是autowireMode=0;

    第二种情况:没有指定类的自动注入模型,笔者代码例子就是这种情况,那么spring会首先推断出构造方法,笔者A里面一个带参数的构造方法,所以再进行第一个判断ctors
    != null的时候就已经成立了,于是也会进入

    结论:在一个注解类里面提供了一个构造方法之所以能达到和自动注入的效果一样并不是因为这种方式就是自动装配,而是因为spring源码当中做了判断;使这种情况下调用的代码和自动装配调用的逻辑一下。但是有的读者会说那这样也能算自动装配啊,当然如果读者一定这么认为那么也无可厚非;仁者见仁智者见智

    那哔哩哔哩说了这么多@Autowried到底和bytype有什么关系呢?为什么有资料会把他们联系在一起呢?
    首先bytype是一种自动注入模型,spring会更加类型去查找一个符合条件的bean如果没找到则不注入,程序也不会报错;如果找到多个spring也不报错,但是也不完成注入,让这个属性等于null,比如你有个接口I,提供两个实现I1和I2都存在容器当中,然后在A类当中去注入I,并且指定自动注入模型为bytype那么这个时候会找到两个符合条件的bean,spring就迷茫了,注入哪个呢?spring哪个都不注入直接让这个属性为null而且也不出异常;但是如果找到了一个那么注入的时候会调用setter,如果没有提供setter就不会注入;
    在这里插入图片描述
    其次@Autowried是一个注解,加在属性上面spring,

    spring会首先根据属性的类型去容器中找,如果没有找到在根据属性的名字找,找到了则注入,没有找到则异常,下图结果就是容器当中没有一个符合的类型和名字都不符合的则异常;
    在这里插入图片描述

    如果先根据类型找到了一个,那么直接注入,下图就是只有一个符合要求的类型能够完美注入;
    在这里插入图片描述

    如果先根据类型找到了多个,那么spring不会立马异常,而是根据名字再找去找,如果根据名字找到一个合理的则注入这个合理的,下图就是I1和I2都符合,但是spring最后却没有异常,是因为属性名字叫i1故而先类型在名字找到了合理的;

    在这里插入图片描述

    如果先根据类型找到了多个,那么spring不会立马异常,而是根据名字再找去找,如果根据名字还是没有找到那么时候spring会异常,下图就是两个i1和i2都符合,于是spring通过名字找i3也找不到,故而出异常;
    在这里插入图片描述

    以后如果再听到@Autowried是bytype请你纠正他,bytype是一种自动注入模型;@Autowried是一个注解,两个人没有关系,一点关系都没有;@Autowried讲道理算是手动装配;那么一个注解的类到底能不能开启自动装配呢?答案是可以的,但是需要你对spring比较精通,以后笔者再更新;

    展开全文
  • Spring自动注入原理

    千次阅读 2020-05-21 17:00:33
    spring的属性注入属于spring bean的生命周期一部分,bean的生命周期首先记住两个概念: spring bean:最终存在spring容器当中的对象 对象:实例化出来的对象,但是一个对象并不一定是spring bean 所谓的bean的生命...

    我的博客

    spring的属性注入属于spring bean的生命周期一部分,bean的生命周期首先记住两个概念:

    1. spring bean:最终存在spring容器当中的对象
    2. 对象:实例化出来的对象,但是一个对象并不一定是spring bean

    所谓的bean的生命周期就是磁盘上的类通过spring扫描,然后实例化,属性注入,跟着初始化,继而放到容器当中的大概过程

    finishBeanFactoryInitialization

    通过finishBeanFactoryInitialization初始化我们的单例非懒加载的Bean,创建一个Z.java来看看

    首先实例化ApplicationContext容器对象

    在这里插入图片描述

    会调用构造方法里的refresh来初始化容器

    在这里插入图片描述

    执行完前后可以看到,在方法finishBeanFactoryInitialization里,Spring已经帮我们创建好了Bean,也就是bean的生命周期都在此方法内

    在这里插入图片描述
    在这里插入图片描述

    通过此方法内的注释可以知道,接着会执行finishBeanFactoryInitialization#beanFactory.preInstantiateSingletons().doGetBean(beanName);方法

    doGetBean

    doGetBean方法内容有点多,这个方法非常重要,整个spring bean生命周期中这个方法有着举足轻重的地位,开始分析doGetBean的执行流程

    第一个getSingleton

    在这里插入图片描述

    getSingleton(beanName) 是实现自动注入最主要的方法,spring初始化bean的时候先判断bean是否在容器当中,如果存在,直接返回,如不存在则生产。同时,这个方法也是API getBean(beanName) 的底层实现。
    spring在初始化的时候容器当中肯定是没有Z的,为什么还需要去判断一下呢?因为一个bean被put到单例池(容器)的渠道有很多;除了spring容器初始化—扫描类----实例化-----put到容器这条线之外还有很多方法可以把一个对象put到单例池,比如:

    在这里插入图片描述

    这里是第一次从单例池中拿Z,肯定是null,所以 singletonObject == null 成立

    在这里插入图片描述

    判断Z是否在正在创建的集合里,第一次肯定是不在的,isSingletonCurrentlyInCreation == false 不成立

    在这里插入图片描述

    以上判断完成,会继续往下面走,继续实例化,然后下面又调用了一次getSingleton,上面也调用了一次getSingleton,两个getSingleton方法并不是同一个方法,这是方法重载,第二次getSingleton就会把bean创建出来

    在这里插入图片描述

    第二个getSingleton

    第二次getSingleton就会把我们bean创建出来,换言之整个bean如何被初始化的都是在这个方法里面

    在这里插入图片描述

    当spring觉得可以着手来创建bean的时候首先便是调用beforeSingletonCreation(beanName); 判断当前正在实例化的bean是否存在正在创建的集合当中

    在这里插入图片描述

    当代码运行完this.singletonsCurrentlyInCreation.add(beanName)之后可以看到singletonsCurrentlyInCreation集合当中只存在一个Z,并且没有执行z的构造方法,说明spring仅仅是把Z添加到正在创建的集合当中,但是并没有完成bean的创建(因为连构造方法都没调用)

    当运行完this.singletonsCurrentlyInCreation.add(beanName) 之后结果大概如下图这样

    在这里插入图片描述

    之后就会走到singletonObject = singletonFactory.getObject();这个调用的是createBean(beanName, mbd, args) 方法;把创建好的spring bean返回出来;至此第二次getSingleton方法结束

    在这里插入图片描述

    createBean

    createBean()方法中调用了doCreateBean方法创建spring bean

    在这里插入图片描述

    doCreateBean

    createBeanInstance:创建对象

    运行完createBeanInstance之后控制台打印了Z的构造方法的内容,说明Z对象已经被创建了,但是这个时候的Z不是spring bean,因为spring bean的生命周期才刚刚开始

    在这里插入图片描述

    把前面知识串起来,画一下当前代码的语境

    在这里插入图片描述

    这个createBeanInstance方法是如何把对象创建出来的呢?大致流程是:

    1. 如果是自动装配,推断出来各种候选的构造方法
    2. 利用推断出来的候选构造方法去实例化对象
    3. 如果没有推断出合适的构造方法(或者没有提供特殊的构造方法),则使用默认的构造方法
    4. 利用默认的构造方法,使用反射去实例化对象

    populateBean:属性自动注入

    实例化完成后,开始执行方法populateBean完成属性自动注入,我们在Z里面添加自动注入X

    在这里插入图片描述

    首先看一下方法执行前的情况

    在这里插入图片描述

    没有执行populateBean之前只实例化了Z,X并没实例化;接下来看看执行完这行代码之后的情况

    在这里插入图片描述

    Z 填充 X (简称 zpx)首先肯定需要获取X,调用getBean(x),getBean的本质上文已经分析过,就是调用getSingleton(beanName),进入到第一次调用getSingleton。第一次getSingleton会从单例池获取一下X,如果X没有存在单例池则开始创建X,创建X的流程和创建Z一模一样,都会走bean的生命周期;比如把X添加到正在创建的bean的集合当中,推断构造方法,实例化X

    {% qnimg Spring自动注入源码/17.png %}

    这样就完成了X的自动注入

    循环依赖

    Z 填充 X,但是X对象里又自动注入Z

    @Component
    public class Z {
    
        @Autowired
        private X x;
    
        public Z() {
            System.out.println("z create");
        }
    
    }
    
    @Component
    public class X {
    
        @Autowired
        private Z z;
    
        public X() {
            System.out.println("x create");
        }
    
    }
    

    是否能够获取到Z呢?首先我们想如果获取失败则又要创建z—>实例化z—填充属性----获取x----就无限循环了;所以结果是完成了循环依赖,所以这里肯定能够获取到Z,为什么呢?
    联系上文第一次调用getSingleton是无法获取到Z的?因为上面说过第一次调用getSingleton是从单例池当中获取一个bean,但是Z显然没有完成生命周期(Z只走到了填充X,还有很多生命周期没走完)
    所以应该是获取不到的?为了搞清楚这个原因得去查看第一次getSingleton的源码

    在这里插入图片描述

    总结:
    首先spring从单例池当中获取Z,前面说过获取不到,然后判断是否在正在创建bean的集合当中,前面分析过这个集合现在存在Z和X;所以if成立进入分支;进入分支,spring直接从三级缓存中获取Z,根据前面的分析三级缓存当中现在什么都没有,故而返回null
    进入下一个if分支,从二级缓存中获取一个ObjectFactory工厂对象;二级缓存中存在Z和X,故而可以获取到;跟着调用singletonFactory.getObject();拿到一个半成品的bean Z对象;然后把Z对象放到三级缓存,同时把二级缓存中Z清除(此时二级缓存中只存在一个X了,而三级缓存中多了一个Z)

    问题:

    1. 为什么首先是从三级缓存中取呢?主要是为了性能,因为三级缓存中存的是一个Z对象,如果能取到则不去二级找了
    2. 为什么一开始不直接存三级缓存呢?如果直接存到三级缓存,只能存一个对象,假设以前存这个对象的时候这对象的状态为za,但是我们这里X要注入的z为zc状态,那么则无法满足
    3. 二级有什么用呢?为什么一开始要存工厂呢?但是如果存一个工厂,工厂根据情况产生任意za或者zb或者zc等等情况;比如说AOP的情况下z注入x,x也注入z,而x中注入的z需要加代理(AOP),但是加代理的逻辑在注入属性之后,也就是z的生命周期走到注入属性的时候z还不是一个代理对象,那么这个时候把z存起来,然后注入x,获取、创建x,x注入z,获取z;拿出来的z是一个没有代理的对象;但是如果存的是个工厂就不一样;首先把一个能产生z的工厂存起来,然后注入x,注入x的时候获取、创建z,x注入z,获取z,先从三级缓存获取,为null,然后从二级缓存拿到一个工厂,调用工厂的getObject();spring在getObject方法中判断这个时候z被AOP配置了故而需要返回一个代理的z出来注入给x,总之getObject会根据情况返回一个z,但是这个z是什么状态,spring会自己根据情况返回
    4. 为什么要remove二级缓存?如果存在比较复杂的循环依赖可以提高性能;比如x,y,z相互循环依赖,那么第一次x注入z的时候从二级缓存通过工厂返回了一个z,放到了三级缓存,而第二次y注入z的时候便不需要再通过工厂去获得z对象了.因为if分支里面首先是访问三级缓存;至于remove则是为了gc吧

    initializeBean:初始化

    在这里插入图片描述

    InitializingBean 对应生命周期的初始化阶段,实例化和属性赋值都是Spring帮助我们做的,能够自己实现的有初始化和销毁两个生命周期阶段.Aware方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式

    在这里插入图片描述

    这几种定义方式的调用顺序没有必要记.因为这几个方法对应的都是同一个生命周期,只是实现方式不同


    参考:
    博客

    展开全文
  • SpringSpring 依赖注入之手动注入 上篇文章中介绍了依赖注入中的手动注入,所谓手动注入是指在xml中采用硬编码的方式来配置注入的对象,比如通过构造器注入或者set方法注入,这些注入的方式都存在不足,比如下面...

    在这里插入图片描述

    1.概述

    转载:添加链接描述

    2. 手动注入的不足

    【Spring】Spring 依赖注入之手动注入

    上篇文章中介绍了依赖注入中的手动注入,所谓手动注入是指在xml中采用硬编码的方式来配置注入的对象,比如通过构造器注入或者set方法注入,这些注入的方式都存在不足,比如下面代码:

    public class 
    展开全文
  • Spring自动注入的几种方式

    千次阅读 2019-06-25 16:58:00
    ---恢复内容开始--- @Service("accountEmailService")public class AccountEmailServiceImpl implements ... /** 通过构造器注入---begin **/ private JavaMailSender javaMailSender; @Auto...

    ---恢复内容开始---

     
    @Service("accountEmailService")
    public class AccountEmailServiceImpl implements AccountEmailService{

        /**  通过构造器注入---begin  **/
        private JavaMailSender javaMailSender;
        @Autowired
        public AccountEmailServiceImpl(JavaMailSender javaMailSender){
            this.javaMailSender = javaMailSender;
        }
        /** 通过构造器注入---end  **/
        /** 通过set方法注入---begin **/
        private JavaMailSender javaMailSender;
        @Autowired
        public void setJavaMailSender(JavaMailSender javaMailSender){
            this.javaMailSender = javaMailSender;
        }
        /** 通过set方法注入---end **/
        /** 通过field注入 **/
        @Autowired
        private JavaMailSender javaMailSender;
        @Override
        public void sendMail(String to, String subject, String htmlText) throws Exception{
            String systemMail = "xxx@qq.com";
            MimeMessage msg = javaMailSender.createMimeMessage();
            MimeMessageHelper messageHelper = new MimeMessageHelper(msg);
            messageHelper.setFrom(systemMail);
            messageHelper.setTo(to);
            messageHelper.setSubject(subject);
            messageHelper.setText(htmlText);
    //        messageHelper.setText(htmlText,true);
            javaMailSender.send(msg);
        }
    }
    下面着重讲两种注入方式:set方法注入和构造函数注入。由于这2种注入方式很相似,都可以满足我们的需求,所以在大多数情况下我们忽视了这2种注入方式的区别。下面让我们看看这2种注入方式的特点。
    我们先看看Spring在使用set方法注入时,是怎样实例化一个Bean和Bean的合作者的:
    在A中有一个setB方法用来接收B对象的实例。那么Spring实例化A对象的过程如下:

    在不考虑Bean的初始化方法和一些Spring回调的情况下,Spring首先去调用A对象的构造函数实例化A,然后查找A依赖的对象本例子中是B(合作者)。一但找到合作者,Spring就会调用合作者(B)的构造函数实例化B。如果B还有依赖的对象Spring会把B上依赖的所有对象都按照相同的机制实例化然后调用A对象的setB(B b)把b对象注入给A。
    因为Spring调用一个对象的set方法注入前,这个对象必须先被实例化。所以在"使用set方法注入"的情况下Spring会首先调用对象的构造函数。
    我们在来看通过构造函数注入的过程:

     如果发现配置了对象的构造注入,那么Spring会在调用构造函数前把构造函数需要的依赖对象都实例化好,然后再把这些实例化后的对象作为参数去调用构造函数。
    在使用构造函数和set方法依赖注入时,Spring处理对象和对象依赖的对象的顺序时不一样的。一般把一个Bean设计为构造函数接收依赖对象时,其实是表达了这样一种关系:他们(依赖对象)不存在时我也不存在,即“没有他们就没有我”。
    通过构造函数的注入方式其实表达了2个对象间的一种强的聚合关系:组合关系。就比如一辆车如果没有轮子、引擎等部件那么车也就不存在了。而且车是由若干重 要部件组成的,在这些部件没有的情况下车也不可能存在。这里车和他的重要部件就时组合的关系。如果你的应用中有这样类似的场景那么你应该使用“构造函数注 入”的方式管理他们的关系。“构造函数注入”可以保证合作者先创建,在后在创建自己。
    通过set方法注入的方式表达了2个对象间较弱的依赖关系:聚合关系。就像一辆车,如果没有车内音像车也时可以工作的。当你不要求合作者于自己被创建 时,“set方法注入”注入比较合适。
    虽然在理论上“构造函数注入”和“set方法注入”代表2种不同的依赖强度,但是在spring中,spring并不会把无效的合作者传递给一个 bean。如果合作者无效或不存在spring会抛出异常,这样spring保证一个对象的合作者都是可用的。所以在spring中,“构造函数注入”和 “set方法注入”唯一的区别在于2种方式创建合作者的顺序不同。
    使用构造函数依赖注入时,Spring保证所有一个对象所有依赖的对象先实例化后,才实例化这个对象。(没有他们就没有我原则)
    使用set方法依赖注入时,Spring首先实例化对象,然后才实例化所有依赖的对象。

    转载于:https://www.cnblogs.com/Nicolasap/p/11083837.html

    展开全文
  • 作为初学者来说,无论spring的控制反转和依赖注入大家都已耳熟能详,但真正能够熟练运用的依然少之又少。springmvc、struts2、spring、hibernate、Jpa、mybatis、mybatisplus这些技术组合,都依然体现着分层的思想,...
  • 为什么要用自动注入,因为在bean很多或者自定义的类很多的情况下,就要在xml文件里一直写bean并且还要将每个属性以set(构造方法)入参的方式引入bean里,这样耦合性也会变高,也比较麻烦 将类属性注入到Spring容器...
  • 基于Spring自动注入的策略模式

    千次阅读 2018-10-25 19:45:20
    基于Spring自动注入的策略模式 1> 类结构关系图 如图:由于IDiscountActivityService 和ISeckillActivityService 都继承了BaseActivityService , spring提供了根据类型注入bean的特性,我们可以将同一种类型的...
  • Spring自动注入单例和多例如何选择

    千次阅读 2019-10-09 16:23:46
    spring自动注入单例和多例如何选择 spring中bean的scope属性,有如下5种类型: singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例 prototype表示每次获得bean都会生成一个新的对象...
  • Spring自动注入报错原因: idea编辑器Autowiring for Bean Class 的设置 Severity 默认为Error , 好气啊, 所以Spring自动注入会报错 ! 啊啊啊, 如图所示: 解决方法 File -> Settings -> Editor -> ...
  • 再就是spring官网在介绍spring自动注入知识的章节也提到过这个名词 Autowiring mode 笔者姑且翻译为自动注入模型吧,如果有211、雅思读者可以给读者留言更合适的翻译 那为什么要锱铢在这个名词上呢?笔者...
  • spring配置文件中,配置了tableService类,在所有类中,都能正常使用,但是在处理收到的udp消息类中,为null
  • spring 自动注入和 dubbo服务调用问题

    千次阅读 2018-06-20 23:06:42
    交由Spring容器管理的某个Service要调用dubbo提供的一个service,(说具体点 就是 工程A中我写写了一个spring security的自定义认证类,这个类是由容器管理了,但是这个时候 我需要通过dubbo来调用某个服务来获取...
  • 主要给大家分析介绍了关于Spring Boot自动注入的原理,文中通过示例代码介绍的非常详细,对大家的学习或者使用Spring Boot自具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
  • bean加入spring容器管理的方式,bean加入applicationcontext容器的方式
  • 在使用spring开发的时候,有时候会出现一个接口多个实现类的情况,但是有没有有时候有这样一种情况,就是你的逻辑代码里面还不知道你需要使用哪个实现类,就是比如说:你去按摩,按摩店里面有几种会员打折,比如有,...
  • 由于不使用spring自动注入,set方法要去掉! public   class  userService  implements  BeanFactoryAware{    private  Tool tool;   private  BeanFactory factory;   public   void  service(){ ...
  • 工厂模式 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是...
  • 今天闲的蛋疼,做了一下spring自动注入的例子: 看看spring自动注入的是否可以区别出不同包下相同名字的java类,答案是不能,spring居然这么渣。 整体结构图如下: 1:首先建立两个同名的java类和接口 com.a包下面...
  • 剖析Spring自动注入中的注解

    千次阅读 2015-08-01 15:45:32
    剖析Spring自动注入中的注解 学习Spring的自动注入时, 对几个注解非常模棱两可,不知道使用的本质, 只因为大家都这样用,所以我一直这样使用。 下面列出@Autowired和@Resource这两个注解他们的使用关系。在做...
  • 在此处,将 Error 改成 Warning 即可:
  • 使用@autowire 自动注入 @Autowired private SessionFactory sessionFactory; 然后在方法中直接使用 Session session = sessionFactory.getCurrentSession() 但是,后来看源码的时候却有了疑问。 在XML配置文件中...
  • Spring自动注入

    万次阅读 2018-08-20 21:01:51
     //false } 自动注入 自动注入是采用约定大约配置的方式来实现的,程序和spring容器之间约定好,遵守某一种都认同的规则,来实现自动注入。 xml中可以在bean元素中通过autowire属性来设置自动注入的方式: byteName...
  • package spring.core.pracitce; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import org.apache.commons.lang3.StringUtils; import org.junit.Test; import
  • 在网上找了很多都没有解决,最后重写了下spring 的ApplicationContextAware方法添加了一个获取上下文context的方法, 具体代码如下:然后在原先的代码上加了一个if判断:完美解决注入为null问题,也保证了在调用...
  • Spring依赖注入自动装配

    万次阅读 2021-02-06 11:13:29
    Spring依赖注入自动装配 首先推荐狂神说的Spring讲义 1.Beans.xml作用 简而言之,我们通过在beans.xml中进行配置,将各种类交给spring来管理。 2.依赖注入 推荐狂神说Spring03:依赖注入(DI) 这要从控制反转说起...
  • Spring自动注入和注解注入

    千次阅读 2018-07-26 16:54:18
    1 自动注入,分为三种: &lt;!-- 1.1 根据属性名称自动完成setter注入,类中的属性名和id名称相同则自动完成注入 --&gt; &lt;bean id="s" class="...--1.2....
  • Spring三种方法的注解自动注入

    千次阅读 2019-10-16 14:35:06
    @Autowired是Spring提供的自动注入的方法,该注解可以放在变量和方法上,在bean+返回值类型的注解中,@Autowired还可以放在参数前;@Autowired默认的注入是根据类型注入,如果相同类型的bean有多个,可以配合@...
  • java反射导致spring自动注入失败

    千次阅读 2018-01-17 10:27:41
    首先我的spring 的bean是我通过扫描包的方式来自动注入的(com.spring.service是我的包名)&lt;context:component-scan base-package="com.spring.service" /&gt;正是因为我是通过扫描的方式,故...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 191,406
精华内容 76,562
关键字:

spring自动注入

spring 订阅