spring bean 的装配方式

任何一个功能模块都是由很多组件(对象)在一起协调完成的,订单管理组件、产品管理组件、支付组件需要彼此了解并相互协作,它们还需要与数据库访问组件一起协作,从而完成从数据库读取数据、写入数据的行为。

在Spring中,所有对象的创建、装配、维护、销毁,都交给Spring容器,对象之间的关联(即装配)也是由Spring容器来负责完成。

 

(一)spring bean 的常用装配方式

1)java中显示配置(使用注解)

interface Wheel {
}

public class BigWheel implements Wheel {
}

public class Car {
    Wheel wheel;
    public Car(Wheel wheel) {
        this.wheel = wheel;
    }
    void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
}

生成:
@Configuration
public class WheelConfig {
    @Bean(name="myWheel")
    public Wheel getWheel(){
        return new BigWheel();
    }
}

注入方法1:
@Bean
public Car getCar() {
    return new Car(getWheel());
}

注入方法2:
@Bean
public Car getCar(Wheel wheel) {
    return new Car(Wheel wheel);
}

注入方法3:
@Bean
public Car getCar(Wheel wheel) {
    Car car = new Car();
    car.setWheel(wheel);
    return car;
}

JavaConfig是配置代码,其与业务逻辑代码应该是隔离的,最好放在单独的包中。配置代码不应该包含任何业务逻辑,也不应该侵入业务逻辑代码。

接口Wheel和类BigWheel可以是我们项目文件中定义的,也可能是引用第三方库中的组件,我们可以在JavaConfig中,使用它们,显示地创建我们所需要的bean。

注解@Configuration 说明WheelConfig类是一个配置类,该类包含了在spring应用上下文中创建bean的细节。

带有@Bean注解的方法getWheel()定义了一个生成Wheel类型的bean的方法,默认情况下该bean的ID与方法名相同,但是我们可以通过设置name属性对其指定命名。

将Wheel类型的bean注入Car类型的bean中,可以将getWheel()方法作为Car的构造函数的参数,此时并不会在每一次注入时都调用getWheel()方法,而是返回同一个由getWheel()方法产生的bean。也可以采用如方法2更加简单的方法从spring容器中寻找符合条件的Wheel类型的bean,该bean可能是有javaConfig显示配置生成、可能是由XML配置生成,也可能是由组件扫描生成的。方法2和方法3的区别在于一个使用了构造器方法,另一个使用了Setter方法,其他方法也是可以的。

 

2)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-2.5.xsd">

    <bean id="myWheel" class="com.oschina.BigWheel">
</beans>

<beans>元素是所有Spring配置文件中的根元素

<bean>元素相当于JavaConfig中的@Bean注解,id可以用来指定bean的名字。这里有两个问题,1)Spring发现这个 <bean>元素时,会调用Wheel的默认构造函数来创建一个bean,这就显得不如JavaConfig功能强大了,因为使用JavaConfig,我们可以指定任意一种方式来创建bean;2)<bean>元素的 class如果填错,在编译器是无法发现的。

我们需要创建的bean通常可能包含很多成员属性,或是bean的引用,或是字面量常量等等。XML配置提供了构造器注入和属性注入两种配置方式,让我们可以在创建bean的时候,注入bean引用或是注入字面量。

a)构造器注入

构造器注入bean引用(<constructor-arg>元素或是c命名空间)
public class Car {
    private Wheel wheel;
}

<bean id="myCar" class="com.oschina.Car">
    <constructor-arg ref="myWheel"/>
</bean>

<bean id="myCar" class="com.oschina.Car">
    c:wheel-ref="myWheel"
/>
<bean id="myCar" class="com.oschina.Car">
    c:_0-ref="myWheel"
/>
<bean id="myCar" class="com.oschina.Car">
    c:_-ref="myWheel"
/>



构造器注入字面量(<constructor-arg>元素或是c命名空间)
public class Car {
    private String name;
}

<bean id="myCar" class="com.oschina.Car">
    <constructor-arg value="myWheel"/>
</bean>

<bean id="myCar" class="com.oschina.Car">
    c:_name="myWheel"
/>
<bean id="myCar" class="com.oschina.Car">
    c:_0="myWheel"
/>
<bean id="myCar" class="com.oschina.Car">
    c:_="myWheel"
/>


装配集合(只能使用<constructor-arg>元素)
public class Car {
    List<Wheel> wheelList;
    List<String> nameList;
}
<bean id="myCar" class="com.oschina.Car">
    <constructor-arg>
        <list>
            <ref bean="myWheel">
            <ref bean="hisWheel">
            <ref bean="herWheel">
        </list>
    </constructor-arg>
    <constructor-arg>
        <list>
            <value> mimo </value>
            <value> timo </value>
            <value> nimo </value>
        </list>
    </constructor-arg>
</bean>



 

b)属性注入

属性注入bean引用(propertyname或是p命名空间)
public class Car {
    private Wheel wheel;
}

<bean id="myCar" class="com.oschina.Car">
    <property name="wheel" ref="myWheel"/>
</bean>

<bean id="myCar" class="com.oschina.Car">
    p:wheel-ref="myWheel"
/>
<bean id="myCar" class="com.oschina.Car">
    p:_0-ref="myWheel"
/>
<bean id="myCar" class="com.oschina.Car">
    p:_-ref="myWheel"
/>



属性注入字面量(propertyname或是p命名空间)
public class Car {
    private String nick;
    private String label;
}

<bean id="myCar" class="com.oschina.Car">
    <propertyname name="nick" value="mimo"/>
    <propertyname name="label" value="good"/>
</bean>

<bean id="myCar" class="com.oschina.Car">
    p:nick="myWheel"
    p:label="good"
/>



装配集合(只能使用<constructor-arg>元素)
public class Car {
    List<Wheel> wheelList;
    List<String> nameList;
}
<bean id="myCar" class="com.oschina.Car">
    <property wheelList = "myWheel">
        <List>
            <value> mimo </value>
        </List>
    </property>
</bean>



 

3)组件扫描(compont scanning),自动装配(autowiring)

如果我们需要创建的bean的类型都是在自己的源文件中定义的,那么除了JavaConfig和XML的显示配置之外,我们还可以通过组件扫描、自动装配的方式来创建bean。

@Congiuation
@ComponentScan
public class WheelConfig{
}

@ComponentScan("com.oschina")
public class WheelConfig{
}

@ComponentScan(basePackages={"com.oschina","com.osindea"})
public class WheelConfig{
}

@ComponentScan(basePackages={CodeChina.class, CodeIndea.class})
public class WheelConfig{
}

@Component("myWheel")
public class BigWheel implements Wheel {
    
}

通过在配置类上加注解@ComponentScan,可以在该配置类所在的包目录下扫描所有文件,对于拥有注解@Component的类,会自动创建对应类型的bean。还可以通过设置ComponentScan注解的basePackages属性,来指定组件扫描的基础包。

还可以在XML配置中指定组件扫描的基础包

<context:component-scan base-package="com.oschina" />
@Component
public class Car{
    @Autowired
    private Wheel wheel;
}

@Component
public class Car{
    private Wheel wheel;

    @Autowired
    void Car(Wheel wheel) {
        this.wheel = wheel;
    }
}

@Component
public class Car{
    private Wheel wheel;

    @Autowired
    void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }
}

通过注解@Autowired,我可以将类型Car所需要的依赖的bean自动注入,该注解可以加在成员属性上、构造方法上,或者setter方法上,或者其他任何方法。

拥有@Autowired注解的属性或是方法,如果在spring容器中找不到合适类型的bean,或者找到了多个,都会报错。@Autowired(required=false),可以设置required属性为false,避免找不到bean的报错。对于多个bean的错误,可以通过@Resource设置name或@Qualifier或@Primary来选择指定的bean。

4)三种方式的比较:

组件扫描、自动装配是最方便的方式,但该方法生成的bean散落在项目中的各个地方,查找不便,且只能对项目内部定义的类进行组件扫描。

XML的方式将所有bean的生成配置在若干张XML文件中,统一配置,统一管理,但是当项目中所需的bean非常多时,也会导致XML文件过于庞大。且该方式只能调用类的默认构造函数进行bean的生成,且class一旦配错,在编译器无法发现。

JavaConfig的方式在所需生成bean的个数非常多时,也会有配置文件过大的问题,但是其可以使用任意一种方式产生bean,比XML更加灵活。

我个人比较喜欢组件扫描配合XML的方式来进行配置。

猜你喜欢

转载自my.oschina.net/u/3498791/blog/1785021