spring3.x第四章 在IOC容器中装配Bean

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_21413417/article/details/54908696

4.1 Spring配置概述

4.1.1 Spring容器高层视图

  Spring启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。
  Bean配置信息是Bean的元数据信息,由4个方面组成:
  * Bean的实现类;
  * Bean的属性信息,如数据源的连接数、用户名、密码;
  * Bean的依赖关系,Spring根据依赖关系配置完成Bean之间的装配;
  * Bean的行为配置,如生命周期范围及生命周期各过程的回调函数等;
  Bean元数据信息在Spring容器中的内部对应是由一个个BeanDefinnition形成的Bean注册表。

4.1.2 基于XML的配置

  Spring1.0的配置文件采用DTD格式,Spring2.0以后使用Schema的格式。

4.2 Bean基本配置

4.2.1 装配一个Bean
<bean id="car" class="com.baobaotao.simple.Car" />
<bean id="boss" class="com.baobaotao.simple.Boss" />

  这段配置信息提供了实例化Bean的名称,通过容器的getBean(“car”)即可获得,class对应实现类。Spring IOC容器据此创建这两个Bean的实例。

4.2.2 Bean的命名

  建议使用id命名,由于name属性名无规范没有限制。如没有设置名称,则默认为class为名称。

4.3 依赖注入

4.3.1 属性注入

  1.要求Bena提供一个默认的构造函数。
  2.为需要注入的属性提供对应的Setter方法(可以没有这个属性和Getter方法)。

<bean id="car" class="com.baobaotao.ditype.Car">
    <property name="maxSpeed">
        <value>
            498
        </value>
    </property>
    <property name="brand">
        <value>
            GT-R
        </value>
    </property>
    <property name="price">
        <value>
            2000000.00
        </value>
    </property>
</bean>
  JavaBean关于属性命名的特殊规范(实测没有出现错误)

  实际测试没有出现错误。代码贴上
  启动Spring容器:

package com.baobaotao.attr;

public static void main(String[] args) {
        // TODO Auto-generated method stub
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource rs = resolver.getResource("classpath:com/baobaotao/attr/beans.xml");
        BeanFactory bf = new XmlBeanFactory(rs);

        System.out.println("init BeanFactory.");
        Foo foo = bf.getBean("foo", Foo.class);
        System.out.println("car bean is resdy for use");

    }

  对应的Bean类

package com.baobaotao.attr;

public class Foo {
    private String iDCode;
    public void setIDCode(String iDCode) {
        this.iDCode = iDCode;
    }
}

  对应的Bean配置文件

<bean
        id="foo"
        class="com.baobaotao.attr.Foo">
        <property name="iDCode" value="070101"/>
    </bean>
4.3.2 构造函数注入
  按类型匹配入参

  通过构造函数注入保证在语法级提供必须提供某些属性值。

<bean
        id="car1"
        class="com.baobaotao.ditype.Car">
        <constructor-arg type="java.lang.String">
            <value>
                GT-R
            </value>
        </constructor-arg>
        <constructor-arg type="double">
            <value>
                2000.00
            </value>
        </constructor-arg>
    </bean>
package com.baobaotao.ditype;

public class Car {
    private String brand;
    private double price;
    public Car(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }
}

  在的声明顺序并不是确定构造函数入参的顺序,Spring的配置文件采用和元素标签顺序无关的策略。与web.xml配置文件顺序相关的策略是不同的(Log4J的配置和Spring的配置)。

  按索引匹配入参
public Car2(String brand, String corp, double price) {
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }
<bean
        id="car2"
        class="com.baobaotao.ditype.Car2">
        <constructor-arg index="0" value="GT-R"/>
        <constructor-arg index="1" value="USA"/>
        <constructor-arg index="2" value="2000.00"/>
    </bean>
  联合使用类型和索引匹配入参
    <bean
        id="car3"
        class="com.baobaotao.ditype.Car3">
        <constructor-arg index="0" type="java.lang.String">
            <value>
                GT-R
            </value>
        </constructor-arg>
        <constructor-arg index="1" type="java.lang.String">
            <value>
                USA
            </value>
        </constructor-arg>
        <constructor-arg index="2" type="int">
            <value>
                2000
            </value>
        </constructor-arg>
    </bean>
    public Car3(String brand, String corp, double price) {
        this.brand = brand;
        this.corp = corp;
        this.price = price;
    }
    public Car3(String brand, String corp, int maxSpeed) {
        this.brand = brand;
        this.corp = corp;
        this.maxSpeed = maxSpeed;
    }

  如若有type属性,则需要使用方式,而不能直接

  通过自身类型反射匹配入参
    public Car4(String brand, Car car, Car2 car2) {
        this.brand = brand;
        this.car = car;
        this.car2 = car2;
    }
<bean
        id="car4"
        class="com.baobaotao.ditype.Car4">
        <constructor-arg>
            <value>
                GT-R
            </value>
        </constructor-arg>
        <constructor-arg>
            <ref bean="car"/>
        </constructor-arg>
        <constructor-arg>
            <ref bean="car2"/>
        </constructor-arg>
    </bean>

    <bean id="car" class="com.baobaotao.ditype.Car" />
    <bean id="car2" class="com.baobaotao.ditype.Car2" />

  但是需要Car,Car2默认的构造方法。否则报错:No default constructor found。

  循环依赖问题

  Spring容器能顺利实例化以构造函数注入方式配置的Bean有一个前提:Bean构造函数入参引用的对象必须已经准备就绪。由于这个机制的限制,如果两个Bean都采用构造函数注入而且都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。例如:

    public CarFromCar2(String brand, Car2 car2){
    }

    public Car2FromCar(String brand, Car car){
    }
<bean
        id="carfromcar2"
        class="com.baobaotao.ditype.CarFromCar2">
        <constructor-arg index="0" value="GT-R" />
        <constructor-arg index="1" ref="car2fromcar" />
    </bean>

    <bean
        id="car2fromcar"
        class="com.baobaotao.ditype.Car2FromCar">
        <constructor-arg index="0" value="GT-R" />
        <constructor-arg index="1" ref="carfromcar2" />
    </bean>
4.3.3 工厂方法注入
  非静态工厂方法
<!-- 工厂类Bean -->
    <bean id="carFactory" class="com.baobaotao.ditypefactory.CarFactory" />

    <!-- 根据配置返回工厂类指定的Bean -->
    <bean id="car5"
        factory-bean="carFactory"
        factory-method="createGT_RCar" />
public class CarFactory {
    public Car createGT_RCar(){
        Car car = new Car();
        return car;
    }
}
  静态工厂方法
<!-- 根据配置返回工厂类指定的Bean -->
    <bean id="car6"
        class="com.baobaotao.ditypefactory.CarFactoryStatic"
        factory-method="createGT_RCar" />

  createGT_RCar方法加上静态修饰即可,然后在配置文件修改,无须实例工厂Bean,直接调用。

4.3.4 选择注入方式的考量

  实际为准,不推荐用工厂方法,因为需要额外的类和代码。而且与业务无关。

4.4 注入参数详解

  在Spring配置文件中,不但可以将String、int注入到Bean中,还可以将集合、Map等注入,还可以注入已经定义的Bean。

4.4.1 字面值
<bean id="car" class="com.baobaotao.attr.Car">
    <property name="brand">
        <value>
            <![CDATA[红旗&CA72]]>
        </value>
    </property>
</bean>

  

4.4.2 引入其他Bean

  Spring IOC容器中定义的Bean可以相互引用,IOC容器则充当红娘的角色。

public class Boss{
    public void setCar(Car car){
    }
}
<bean id="car" class="com.baobaotao.attr.Car" />
<bean id="boss" class="com.baobaotao.attr.Boss">
    <property name="car">
        <ref bean="car"></ref>
    </property>
</bean>

  元素可以通过以下三个属性引用容器中其他Bean。

  bean:该属性引用同一容器或父容器的Bean;
  local:该属性只能引用同一配置文件中定义的Bean;
  parent:引用父容器的Bean;

  启动Spring容器

ClassPathXmlApplicationContext pFactory = new ClassPathXmlApplicationContext(
                new String[]{"com/baobaotao/beanparent/beans1.xml"});

        ClassPathXmlApplicationContext factoryApplicationContext = new ClassPathXmlApplicationContext(
                new String[]{"com/baobaotao/beanparent/beans2.xml"}, pFactory);

        System.out.println("init BeanFactory.");
        Boss boss = factoryApplicationContext.getBean("boss", Boss.class);

        System.out.println("car bean is resdy for use");
        boss.getCar();

  对应的beans1.xml和beans2.xml文件

    <bean
        id="car"
        class="com.baobaotao.beanparent.Car">
        <property name="brand" value="GT-R" />
        <property name="maxSpeed" value="498" />
        <property name="price" value="200000" />
    </bean>
<bean
        id="car"
        class="com.baobaotao.beanparent.Car">
        <property name="brand" value="吉利" />
        <property name="maxSpeed" value="200" />
        <property name="price" value="2000" />
    </bean>

    <bean
        id="boss"
        class="com.baobaotao.beanparent.Boss">
        <constructor-arg>
            <ref parent="car"/>
        </constructor-arg>
    </bean>
4.4.3 内部Bean

  类似匿名内部类

<bean id="boss" class="com.baobaotao.attr.Boss">
    <property name="car">
        <bean class="com.baobaotao.attr.Car">
            <constructor-arg index="0" type="java.lang.String"/>
        </bean>
    </property>
</bean>
4.4.4 null值

  什么都不输入为空字符串

<bean id="car" class="com.baobaotao.attr.Car">
    <property name="brand"><value><null/></value></property>
</bean>
4.4.5 级联属性
<bean id="boss" class="com.baobaotao.attr.Boss">
    <!-- 以圆点的方式定义级别属性,Boss.getCar().setBrand("GT-R") -->
    <property name="car.brand" value="GT-R"/>
</bean>
4.4.6 集合类型属性
  List
public void setF(List f){}
<bean id="boss1" class="com.baobaotao.attr.Boss">
    <property name="f">
        <list>
            <value></value>
            <value></value>
            <value></value>
            <value></value>
        </list>
    </property>
</bean>
  Set

  和List一样的配置,只需要改成set

  Map
<bean>
    <property>
        <map>
            <entry>
                <key>
                    <value>
                        AM
                    </value>
                </key>
                <value>
                    见面
                </value>
            </entry>
            <entry>
                <key>
                    <ref bean="key" />
                </key>
                <ref bean="value" />
            </entry>
        </map>
    </property>
</bean>
  Properties

  Properties类型其实可以看成是Map的特例,键值对只能是字符串。

public void setMails(Properties m){}
<bean>
    <property>
        <props>
            <prop key="jobMail">1@163.com></prop>
            <prop key="lifeMail">1@qq.com></prop>
        </props>
    </property>
</bean>
  强类型集合
public void setJobTime(Map<String, Integer> jobTime){}

  和普通配置一样,只不过配置文件中会被自动转换。

  集合合并

  允许子集成父的同名属性集。这样子Bean拥有父的基础属性。

<bean id="parentBoss" abstract="true" class="com.baobaotao.attr.Boss">
    <property name=favorites>
        <set>
            <value>看报</value>
            <value>写字</value>
            <value>高尔夫</value>
        </set>
    </property>
</bean>
<bean id="childBoss" parent="parentBoss">
    <property name="favorites">
        <set merge="true">
            <value>爬山</value>
            <value>游泳</value>
        </set>
    </property>
</bean>
  通过util命名空间配置集合类型的Bean
...
//可以不用指定class
<util:list id="a" list-class="java.util.LinkedList">
    <value>看报</value>
</util:list>
4.4.7 简化配置方式
  使用p命名空间
<bean id="car" class="com.baobaotao.ditype.Car">
    <property name="brand" value="GTR"/>
    <property name="maxSpeed" value="489"/>
    <property name="price" value="20000"/>
</bean>
<bean id="boss" class="com.baobaotao.ditype.Boss">
    <property name="car" ref="car"/>
</bean>
///////////////////使用P命名空间后///////////////////
<bean id="car" class="com.baobaotao.ditype.Car"
    p:brand="GTR"
    p:maxSpeed="200"
    p:price="200000" />
<bean id="boss" class="com.baobaotao.ditype.Boss"
    p:car-ref="car" />
4.4.8 自动装配

  Spring IOC容器通过反射机制获得Bean实现类的结构信息。掌握容器中所有Bean的这些信息后,Spring IOC容器就可以按照某种规则对容器中的Bean进行自动装配,而无须我们通过显式的方式进行配置。Spring可以按照某些规则进行Bean的自动装配。
  元素提供了一个指定自动装配类型的属性:autowire=”<自动装配类型>”。四种自动装配类型。
  byName:根据名称自动匹配。假设Boss有一个名为car的属性,容器中刚好有一个名为car的Bean,Spring就会自动将其装配给Boss的car属性。
  byType:根据类型自动匹配。假设Boss有一个Car类型的属性,容器中刚好有一个Car类型的Bean,Spring就会自动将其装配给Boss的这个属性。
  constructor:与ByType类似,只不过它是针对构造函数注入而言,如果Boss有一个构造函数,构造函数包含一个Car类型的入参,如果容器中有一个Car类型的Bean,则Spring将自动把这个Bean作为Boss构造函数的入参。否则抛出异常
  autodetect:根据Bean的自省机制决定采用byType还是constructor进行自动装配:如果Bean提供了默认的构造函数,则采用byType;否则采用constructor。
  实际很少用,因为没有配置文档清晰明显。

4.5 方法注入

  如果希望往singleton的Boss中注入prototype的Car,并希望每次调用Boss Bean的getCar()时都能够返回一个新的Car Bean。

4.5.1 lookup方法注入

  Spring IOC容器拥有复写Bean方法的能力。CGLib类包可以在运行期动态操作Class字节码,为Bean动态创建子类或实现类。

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 引用Spring的多个Schema空间的格式定义文件 -->
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean
        id="car"
        class="com.baobaotao.injectfun.Car"
        p:brand="GTR"
        p:price="2000"
        scope="prototype">
    </bean>

    <!-- 让getCar()每次返回新的Car Bean -->
    <bean
        id="magicBoss"
        class="com.baobaotao.injectfun.MagicBoss">
        <lookup-method name="getCar" bean="car"/> 
    </bean>

</beans>
public interface MagicBoss {
    Car getCar();
}
4.5.2 方法替换
<bean id="boss1" class="com.baobaotao.injectfun.Boss1">
    <replaced-method name="getCar" replacer="boss2" />
</bean>
<bean id="boss2" class="com.baobaotao.injectfun.Boss1"/>

4.6 之间的关系

4.6.1 继承
<!-- 定义为抽象been -->
<bean id="abstractCar" class="com.baobaotao.tagdepend.Car"
    p:brand="GTR" p:price="200000.00" p:color="黑色" abstract="true" />
<bean id="car3" p:color="红色" parent="abstractCar" />
<bean id="car4" p:color="白色" parent="abstractCar" />

  父bean主要功能主要为了简化子bean的配置,所以一般声明为abstract=”true”,表明不实例化。

4.6.2 依赖

  有时候依赖关系不明显,但是如果一个Bean的参数需要在另一个Bean执行之后再通过一系列的逻辑确定,则需要指定前置依赖的Bean。如果需要依赖多个Bean,可以通过逗号,空格或分号的方式。

<bean id="manager" class="com.baobaotao.tagdepend.CacheManager"
    depends-on="sysInit" />
<bean id="sysInit" class="com.baobaotao.tagdepend.SysInit" />
4.6.3 引用

  假设一个要引用另一个的id属性值

<bean id="car" class="com.baobaotao.Car"/>
<bean id="boss" class="com.baobaotao.Boss"
    p:carId="car" scope="prototype" />
/////以上配置只能在运行期发现问题,下面在容器启动时就检查正确性////
<bean id="boss" class="com.baobaotao.Boss">
    <property name="carId">
        <idref bean="car" />
    </property>
</bean>

4.7 整合多个配置文件

<import resource="classpath:com/baobaotao/impt/beans1.xml" />

4.8 Bean作用域

  singleton

  在Spring IOC容器中仅存一个Bean实例,Bean以单实例的方式存在

  prototype

  每次从容器调用Bean时,都返回一个新的实例。调用getBean(),相当于执行new XxxBean()

  request

  每次HTTP请求都会创建一个新的Bean。仅适用于WebApplicationContext环境

  session

  同一个HTTP Session共享一个Bean,不同HTTP Session使用不同的Bean。仅适用于WebApplicationContext环境

  globalSession

  同一个全局Seesion共享一个Bean,一般用于Portlet应用环境。仅适用于WebApplicationContext环境

4.8.1 singleton作用域
<bean id="car" class="com.baobaotao.Car" scope="singleton" />
<bean id="boss1" class="com.baobaotao.Boss" p:car-ref="car" />
<bean id="boss2" class="com.baobaotao.Boss" p:car-ref="car" />

指向的同一个Bean,Spring的ApplicationContext容器在启动时,自动实例化所有singleton的Bean并缓存于容器中。lazy-init=”true”

4.8.2 prototype作用域

  启动时不实例化,而是每次调用getBean(“car”)就实例化。

4.8.3 Web应用环境相关的Bean作用域

  利用HTTP请求监听器进行配置。在ContextLoaderListener将Web容器与Spring容器整合,Spring容器启动和关闭操作由Web容器的启动和关闭事件触发。而这里需要进行HTTP的事件的监听。

<listener>
    <listener-class>
    org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>
  request作用域
<bean name="car" class="com.baobaotao.Car" scope="request" />

  这样每次HTTP请求调用到car Bean时,Spring容器创建一个新的Bean,请求处理完毕后,销毁这个Bean。

  session作用域
<bean name="car" class="com.baobaotao.Car" scope="session" />

  作用域横跨真个HTTP Session,Session中所有的HTTP请求都共享同一个car Bean,当HTTP Session结束后,实例才被销毁。

  globalSession作用域
<bean name="car" class="com.baobaotao.Car" scope="globalSession" />

  类似session作用域,不过仅在Portle的Web应用中使用。

4.8.4 作用域依赖问题
<bean name="car" class="com.baobaotao.Car" scope="request">
    <aop:scoped-proxy />
</bean>
<bean id="boss" class="com.baobaotao.Boss">
    <property name="car" ref="car" />
</bean>

  Spring通过动态代理技术,能够让单例boss Bean引用到对应HTTP请求的car Bean。

4.9 FactoryBean

  用户可以通过实现该工厂接口定制实例化Bean的逻辑。

4.10 基于注解的配置

4.10.1 使用注解定义Bean

  Spring容器成功启动的三大要件分别是:Bean定义信息、Bean实现类以及Spring本身。Bean定义信息是可以通过XML文件、注解以及类实现。下面是使用注解定义的一个Bean:

@Component("userDao")
public class UserDao{

}

等价于

<bean id="userDao" class="com.baobaotao.UserDao" />
  @Repository:用于对DAO实现类进行标注
  @Service:用于对Service实现类进行标注
  @Controller:用于对Controller实现类进行标注
  @Component:用于对实现类进行标注
4.10.2 使用注解配置信息启动Spring容器

  Spring提供context命名空间(xmlns:context)通过扫描类包以应用注解定义Bean的方式。

<context:component-scan base-package="com.baobaotao.anno" />

  如果仅希望扫描特定的包而非基包下的所有类,如下:

<context:component-scan base-package="com.baobaotao" resource-pattern="anno/*.class" />
4.10.3 自动装配Bean
  使用@Autowired进行自动注入
  使用@Autowired的required属性

  @Autowired(required=false)即使Spring找不到匹配的Bean也不报错,默认是true,即会报错。

  使用@Qualifier指定注入Bean的名称

 &emsp;@Autowired注入的是类,而@Qualifier(“beanname”)注入的是某个name

public class LogonService{
    @Autowired
    private LogDao logDao;
    //注入的Bean名为userDao,类型为UserDao
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
}
  对类方法进行标注
public class LogonService{
    //自动将LogDao传给方法入参
    @Autowired
    public void setLogDao(LogDao logDao){

    }
    @Autowired
    @Qualifier("userDao")
    public void setUserDao(UserDao userDao){

    }
    @Autowired
    public void init(@Qualifier("userDao")UserDao userDao, LogDao logDao){

    }
}
  对集合类进行标注
4.10.4 Bean作用范围及生命过程方法

  @Scope(“”)指定作用范围,singleton或者prototype。
  @PostConstruct和@PreDestroy,容器初始化和销毁时执行的方法,可以有多个。

4.11 基于Java类的配置
4.11.1 使用Java类提供Bean定义信息

  下面两个配置是等价的:

@Configuration
public class AppConf{
    @Bean
    public UserDao userDao(){
        return new UserDao();
    }
    @Bean
    public LogDao logDao(){
        return new LogDao();
    }
    @Bean
    public LogonService logonService(){
        LogonService logonService = new LoonService();
        logonService.setLogDao(logDao());
        logonService.setUserDao(userDao());
        return logonService;
    }
}
<bean id="userDao" class="com.baobaotao.UserDao" />
<bean id="logDao" class="com.baobaotao.LogDao" />
<bean id="logonService" class="com.baobaotao.LogonService"
    p:logDao-ref="userDao" p:userDao-ref="logDao" />

  如果注入的Bean不在一个包下:

@Configuration
public class ServiceConfig{
    @Autowired
    private DaoConfig daoConfig;
    @Bean
    public LogonService logonService(){
        LogonService logonService = new LogonService();
        logonService.setLogDao(daoConfig.logDao());
        logonService.setUserDao(daoConfig.userDao());
        return logonService;
    }
}

  调用daoConfig的logDao()和userDao()方法,就相当于将DaoConfig配置类中定义的Bean注入进来。Spring会对配置类所有标注@Bean的方法进行”改造”(AOP增强),将对Bean生命周期管理的逻辑植入进来。所以我们调用daoConfig.logDao()方法不是简单的执行方法逻辑,而是从Spring容器中返回相应Bean的单例。

4.11.2 使用基于Java类的配置信息启动Spring容器

  直接通过@Configuration类启动Spring容器
可以查看使用ClassPathXmlApplicationContext调用xml文件启动Spring容器的方法。

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConf.class);
ApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(DaoConfig.class);
ctx.register(ServiceConfig.class);
ctx.refresh();

  通过XML配置文件引用@Configuration的配置

<context:component-scan base-package="com.baobaotao.conf" resource-pattern="AppConf.class" />

  通过Configuration配置类引用XML配置信息,即类与XML混用

<bean id="userDao" class="com.baobaotao.UserDao" />
@Configuration
@ImportResource("classpath:com/baobaotao/conf/beans3.xml")
public class LogonAppConfig{
    @Bean
    @Autowired
    public LogonService logonService(UserDao userDao){

    }
}
4.12 不同Bean配置方式比较
4.13 小结

  用户不但可以通过属性注入的方式建立Bean和Bean的依赖,也可以通过构造函数的方式完成。
  用户可以设置Bean的属性,也可以通过ref引用本地容器,父容器,本文件的Bean。
  标签可以建立继承、依赖、引用的关系。
  Spring提供了5个Bean作用范围。还可以用代码定义新的作用域。singleton、prototype、request、session、globalSession。
  @Component以及@Repository、@Service、@Controller配合@Autowired可以很好地基于注解的配置进行Bean的定义和注入。基于注解。
  @Configuration注解后就可以为Spring容器提供Bean的定义信息,在类方法中标注Bean即相当于定义了一个Bean。基于Java类。

展开阅读全文

没有更多推荐了,返回首页