依赖注入

        控制反转:当某个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>

refbean属性关联和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 

posted on 2018-10-23 17:09  溪水静幽  阅读(224)  评论(0)    收藏  举报