Spring Framework IOC容器 Bean管理

利用XML进行Bean管理

基本注入

基于 xml 配置文件的 Bean 管理有三种基本注入方式:基于 Setter 方法注入、基于有参构造方法注入、基于p命名空间注入

<!--  通过Setter方法注入  -->
<bean id="book1" class="com.company.po.Book">
    <property name="name">
        <value><![CDATA[天龙八部]]></value>
    </property>
    <property name="author" value="金庸"/>
</bean>

<!--  通过有参构造方法注入  -->
<bean id="order" class="com.company.po.Order">
    <constructor-arg name="name" value="电脑"/>
    <constructor-arg name="address" value="中国"/>
</bean>

<!--  通过p命名空间注入  -->
<bean id="book2" class="com.company.po.Book" p:author="古龙" p:name="小鱼儿与花无缺"/>

内外部 Bean 注入

外部 Bean 的注入方式

<!-- 外部bean -->
<bean id="userService" class="com.company.service.UserService">
    <property name="userDao" ref="userDao"/>
</bean>

<bean id="userDao" class="com.company.dao.UserDaoImpl"/>

内部 Bean 的注入方式

<bean id="emp" class="com.company.bean.Emp">
    <property name="name" value="张三"/>
    <property name="gender" value="男"/>

    <!-- 设置内部对象属性 -->
    <property name="dept">
        <bean id="dept" class="com.company.bean.Dept">
            <property name="name" value="法律部"/>
        </bean>
    </property>
</bean>

集合属性注入

<bean id="stu" class="com.company.bean.collections.Stu">
    <property name="courseArray">
        <array>
            <value>课程1</value>
            <value>课程2</value>
        </array>
    </property>

    <property name="courseList">
        <list>
            <value>课程3</value>
            <value>课程4</value>
        </list>
    </property>

    <property name="courseMap">
        <map>
            <entry key="课程5" value="JAVA"/>
            <entry key="课程6" value="MySQL"/>
        </map>
    </property>

    <property name="courseSet">
        <set>
            <value>课程7</value>
            <value>课程8</value>
        </set>
    </property>
</bean>

FactoryBean

除了普通 Bean之外,还有一种工厂Bean,又称 FactoryBean,返回的对象不是指定类的实例,而是该 FactoryBean的getObject方法返回的对象。

  • 普通 Bean:在配置文件中定义的Bean的类型就是返回类型
  • 工厂 Bean:在配置文件中定义的Bean的类型可以和返回类型不同

创建自定义 FactoryBean 的方式

  1. 第一步:创建类,让这个类作为工程Bean,实现接口 FactoryBean
  2. 第二步:实现 getObject 方法,在实现的方法中定义返回到饿Bean类型
public class MyBean implements FactoryBean<Book> {

    // 返回定义的对象
    @Override
    public Book getObject() throws Exception {
        Book book = new Book();
        book.setAuthor("刘德华");
        book.setName("JAVA核心技术");
        return book;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

关于 FactoryBean 的细节可参考 文章,总结以下四点:

  1. FactoryBean 是 BeanFactory 支持的、用来暴露Bean实例的接口,你可以通过实现接口里面的getObject() 方法来实例化比较复杂的对象
  2. 通过 bean name从 Spring 容器中获取 Bean实例时,首先会获取 bean name 直接关联的 Bean实例,如果 Bean实例不是 FactoryBean 类型,则直接返回 Bean实例,否则,调用 getObject() 方法返回;
  3. 相比于普通 Bean,Spring中会多维护一个FactoryBean的实例,如果想获取FactoryBean实例对象,只需要在FactoryBean的 bean name 前加上 & 符号即可。
<bean id="myBean" class="com.company.factorybean.MyFactoryBean"/>
@Test
public void testFactoryBean() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");
    Object book = context.getBean("&myBean");
    System.out.println(book);
}

Bean 作用域

  1. 默认情况是单实例对象,也就是 Singleton 的
  2. 利用 scope 属性可以配置Bean为单实例还是多实例,singleton 和 prototype
@Test
public void testNamespace() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Order order1 = context.getBean("order", Order.class);
    Order order2 = context.getBean("order", Order.class);
    System.out.println(order1);
    System.out.println(order2);
}

singleton 和 prototype 区别:singleton的实例化在加载配置文件时候完成,prototype则是在getBean的时候才会创建,也就是创建的时机不一样。

Bean 的生命周期

  1. 通过无参数构造器创建Bean实例
  2. 为Bean的属性设置值和其他Bean的引用
  3. 把Bean的实例传递给Bean后置处理器的方法 postProcessBeforeInitialization
  4. 调用Bean的初始化方法 — 需要配置 init-method
  5. 把Bean的实例传递给Bean后置处理器的方法 postProcessAfterInitialization
  6. 使用Bean
  7. 当容器关闭,调用Bean的销毁方法 — 需要配置 destroy-method
<bean id="dept" class="com.company.bean.Dept" init-method="initMethod" destroy-method="destroyMethod">
    <property name="name" value="法务部"/>
</bean>

<!--  配置后置处理器,将会对当前配置文件中的所有Bean都添加后置处理器  -->
<bean id="myPostBean" class="com.company.bean.MyPostBean"/>
public class Dept {
    private String name;

    public Dept() { System.out.println("第一步:无参数构造方法"); }

    public void setName(String name) {
        this.name = name;
        System.out.println("第二步:Set方法设置属性");
    }

    public void initMethod() { System.out.println("第四步:初始化方法"); }
    public void destroyMethod() { System.out.println("第七步:销毁方法"); }
}
@Test
public void testDept() {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");
    Dept dept = context.getBean("dept", Dept.class);
    System.out.println("第六步:使用对象");
    context.close();
}

最后输出结果:

第一步:无参数构造方法
第二步:Set方法设置属性
第三步:在初始化之前执行的方法
第四步:初始化方法
第五步:在初始化之前执行的方法
第六步:使用对象
第七步:销毁方法

基于XML自动装配

根据指定的装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入
Bean 标签属性autowire,包含两种方式:byName、byType

外部属性文件

例如把数据库相关配置放在外部文件中

<?xml version="1.0" encoding="UTF-8"?>
<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 id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <context:property-placeholder location="classpath:jdbc.properties"/>

</beans>

基于注解进行Bean管理

注解的格式 @注解名称(属性名称=属性值),可以简化配置

基于注解创建对象

  1. @Component
  2. @Service
  3. @Controller
  4. @Repository
    以上四个注解功能是一样的

基于注解方式属性注入

  1. @Autowired:把对象根据属性类型
  2. @Qualifier:把对象根据属性名称
  3. @Resource:把对象根据属性类型或名称进行注入
  4. @Value:注入普通类型属性