5.Spring4.x学习[核心篇][Spring在IOC中装配Bean](1)

上一篇讲了Spring中的IOC,该篇要具体讲解如何通过IOC对Bean进行注入
首先看一下Bean的元数据信息:

  1. Bean的实现类
  2. Bean的属性
  3. Bean的依赖关系
  4. Bean的行为配置
    Bean的元数据信息在Spring容器中对应的是一个BeanDefinition 形成的Bean注册表。Spring4.x支持的配置方式如下:

1.基于XML文件配置
2.基于注解配置
3.Java类的配置
4.Groovy动态语言配置

主要说下前两种。

  • 基于XML的配置
    基于XML的配置是开发过程中常用的方式,因为XML配置文件可以对配置归档,便于统一管理,结构化的XML树相对比较直观。对于XML的配置方式,会把Bean相关的注入方式,Bean之间关系,Bean的作用域等知识点一起讲解
    准备工作:引入Spring4.0的命名空间到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-4.0.xsd">

</beans>

下面看一个Spring HelloWorld 程序:
首先配置要注入的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-4.0.xsd">
     <bean id="car" name="#car1" class="com.smart.simple.Car"></bean>
</beans>

只需要在Beans节点下配置一个Bean节点,并指定Bean的id,class,然后通过启动Spring容器,getBean(name)的方式获取到Bean的信息。

public class BeanRetrieveTest {

    public ApplicationContext factory = null;

    private static String[] CONFIG_FILES = {"com/smart/simple/beans.xml"};

    @BeforeClass
    public void setUp() throws Exception {
        //获取到Spring容器
        factory = new ClassPathXmlApplicationContext(CONFIG_FILES);
    }

    @Test
    public void testBeanRetrieve(){
        Car car = (Car)factory.getBean("#car1");
        assertNotNull(car);
    }
}

@BeforeClass 是 junit单元测试的注释,可以通过反射优先于测试单元执行,用来初始化测试单元中的外部需求。
XML中Bean节点的id属性,和name属性,都可以指定多个,用;,等隔开(推荐只用id即可)。而class一定要配置全限定类名。spring容器的反射也是基于全类名去查找对应的Class,进而实例化Bean的。

流程就是两步走:
1.读取xml文件,获取Bean信息
2.实例化Spring容器,并通过getBean获取到注入的Bean
getBean还有个传入Class的获取Bean的方式:

@Test
    public void testBeanRetrieve(){
        Car car = (Car)factory.getBean(Car.class);
        assertNotNull(car);
    }

这种方式,指定在容器中同一class类型的Bean只能有一个,否则就会抛org.springframework.beans.factory.NoUniqueBeanDefinitionException异常

Spring 依赖注入方式

  1. 属性注入
    属性注入要求Bean提供一个默认构造函数(无参),并为需要的属性提供对应的setter方法。Spring先调用默认的构造器初始化Bean,然后通过反射方式调用setter方法注入属性值。例子:
public class Car {
    private int maxSpeed;
    public String brand;
    private double price;

    public static  String  HONG_QI = "红旗";
    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public int getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(int maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String toString(){
        return "brand:"+brand+"/maxSpeed:"+maxSpeed+"/price:"+price;
    }

}
<bean id="car" class="com.smart.ditype.Car">
        <property name="brand" value="红旗&amp;CA72"/>
        <property name="maxSpeed" value="200"/>
        <property name="price" value="20000.00"/>
    </bean>
public class DiTypeTest {
    public ApplicationContext factory = null;

    private static String[] CONFIG_FILES = { "com/smart/ditype/beans.xml" };

    @BeforeClass
    public void setUp() throws Exception {
        factory = new ClassPathXmlApplicationContext(CONFIG_FILES);
    }

    @Test
    public void testCar(){
        Car car = (Car)factory.getBean("car");
        assertNotNull(car);
        System.out.println(car);
    }
}

Spring只会检查是否有对应的setter方法,至于Bean中是否要求有成员属性的变更则不会要求。这样注入在获取属性的时候也不会出问题,但是Java Bean的规范还是要求getXXX能与属性名字保持一致。
JavaBean的属性命名规范:property元素指定的属性名和Bean中setter方法中满足:xxx属性对应setXxx()方法。
特殊情况,对于特定意义的英文大写缩略词,如USA,变量的前两个字母要不全小写,要不全部大写的规范。
错误的命名规范会导致报错。

  1. 构造函数注入
    1.按类型匹配
    public Car(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }   
    <!--构造函数注入:type -->
    <bean id="car1" class="com.smart.ditype.Car">
        <constructor-arg type="java.lang.String">
            <value>红旗CA72</value>
        </constructor-arg>
        <constructor-arg type="double">
            <value>20000</value>
        </constructor-arg>
    </bean>

2.按索引匹配入参

<!--构造函数注入:index -->
<bean id="car2" class="com.smart.ditype.Car">
        <constructor-arg index="0" value="红旗CA72" /> <constructor-arg
        index="1" value="中国一汽" /> <constructor-arg index="2" value="20000" />
        </bean>

3.混合匹配入参

    <!--构造函数注入:type&index -->
    <bean id="car3" class="com.smart.ditype.Car">
        <constructor-arg index="0" type="java.lang.String">
            <value>红旗CA72</value>
        </constructor-arg>
        <constructor-arg index="1" type="java.lang.String">
            <value>中国一汽</value>
        </constructor-arg>
        <constructor-arg index="2" type="int">
            <value>200</value>
        </constructor-arg>
    </bean>
    <bean id="car4" class="com.smart.ditype.Car">
        <constructor-arg index="0">
            <value>红旗CA72</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>中国一汽</value>
        </constructor-arg>
        <constructor-arg index="2" type="int">
            <value>200</value>
        </constructor-arg>
    </bean>

在构造参数中,Java通过自身反射可以自动识别匹配要配置的参数的类型。

public class Boss {
    private String name;
    private Car car;
    private Office office;


    public Boss(String name, Car car, Office office) {
        this.name = name;
        this.car = car;
        this.office = office;
    }

    public Boss(String name, Car car) {
        this.name = name;
        this.car = car;
    }

    public Boss() {

    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public String toString(){
        return "name:"+name+"/car:"+car.getBrand()+"/office:"+office;
    }
}
    <bean id="boss1" class="com.smart.ditype.Boss">
        <constructor-arg>
            <value>John</value>
        </constructor-arg>
        <constructor-arg>
            <ref bean="car" />
        </constructor-arg>
        <constructor-arg>
            <ref bean="office" />
        </constructor-arg>
    </bean>
    <bean id="office" class="com.smart.ditype.Office" />

循环依赖问题
Spring容器对构造函数配置Bean进行实例化有个前提,就是Bean构造函数入参引用的对象必须已经准备就绪。所以,如果两个Bean都采用构造注入,并且互相引用对方,就会导致类似于线程死锁问题。

public class Boss {
    private String name;
    private Car car;

    public Boss(String name, Car car) {
        this.name = name;
        this.car = car;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

}
public class Car {
    public String brand;
    private String corp;    
    private double price;
    private Boss boss;


    public Car(String brand, Boss boss) {
        this.brand = brand;
        this.boss= boss;
    }   


    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public Boss getBoss() {
        return boss;
    }

    public void setBoss(Boss boss) {
        this.boss= boss;
    }

}
    <bean id="boss3" class="com.smart.ditype.Boss">
        <constructor-arg>
            <value>John</value>
        </constructor-arg>
        <constructor-arg>
            <ref bean="carRef" />
        </constructor-arg>
    </bean>
    <bean id="carRef" class="com.smart.ditype.Car">
        <constructor-arg><value>BMW</value></constructor-arg>
        <constructor-arg><ref bean="boss3"/></constructor-arg>
    </bean>

这样就会出现循环依赖的问题。报了如下错误:
Error creating bean with name ‘boss3’: Requested bean is currently in creation: Is there an unresolvable circular reference?

  1. 工厂方法注入
    1.非静态工厂
public class CarFactory {
   //指定非静态工厂方法
   public Car createHongQiCar(){
       Car car = new Car();
       car.setBrand("红旗CA72");
       return car;
   }

   public static Car createCar(){
       Car car = new Car();
       return car;
   }
}
    <!-- 非静态工厂方法 指定factoryBean 和factoryMethod-->
    <bean id="carFactory" class="com.smart.ditype.CarFactory" />
    <bean id="car5" factory-bean="carFactory" factory-method="createHongQiCar">
    </bean>

2.静态工厂

<!-- 静态工厂方法 只需要指定静态的 factoryMethod-->
<bean id="car6" class="com.smart.ditype.CarFactory"
        factory-method="createCar"></bean>
  1. 注入参数详解
    1.字面值:一般指可以使用字符串表示的值,使用value元素标签注入
    <bean id="car" class="com.smart.attr.Car">
        <property name="maxSpeed">
            <value>200</value>
        </property>
        <property name="brand">
            <value>
                <![CDATA[红旗&CA72]]>
            </value>
        </property>
        <property name="price" value="2000.00" />
    </bean>

通过属性注入,可以把字面值直接注入到对应的属性中,<![CDATA[红旗&CA72]]>用来让xml标签对注入的内容当做普通字符处理。XML中有&,<,>,”,’ 一共5个特殊字符需要特别处理。也可以通过转义序列来进行处理。

2.引用其他Bean

    <bean id="boss" class="com.smart.attr.Boss">
        <property name="car" ref="car" />
        <property name="name" value="Tom" />
        <property name="age" value="45" />
    </bean>

引用ref可以通过配置属性local和parent进行不同Bean的引用,此处不常用。
3. 内部Bean
如果car Bean只被Boss引用,那么可以在Boss内部声明一个Bean

    <!-- 内部Bean -->
    <bean id="boss2" class="com.smart.attr.Boss">
        <property name="car">
            <bean class="com.smart.attr.Car">
                <property name="maxSpeed" value="200" />
                <property name="price" value="2000.00" />
            </bean>
        </property>
    </bean>
  1. null值引用
    <bean id="car" class="com.smart.attr.Car" lazy-init="default">
        <property name="brand">
            <value>
                <null/>
            </value>
        </property>
        <property name="maxSpeed">
            <value>200</value>
        </property>
        <property name="price" value="2000.00" />
    </bean>

5.级联属性
当一个Bean中需要对另外一个Bean中的属性赋值时,就用到了级联属性

    <bean id="boss" class="com.smart.attr.Boss">
        <property name="car.brand" value="BMW"/>
    </bean>

6.集合类型属性

    <!-- 引用Bean -->
    <bean id="boss1" class="com.smart.attr.Boss">
        <property name="car" ref="car" />
        <property name="favorites">
            <!-- list>
                <value>看报</value>
                <value>赛车</value>
                <value>高尔夫</value>
                </list -->
            <set>
                <value>看报</value>
                <value>赛车</value>
                <value>高尔夫</value>
            </set>
        </property>
        <property name="jobs">
            <map>
                <entry >
                    <key>
                        <value>AM</value>
                    </key>
                    <value>会见客户</value>
                </entry>
                <entry>
                    <key>
                        <value>PM</value>
                    </key>
                    <value>公司内部会议</value>
                </entry>
            </map>
        </property>
        <property name="mails">
            <props>
                <prop key="jobMail">john-office@smart.com</prop>
                <prop key="lifeMail">john-life@smart.com</prop>
            </props>
        </property>
        <property name="jobTime">
            <map>
                <entry>
                    <key>
                        <value>会见客户</value>
                    </key>
                    <value>124</value>
                </entry>
            </map>
        </property>
    </bean>

集合除了上述方式配置,还可以通过util标签简化集合配置。

7.简化配置方式
通过p标签对配置进行简化

  <bean id="car" class="com.smart.ditype.Car"
      p:brand="红旗&amp;CA72"
      p:maxSpeed="200"
      p:price="20000.00"/>
    <bean id="boss" class="com.smart.ditype.Boss"
      p:car-ref="car"/>

8.自动装配
1、创建CumputerBean类

package www.csdn.spring.autowire.bean;
public class CumputerBean {
// 电脑名称
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "CumputerBean [name=" + name + "]";
}
}

2、创建DeptBean 类

package www.csdn.spring.autowire.bean;
public class DeptBean {
//部门名称
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "DeptBean [name=" + name + "]";
}
}

3、创建EmployeeBean

package www.csdn.spring.autowire.bean;
public class EmployeeBean {
private DeptBean deptBean;
private CumputerBean cumputerBean;
public void setDeptBean(DeptBean deptBean) {
this.deptBean = deptBean;
}
public void setCumputerBean(CumputerBean cumputerBean) {
this.cumputerBean = cumputerBean;
}
@Override
public String toString() {
return "EmployeeBean [deptBean=" + deptBean + ", cumputerBean="
+ cumputerBean + "]";
}
}

首先分析no、byName、byType的配置都是采用setter方法依赖注入实现的案例
1、no配置(通过ref=””引用需要的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">
<!-- 电脑bean -->
<bean id="cumputerBean" class="www.csdn.spring.autowire.bean.CumputerBean">
<property name="name" value="HP6325笔记本" />
</bean>
<!-- 部门bean -->
<bean id="deptBean" class="www.csdn.spring.autowire.bean.DeptBean">
<property name="name" value="CSDN教育事业部" />
</bean>
<!-- 员工bean  根据EmployeeBean中的 属性名称  去匹配-->
<bean id="employeeBean" class="www.csdn.spring.autowire.bean.EmployeeBean">
<property name="cumputerBean" ref="cumputerBean" />
<property name="deptBean" ref="deptBean" />
</bean>
</beans>

2、byName配置(分析:会根据EmployeeBean中属性的名称 自动装配)

<?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 -->
<bean id="cumputerBean" class="www.csdn.spring.autowire.bean.CumputerBean">
<property name="name" value="HP6325笔记本" />
</bean>
<!-- 部门bean -->
<bean id="deptBean" class="www.csdn.spring.autowire.bean.DeptBean">
<property name="name" value="CSDN教育事业部" />
</bean>
<!-- 员工bean-->
<bean id="employeeBean" class="www.csdn.spring.autowire.bean.EmployeeBean" autowire="byName"/>
</beans>

3、byType配置(分析:会根据EmployeeBean中属性的类型 自动装配)

<?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 -->
<bean id="cumputerBean" class="www.csdn.spring.autowire.bean.CumputerBean">
<property name="name" value="HP6325笔记本" />
</bean>
<!-- 部门bean -->
<bean id="deptBean" class="www.csdn.spring.autowire.bean.DeptBean">
<property name="name" value="CSDN教育事业部" />
</bean>
<!-- 员工bean  根据EmployeeBean中的 属性名称  去匹配-->
<bean id="employeeBean" class="www.csdn.spring.autowire.bean.EmployeeBean" autowire="byType"/>
</beans>

注意:当根据byType类型装配时,当在容器内找到多个匹配的类型时会出现如下error

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'employeeBean' defined in class path resource [spring-byType.xml]: Unsatisfied dependency expressed through bean property 'deptBean': : No qualifying bean of type [www.csdn.spring.autowire.bean.DeptBean] is defined: expected single matching bean but found 2: deptBean,deptBean1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [www.csdn.spring.autowire.bean.DeptBean] is defined: expected single matching bean but found 2: deptBean,deptBean1

4、Constructor(构造器参数根据byType类型匹配,自动装配)
首先修改EmployeeBean类 修改后代码如下:

package www.csdn.spring.autowire.bean;
public class EmployeeBean {
private DeptBean deptBean;
private CumputerBean cumputerBean;
public EmployeeBean(DeptBean deptBean, CumputerBean cumputerBean) {
super();
this.deptBean = deptBean;
this.cumputerBean = cumputerBean;
}
@Override
public String toString() {
return "EmployeeBean [deptBean=" + deptBean + ", cumputerBean="
+ cumputerBean + "]";
}
}

配置文件操作:

 <?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 -->
<bean id="cumputerBean" class="www.csdn.spring.autowire.bean.CumputerBean">
<property name="name" value="HP6325笔记本" />
</bean>
<!-- 部门bean -->
<bean id="deptBean" class="www.csdn.spring.autowire.bean.DeptBean">
<property name="name" value="CSDN教育事业部" />
</bean>
<!-- 员工bean 根据EmployeeBean中的 属性名称 bytype 去匹配 -->
<bean id="employeeBean" class="www.csdn.spring.autowire.bean.EmployeeBean"
autowire="constructor">
</bean>
</beans>

接下来的内容,会涉及到Bean之间关系等。会独立为另外的文章,接下篇。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值