依赖注入
控制反转:当某个java 对象需要依赖另一个java 对象时,不是自身直接创建依赖对象,而是由实现IoC 的容器来创建,并将它注入需要这个依赖对象的java 对象中。
Spring bean的依赖注入有两种形式,通过构造方法注入和通过set方法注入。构造方法注入是通过constructor-arg元素来指定的,set方法注入是通过property元素来指定的。
在spring 的应用中,所有的组件是一个的Bean,可以是任何的java 对象。spring 负责创建这些Bean的实例,并管理生命周期。spring 框架是通过其内置的容器来完成Bean 的管理的
Spring BeanFactory Container
BeanFactory容器由org.springframework.beans.factory.BeanFactory接口实现。BeanFactory以及相关接口,比如BeanFactoryAware、InitializingBean、DisposableBean仍然存在于Spring中以便与Spring集成的第三方框架向后兼容。
对于BeanFactory接口由一系列的实现,都是可以直接使用的。常用的BeanFactory的实现是XmlBeanFactory类。这个容器从XML配置文件中读取配置元数据,并且创建的完整配置的系统和应用
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("FactoryBeans.xml"));//优先使用ApplicationContext HelloWorld obj = (HelloWorld) factory.getBean("helloWorld"); obj.getMessage();
Spring ApplicationContext Container
ApplicationContext 是Spring的高级容器,和BeanFactory类似,能够加载bean定义、绑定bean并且根据请求分发bean实例。除此之外,增加企业级的功能,能够在对应的事件Listener上发布对应的Event。这个容器是由org.springframework.context.ApplicationContext接口定义的。
ApplicationContext经常用到的三个实现:
1.ClassPathXmlApplicationContext:从类路径中的XML文件载入上下文定义信息。把上下文定义文件当成类路径资源
2.FileSystemXmlApplicationContext:从文件系统中的XML文件载入上下文定义信息
3.XmlWebApplicationContext:从Web系统中的XML文件载入上下文定义信息
XMLWebApplicationContext是专门为web应用的,允许从相对于web根目录的路劲中装载配置文件完成初始化工作,从XMLWebApplicationContext中获得ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便web应用可以访问spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext serviceContext)方法来获得XMLWebApplicationContext对象
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);
ContextLoaderListener和DispatcherServlet 会创建XMLWebApplicationContext容器,但是ContextLoaderListener监听器初始化完毕后,才会初始化Servlet
Configuration metadata 包含一系列属性:

配置文件
<?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-3.0.xsd"> <!-- A bean definition with lazy init set on --> <bean id = "..." class = "..." lazy-init = "true"> <!-- collaborators and configuration for this bean go here --> </bean> <!-- A bean definition with initialization method --> <bean id = "..." class = "..." init-method = "..."> <!-- collaborators and configuration for this bean go here --> </bean> <!-- A bean definition with destruction method --> <bean id = "..." class = "..." destroy-method = "..."> <!-- collaborators and configuration for this bean go here --> </bean> </beans>
Scope

如果一个Bean被定义为Singleton,IoC容器回根据bean的定义每次都只创建一个对象。单例的bean实例存储在单例Beans的cache中,后续请求相同名称的bean,都只返回相同的bean,不会在重新创建。
如果bean的scope被设置成了prototype,Spring IoC容器每次都会创建新的Bean的实例。all state-full 的Beans使用prototype,stateless的bean使用singleton。
abstract:指定该Bean 是否为抽象的。如果是抽象的,则spring 不为它创建实例
parent 如果两个Bean 的属性装配信息很相似,那么可以利用继承来减少重复的配置工作
<!-- 装配Bean 的继承父类作为模板,不需要实例化,设置abstract=”true” -->
<bean id=”parent” class=”com.service.Parent” abstract=”true”> <property name=”name” value=”tom”/> <property name=”pass” value=”123”/> </bean> <!-- 装配Bean 的继承子类中用parent 属性指定父类标识或别名子类可以覆盖父类的属性装配,也可以新增自己的属性装配--><bean id=”child”
class=”cn.service.Chlid” parent=”parent”> <property name=”age” value=”22”/> </bean>
集合配置

<?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-3.0.xsd"> <!-- Bean Definition to handle references and values --> <bean id = "..." class = "..."> <!-- Passing bean reference for java.util.List --> <property name = "addressList"> <list> <ref bean = "address1"/> <ref bean = "address2"/> <value>Pakistan</value> </list> </property> <!-- Passing bean reference for java.util.Set --> <property name = "addressSet"> <set> <ref bean = "address1"/> <ref bean = "address2"/> <value>Pakistan</value> </set> </property> <!-- Passing bean reference for java.util.Map --> <property name = "addressMap"> <map> <entry key = "one" value = "INDIA"/> <entry key = "two" value-ref = "address1"/> <entry key = "three" value-ref = "address2"/> </map> </property> </bean> </beans>
自动装配模式
以下的自动装配模式让Spring容器再依赖注入中使用自动装配。需要在的元素定义中指明自动装配的模式。

自动装配的局限性在项目中大量使用就会很好理解,如果自动装配一般都没有被使用,开发者会对项目当中一个两个自动装配定义感到很困惑。虽然自动装配能够很有效的降低指定属性和构造参数的需求,但是需要考虑到自动装配的局限性和缺点。

SpellChecker.java
public class SpellChecker { public SpellChecker() { System.out.println("Inside SpellChecker constructor." ); } public void checkSpelling() { System.out.println("Inside checkSpelling." ); } }
TextEditor.java
public class TextEditor { private SpellChecker spellChecker; private String name; public void setSpellChecker( SpellChecker spellChecker ){ this.spellChecker = spellChecker; } public SpellChecker getSpellChecker() { return spellChecker; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void spellCheck() { spellChecker.checkSpelling(); } }
ByNameBeans.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"> <bean id = "textEditor" class = "com.AutoWiringByName.TextEditor"> <property name = "spellChecker" ref = "spellChecker" /> <property name = "name" value = "Generic Text Editor" /> </bean> <!-- Definition for spellChecker bean --> <bean id = "spellChecker" class = "com.AutoWiringByName.SpellChecker"></bean> </beans>
如果是用autowiring byName的方式,那么配置文件如下所示:
<?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 = "textEditor" class = "com.AutoWiringByName.TextEditor" autowire="byName"> <property name = "name" value = "Generic Text Editor" /> </bean>
<bean id="textEditor" class="com.soygrow.AutoWiringByType.TextEditor" autowire="byType">
<property name="name" value="Generic Text Editor"/>
</bean>
<!-- Definition for spellChecker bean --> <bean id = "spellChecker" class = "com.AutoWiringByName.SpellChecker"></bean> </beans>
spring Autowiring by Constructor
这种模式非常类似于byType,但是它适用于构造参数。当autowire属性在XML配置中被设置的情况下,Spring容器会自动查找对应的bean。Spring容器会使用配置文件中确切名称的bean尝试匹配和装配它的构造参数。如果找到匹配的,则注入对应的bean,否则会抛出异常。
<?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"> <!-- Definition for textEditor bean --> <bean id = "textEditor" class = "com.soygrow.AutoWiringConstructor.TextEditor" autowire="constructor"> <constructor-arg value = "Generic Text Editor"/> </bean> <!-- Definition for spellChecker bean --> <bean id = "spellChecker" class = "com.soygrow.AutoWiringConstructor.SpellChecker"></bean> </beans>
class属性指定类型
在定义bean时可以通过class属性来指定当前bean对应的类型。class属性要求是对应Class类的全限定名,即需要包含包名的。如果需要定义的bean类型属于某个类的静态内部类,那么对应的class值应该是该内部类的二进制名称,即中间以$符号连接。下面来看一个示例,假设有如下这样一个类,先需要分别定义Hello对应的bean和World对应的bean,其中Hello所在的包名为com.app。
public class Hello { public static class World { } }
那么,对应bean定义应该是这样子的:
<bean id="hello" class="com.app.Hello"/> <bean id="world" class="com.app.Hello$World"/>
静态方法实例化
如果bean是通过指定Class的静态方法进行实例化的,则可以通过指定Class为拥有对应静态方法的Class,指定factory-method为对应的实例化静态方法。假设拥有如下这样一个Class。
public class Hello { public static World createWorld() { return new World(); } public static class World { } }
需要通过Hello的createWorld静态方法来构造一个类型为World的bean,那么我们可以如下定义:
<bean id="world" class="com.app.Hello" factory-method="createWorld"/>
从以上定义可以看到在通过静态方法实例化对应的bean时,是不需要指定对应的bean的类型的,指定的class是对应静态方法所在的Class。实例化的bean的类型Spring将根据factory-method指定方法的返回值决定。
如果用于实例化的静态方法factory-method是需要接收参数的,那么对应的参数将通过constructor-arg来定义,定义的规则和定义构造方法参数是一样的。来看一个示例,现将我们的Hello和World的定义改为如下这样,Hello中用来实例化World对象的createWorld方法需要接收两个参数。
public class Hello { public static World createWorld(String s1, int i2) { return new World(s1, i2); } } public class World { public World(String s1, int i2) { } }
那么对应的bean定义应该是这样的:
<bean id="world" class="com.app.Hello" factory-method="createWorld">
<constructor-arg value="s1"/>
<constructor-arg value="2"/>
</bean>
通过实例方法进行实例化
bean可能是需要通过某个对象进行实例化的,这个时候可以通过factory-bean指定用来实例化该bean的对应对象,通过factory-method指定factory-bean中用来实例化该bean的方法。factory-bean必须也是属于ApplicationContext中的一个bean。如有如下这样一个类Hello,其中拥有一个实例方法createWorld将实例化一个World对象。
public class Hello { public World createWorld() { return new World(); }
如果我们需要定义一个bean使用Hello的实例对象的createWorld方法来实例化对应的World对象,则可以这样定义:
<bean id="hello" class="com.app.Hello"/>
<bean id="world" factory-bean="hello" factory-method="createWorld"/>
此时也是不需要指定class的,如果用户指定了class,Spring也会将其忽略,对应bean的类型将由factory-method指定方法的返回值决定。当factory-method需要接收参数时,则对应的参数可以通过constructor-arg来定义。如Hello是这样定义的,其createWorld方法需要接收参数。
public class Hello { public World createWorld(String s1, int i2) { return new World(s1, i2); } }
那么,对应的bean应该如下定义:
<bean id="hello" class="com.app.Hello"/>
<bean id="world" factory-bean="hello" factory-method="createWorld">
<constructor-arg value="s1"/>
<constructor-arg value="2"/>
</bean>
ref的bean属性关联和parent属性关联是不同的。通过bean属性指定关联时会在当前容器及其父容器中寻找关联项,而通过parent属性指定关联时只会在当前容器的父容器中寻找关联项。
<!-- 假设是定义在父容器中的bean -->
<bean id="parenWorld" class="com.app.World"/>
<bean id="hello" class="com.app.Hello">
<property name="world">
<ref parent="parentWorld"/><!-- 关联父容器中id或name为world的bean -->
</property>
</bean>
depends-on
depends-on是定义在bean元素上的一个属性,用于指定其所依赖的bean,这样Spring将在初始化当前bean前先初始化其depends-on属性所指定的bean。所以depends-on的功能就是指定一个bean所依赖的bean,通过它来告诉Spring将优先初始化depends-on属性指定的bean。
注意,该属性只有告诉Spring该优先初始化哪个bean的功能,Spring不会因为这个属性的定义而注入对应的bean。在不使用depends-on属性的情况下,Spring初始化bean的顺序是固定的,通常是根据bean定义的先后顺序来进行初始化。在下面的示例中,Spring将先初始化world,再初始化hello,但是其不会将world注入给hello。
<bean name="world" class="com.app.World"/> <!-- 通过depends-on属性告诉Spring先初始化depends-on属性指定的bean,即这里的world --> <bean id="hello" class="com.app.Hello" depends-on="world"/>
lazy-init
默认情况下,Spring将在初始化bean容器的时候初始化所有的单例bean。如果有一个bean是单例的,但是又不想其在bean容器初始化的时候被初始化,那么可以在该bean上定义lazy-init属性为true,这样Spring将不会在初始化bean容器的时候将其初始化。相对应的,其会在第一次被需要的时候进行初始化。
如果有一个叫beanA的单例bean定义lazy-init为true,这就表示其不会在Spring初始化bean容器的时候进行初始化,但是如果其被另一个名叫beanB的单例bean所依赖,而beanB没有指定lazy-init为true,这就意味着beanB将在Spring初始化bean容器时进行初始化,这个时候由于Spring需要给beanB注入一个beanA,也就是说beanA在这个时候被需要,所以此时Spring也将会初始化beanA。
<!-- 通过lazy-init="true"指定其将不在初始化bean容器时初始化 -->
<bean id="beanA" class="com.app.BeanA" lazy-init="true"/>
<!-- 由于beanB对beanA有依赖,所以在初始化BeanB后,如果容器中不存在beanA,Spring将初始化备案A -->
<bean id="beanB" class="com.app.BeanB">
<property name="beanA" ref="beanA"/>
</bean>
lazy-init的值默认是false,也就是说对于单例bean而言,如果没有指定lazy-init=”true”时其默认会在bean容器初始化时被初始化,如果用户希望这种全局的初始化策略为不在bean容器初始化时进行初始化,则可以在beans元素上定义default-lazy-init=”true”
参考:
https://blog.csdn.net/soygrow/article/details/79267434
https://blog.csdn.net/elim168/article/details/73849296
https://blog.csdn.net/elim168/article/details/73867504
浙公网安备 33010602011771号