装配Bean是Spring的IOC中必不可少的一步,本文介绍通过XML方式和通过注解的方式进行Bean装配的过程。


通过XML方式配置

使用之前果汁的例子,xml的装配过程如下:

<bean name="juice" class="pojo.Juice">     
    <property name="type" value="草莓"/>     
    <property name="suger" value="5分糖"/>     
    <property name="size" value="中杯"/> 
</bean> 
<bean name="order" class="pojo.Order">     
    <property name="juice" ref="juice"/>
 </bean>

其中name属性定义bean元素的名称,能以逗号或者空格隔开起多个别名,并且可以使用很多的特殊字符。还可以使用id,id属性是Spring属性能找到当前Bean的一个依赖的编号。id和name都可以用来定义bean的名称。

如果id和name都没有声明的话,Spring会采用“全限定名#{number}”的方式生成编号,如上面juice没有定义的话编号就是“pojo.Juice#1”

class属性是一个类的全限定名,通俗讲就是类的位置

property定义类的属性,name是属性的名称,value是值,ref属性引用对应的bean名称


集合的装配过程如下:

首先我们写一个类包含各种集合:

public class ComplexAssmble {
    private Long id;
    private List<Juice> list;
    private Map<String,String> map;
    private Properties properties;
    private Set<String> set;
    private String[] array;
     //Getter and Setter
}

xml文件配置如下

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean name="juice" class="pojo.Juice">
        <property name="type" value="草莓"/>
        <property name="suger" value="5分糖"/>
        <property name="size" value="中杯"/>
    </bean>

    <bean id="complexAssembly" class="pojo.ComplexAssmble">
        <property name="id" value="1"/>
        <property name="list">
            <list>
               <ref bean="juice"/>
            </list>
        </property>

        <property name="map">
            <map>
                <entry key="key1" value="value-key-1"/>
                <entry key="key2" value="value-key-2"/>
                <entry key="key3" value="value-key-3"/>
            </map>
        </property>

        <property name="properties">
            <props>
                <prop key="prop1">value-prop-1</prop>
                <prop key="prop2">value-prop-2</prop>
                <prop key="prop3">value-prop-3</prop>
            </props>
        </property>
        <property name="set">
            <set>
                <value>value-set-1</value>
                <value>value-set-2</value>
                <value>value-set-3</value>
            </set>
        </property>
        <property name="array">
            <array>
                <value>value-array-1</value>
                <value>value-array-2</value>
                <value>value-array-3</value>
            </array>
        </property>
    </bean>
</beans>
  1. List 属性为对应的 <list> 元素进行装配,然后通过多个 <value> 元素设值,当为集合注入时,使用多个 <ref> 元素的 Bean 属性去引用之前定义好的 Bean
  2. Map 属性为对应的 <map> 元素进行装配,然后通过多个 <entry> 元素设值,只是 entry 包含一个键值对(key-value)的设置,当为集合注入时,使用多个 <entry> 元素的 key-ref 属性去引用之前定义好的 Bean 作为键,而用 value-ref 属性引用之前定义好的 Bean 作为值
  3. Properties 属性为对应的 <properties> 元素进行装配,通过多个 <property> 元素设值,只是 properties 元素有一个必填属性 key ,然后可以设置值
  4. Set 属性为对应的 <set> 元素进行装配,然后通过多个 <value> 元素设值,当为集合注入时,使用多个 <ref> 元素的 bean 去引用之前定义好的 Bean
  5. 对于数组而言,可以使用 <array> 设置值,然后通过多个 <value> 元素设值。

说明:

property:通过setter对应的方法注入。

constructor-arg:通过构造函数注入。

下面介绍用constructor-arg注入:

首先新建一个带构造函数的Teacher类:

public class Teacher {
    private int age;
    private String name;
    private List<String> justtest;

    public Teacher(){}
    public Teacher(int age, String name, List<String> justtest) {
        this.age = age;
        this.name = name;
        this.justtest = justtest;
    }
    //Getter and Setter
}

XML文件中通过构造函数注入bean:

<bean id="teacher" class="pojo.Teacher">
    <constructor-arg name="age" value="45"/>
    <constructor-arg name="name" value="Bob"/>
    <constructor-arg name="justtest">
        <list>
            <value>test1</value>
            <value>test2</value>
            <value>test3</value>
        </list>
    </constructor-arg>
</bean>

如果每条语句都要写constructor-arg或者property 会显得很麻烦,因此Spring引入了命名空间

1.c命名空间

c-命名空间是在 Spring 3.0 中引入的,它是在 XML 中更为简洁地描述构造器参数的方式

首先在xml的顶部声明:

xmlns:p="http://www.springframework.org/schema/p"
<!--普通的xml方式装配-->
<bean name="juice" class="pojo.Juice">
    <property name="type" value="草莓"/>
    <property name="suger" value="5分糖"/>
    <property name="size" value="中杯"/>
</bean>
<!--p命名空间装配-->
<bean name="juice2" class="pojo.Juice" p:type="草莓" p:suger="5分糖" p:size="中杯"/>

注意:c命名空间装配要以c:开头,如果装配的是对象,则需要加上 -ref ,idea上有很友好的提示功能

2.p命名空间

p命名空间是用setter的方式装配bean,首先也需要在xml的顶部声明:

xmlns:p="http://www.springframework.org/schema/p"
<!--普通的xml方式装配-->
<bean name="juice" class="pojo.Juice">
    <property name="type" value="草莓"/>
    <property name="suger" value="5分糖"/>
    <property name="size" value="中杯"/>
</bean>
<!--p命名空间装配-->
<bean name="juice2" class="pojo.Juice" p:type="草莓" p:suger="5分糖" p:size="中杯"/>

同样的如果装配的是对象,则加入 ref。

3.util命名空间

util命名空间可以简化集合类的装配,同样需要现在xml的顶部声明,注意要用util工具时还需要在xsi:schemaLocation中加入以下两项地址

xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd
<!--普通xml方式装配集合-->
<property name="map">
    <map>
        <entry key="key1" value="value-key-1"/>
        <entry key="key2" value="value-key-2"/>
        <entry key="key3" value="value-key-3"/>
    </map>
</property>
<!--util方式装配集合-->
<util:map id="map2">
    <entry key="key1" value="value-key-1"/>
    <entry key="key2" value="value-key-2"/>
    <entry key="key3" value="value-key-3"/>
</util:map>

其他集合方式方法相同,idea会有友好的提示

通过注解方式配置

学习完xml方式装配之后,我们会发现如果需要装配的内容很多时xml文件会很臃肿,因此我们会更多的使用注解的方式进行装配:

在 Spring 中,它提供了两种方式来让 Spring IoC 容器发现 bean:

  1. 组件扫描:通过定义资源的方式,让 Spring IoC 容器扫描对应的包,从而把 bean 装配进来。
  2. 自动装配:通过注解定义,使得一些依赖关系可以通过注解完成。


1.使用Component注解

@Component("teacher1")
public class Teacher {
    @Value("45")
    private int age;
    @Value("李华")
    private String name;

在之前的teacher类中进行修改,增加注解,上面的这段代码相当于之前使用构造器注入或者setter注入:

<bean id="teacher" class="pojo.Teacher">
    <property name="age" value="45"/>
    <property name="name" value="Bob"/>
</bean>

@Component注解:

表示 Spring IoC 会把这个类扫描成一个 bean 实例,而其中的value属性代表这个类在 Spring 中的id,如果Component不写参数,Spring IoC 容器就默认以类名首字母小写来命名作为 id,配置到容器中。

@Value注解:

表示值的注入,跟在 XML 中写 value 属性是一样的。

然后新建一个类去扫描这些注解,注意新建的类需要和Teacher放在同一个目录之下,用ComponentScan进行扫描。ComponentScan可以选择basePackages和basePackageClasses两个配置项,使用方法如下:

  1. basePackages它是由 base 和 package 两个单词组成的,而 package 还是用了复数,意味着它可以配置一个 Java 包的数组,Spring 会根据它的配置扫描对应的包和子包,将配置好的 Bean 装配进来
  2. basePackageClasses:它由 base、package 和 class 三个单词组成,采用复数,意味着它可以配置多个类, Spring 会根据配置的类所在的包,为包和子包进行扫描装配对应配置的 Bean
//默认扫描所在包的java类
@ComponentScan
public class TeacherScan {
}
//第二种方法
@ComponentScan(basePackages = "pojo")
public class TeacherScan{
}
//第三种方法
@ComponentScan(basePackageClasses = pojo.Teacher.class)
public class TeacherScan{
}

接下来就可以测试了,注意这里不能再使用ClassPathXmlApplicationContext

 

@Test
public void test4(){
    ApplicationContext context=new AnnotationConfigApplicationContext(TeacherScan.class);
    Teacher teacher= (Teacher) context.getBean("teacher1");
    System.out.println("age is "+teacher.getAge()+" name is " +teacher.getName());
}

测试结果如下:

java bean配置xmlElement_Spring

达到了注入的效果,但是用@Value无法注入对象等,具有一定的局限性。


2.自动装配 @Autowired

自动装配是指由Spring自己发现对应的bean,并且完成装配的过程。

在目录下新建一个service包,首先建一个接口TeacherService,包含一个输出的方法:

package service;
public interface TeacherService {
    public void teacherinfo();
}

新建TeacherServiceImpl实现类,使用Autowired注释自动装配:

@Component("teacherserver")
public class TeacherServiceImpl implements TeacherService{
    @Autowired
    private Teacher teacher=null;
    @Override
    public void teacherinfo() {
        System.out.println("age is "+teacher.getAge()+" name is " +teacher.getName());
    }
}

在TeacherScan类的注解中加上对service包的扫描:

@ComponentScan(basePackages = {"pojo","service"})
public class TeacherScan{
}

测试结果发现已经自动装配成功:

@Test
public void test5(){
    ApplicationContext context=new AnnotationConfigApplicationContext(TeacherScan.class);
    TeacherService service= (TeacherService) context.getBean("teacherserver");
    service.teacherinfo();
}

包扫描还可以定义在xml文件中,如下所示:

<context:component-scan base-package="pojo"/>
<context:component-scan base-package="service"/>

测试代码需要相应进行修改:

@Test
public void test6(){
    ApplicationContext context=new ClassPathXmlApplicationContext("mytestxml.xml");
    TeacherService service= (TeacherService) context.getBean("teacherserver");
    service.teacherinfo();
}

运行结果

java bean配置xmlElement_xml_02

@Autowired注解在 Spring IoC 定位所有的 Bean 后,再根据类型寻找资源,然后将其注入。

运行过程:定义 Bean —> 初始化 Bean(扫描) —>根据属性需要从 Spring IoC 容器中搜寻满足要求的 Bean —> 满足要求则注入

自动注入时可能会遇到的问题:

上面的Autowired会自动注入类型为Teacher的资源,但是当有两个Teacher资源时,Autowired就不知道选哪一个,新建一个xml文件用来测试同时有两个Teacher资源时Autowired如何选择:

<bean id="teacher1" class="pojo.Teacher">
    <constructor-arg name="age" value="45"/>
    <constructor-arg name="name" value="Bob"/>
    <constructor-arg name="justtest">
        <list>
            <value>test1</value>
        </list>
    </constructor-arg>
</bean>
<bean id="teacher2" class="pojo.Teacher">
    <constructor-arg name="age" value="35"/>
    <constructor-arg name="name" value="Amy"/>
    <constructor-arg name="justtest">
        <list>
            <value>test1</value>
        </list>
    </constructor-arg>
</bean>

此时测试时发现Autowired装配的一直是teacher1,如何装配teacher2呢?Spring 提供了两个注解:

@Primary 注解:

代表首要的,当 Spring IoC 检测到有多个相同类型的 Bean 资源的时候,会优先注入使用该注解的类。

@Qualifier 注解:

上面所谈及的歧义性,一个重要的原因是 Spring 在寻找依赖注入的时候是按照类型注入引起的。除了按类型查找 Bean

在上述的service实现类中加上这个注解:

@Autowired @Qualifier("teacher2") private Teacher teacher=null;

最后成功装配teacher2资源。


以上都是使用Component注解来装配bean,但是Component只能用在类上,当需要注解到方法之上时,就需要用到@Bean,测试一下@Bean

package pojo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//configuration相当于 XML 文件的根元素,必须有
@Configuration
public class BeanTest {
    @Bean(name = "beantest")
    public String printsomething(){
        return "hello Spring";
    }
}
@Test
public void test8(){
    ApplicationContext context=new AnnotationConfigApplicationContext("pojo");
    String str= (String) context.getBean("beantest");
    System.out.println(str);
}

Bean的配置项中包含 4 个配置项:

name 是一个字符串数组,允许配置多个 BeanName

autowire 标志是否是一个引用的 Bean 对象,默认值是 Autowire.NO

initMethod 自定义初始化方法

destroyMethod 自定义销毁方法

Bean注解的好处就是能够动态获取一个 Bean 对象,能够根据环境不同得到不同的 Bean 对象。