Spring-框架基础02Bean的生命周期作用域装配总结

7次阅读

共计 7176 个字符,预计需要花费 18 分钟才能阅读完成。

本文源码:GitHub·点这里 || GitEE·点这里

一、装配方式

Bean 的概念:Spring 框架管理的应用程序中,由 Spring 容器负责创建,装配,设置属性,进而管理整个生命周期的对象,称为 Bean 对象。

1、XML 格式装配

Spring 最传统的 Bean 的管理方式。

  • 配置方式
<bean id="userInfo" class="com.spring.mvc.entity.UserInfo">
    <property name="name" value="cicada" />
</bean>
  • 测试代码
ApplicationContext context01 = new ClassPathXmlApplicationContext("/bean-scan-02.xml");
UserInfo userInfo = (UserInfo)context01.getBean("userInfo") ;
System.out.println(userInfo.getName());

2、注解扫描

在实际开发中:通常使用注解 取代 xml 配置文件。

  • 常见注解
@Component <==> <bean class="Class">
@Component("id") <==> <bean id="id" class="Class">
@Repository:Mvc 架构中 Dao 层 Bean 的注解
@Service:Mvc 架构中 Service 层 Bean 的注解
@Controller:Mvc 架构中 Controller 层 Bean 的注解 
  • 使用案例
// 1、注解代码块
@Component("infoService")
public class InfoServiceImpl implements InfoService {
    @Override
    public void printName(String name) {System.out.println("Name:"+name);
    }
}

// 2、配置代码块
@ComponentScan // 组件扫描注解
public class BeanConfig {}
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeanConfig.class)
public class Test01 {
    @Autowired
    private InfoService infoService ;
    @Test
    public void test1 (){infoService.printName("cicada");
        System.out.println(infoService==infoService);
    }
}

3、XML 配置扫描

上面使用 ComponentScan 注解,也可在配置文件进行统一的配置,效果相同,还简化代码。

<context:component-scan base-package="com.spring.mvc" />

4、Java 代码装配

这种基于 Configuration 注解,管理 Bean 的创建,在 SpringBoot 和 SpringCloud 的框架中,十分常见。

  • 配置类代码
@Configuration // 配置类注解
public class UserConfig {
    @Bean
    public UserInfo userInfo (){System.out.println("userInfo...");
        return new UserInfo() ;}
}
  • 测试代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = UserConfig.class)
public class Test03 {
    @Autowired
    private UserInfo userInfo ;
    @Autowired
    private UserInfo userInfo1 ;
    @Test
    public void test1 (){
        /*
         * userInfo...
         * true
         */
        System.out.println(userInfo==userInfo1);
    }
}

二、属性值设置

上面是 Bean 的装配几种常见方式,下面来看看 Bean 属性值设置,这里就基于 Xml 配置的方式。

1、基础类型和集合

  • 配置代码
<!-- 配置 Employee 公共属性 -->
<bean id="emp1" class="com.spring.mvc.entity.Employee">
    <property name="name" value="cicada" />
    <property name="id" value="1" />
</bean>
<bean id="emp2" class="com.spring.mvc.entity.Employee">
    <property name="name" value="smile" />
    <property name="id" value="2" />
</bean>
<!-- 配置 Department 属性 -->
<bean id="department" class="com.spring.mvc.entity.Department">
    <!-- 普通属性值的注入 -->
    <property name="name" value="IT 部门" />
    <!-- 给数组注入值 -->
    <property name="empName">
        <list>
            <value>empName1</value>
            <value>empName2</value>
            <value>empName3</value>
        </list>
    </property>
    <!-- 给 List 注入值:可以存放相同的值 -->
    <property name="empList">
        <list>
            <ref bean="emp1"/>
            <ref bean="emp2"/>
            <ref bean="emp1"/>
        </list>
    </property>
    <!-- 配置 Set 属性,相同的对象会被覆盖 -->
    <property name="empSet">
        <set>
            <ref bean="emp1"/>
            <ref bean="emp2"/>
            <ref bean="emp1"/>
        </set>
    </property>
    <!-- 配置 Map 属性,key 相同的话,后面的值会覆盖前面的 -->
    <property name="empMap">
        <map>
            <entry key="1" value-ref="emp1" />
            <entry key="2" value-ref="emp2" />
            <entry key="2" value-ref="emp1" />
        </map>
    </property>
    <!-- 配置属性集合 -->
    <property name="pp">
        <props>
            <prop key="pp1">Hello</prop>
            <prop key="pp2">World</prop>
        </props>
    </property>
</bean>
  • 测试代码
public class Test05 {
    @Test
    public void test01 (){ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-03.xml");
        Department department = (Department) context.getBean("department");
        System.out.println(department.getName());
        System.out.println("--------------------->String 数组");
        for (String str : department.getEmpName()){System.out.println(str);
        }
        System.out.println("--------------------->List 集合");
        for (Employee smp : department.getEmpList()){System.out.println(smp.getId()+":"+smp.getName());
        }
        System.out.println("--------------------->Set 集合");
        for (Employee emp : department.getEmpSet()){System.out.println(emp.getId()+":"+emp.getName());
        }
        System.out.println("--------------------->Map 集合");
        for (Map.Entry<String, Employee> entry : department.getEmpMap().entrySet()){System.out.println(entry.getKey()+":"+entry.getValue().getName());
        }
        System.out.println("--------------------->Properties");
        Properties pp = department.getPp();
        System.out.println(pp.get("pp1"));
        System.out.println(pp.get("pp2"));
    }
}

2、配置构造函数

根据配置的参数个数和类型,去映射并加载 Bean 的构造方法。

  • 配置代码
<!-- 这里配置 2 个参数,所有调用 2 个参数的构造函数 -->
<bean id="employee" class="com.spring.mvc.entity.Employee">
    <constructor-arg index="0" type="java.lang.String" value="cicada"/>
    <constructor-arg index="1" type="int" value="1"/>
</bean>
  • 测试代码
public class Test06 {
    @Test
    public void test01 (){ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-04.xml");
        Employee employee = (Employee) context.getBean("employee");
        System.out.println(employee.getId()+":"+employee.getName());
    }
}

3、配置继承关系

  • 配置代码
<!-- 配置父类信息 -->
<bean id="student" class="com.spring.mvc.entity.Student">
    <property name="name" value="Spring" />
    <property name="age" value="22" />
</bean>
<!-- 配置子类信息 -->
<bean id="grade" class="com.spring.mvc.entity.Grade">
    <!-- 覆盖 -->
    <property name="name" value="Summer" />
    <property name="degree" value="大学" />
</bean>
  • 测试代码
public class Test07 {
    @Test
    public void test01 (){ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-05.xml");
        Grade grade = (Grade) context.getBean("grade");
        /* Summer;0; 大学  */
        System.out.println(grade.getName()+";"+grade.getAge()+";"+grade.getDegree());
    }
}

三、作用域

作用域:用于确定 spring 创建 bean 实例个数,比如单例 Bean, 原型 Bean, 等等。

类型 说明
singleton IOC 容器仅创建一个 Bean 实例,IOC 容器每次返回的是同一个单例 Bean 实例,默认配置。
prototype IOC 容器可以创建多个 Bean 实例,每次返回的 Bean 都是新的实例。
request 每次 HTTP 请求都会创建一个新的 Bean,适用于 WebApplicationContext 环境。
session 同一个 HTTP Session 共享一个 Bean 实例。不同 HTTP Session 使用不同的实例。
global-session 同 session 作用域不同的是,所有的 Session 共享一个 Bean 实例。

四、生命周期

在 Spring 框架中 Bean 的生命周期非常复杂,过程大致如下:实例化,属性加载,初始化前后管理,销毁等。下面基于一个案例配置,会更加的清楚。

1、编写 BeanLife 类

public class BeanLife implements BeanNameAware {
    private String name ;
    public String getName() {return name;}
    public void setName(String name) {System.out.println("设置名称:"+name);
        this.name = name;
    }
    @Override
    public void setBeanName(String value) {System.out.println("BeanNameAware..SetName:"+value);
    }
    public void initBean() {System.out.println("初始化 Bean..");
    }
    public void destroyBean() {System.out.println("销毁 Bean..");
    }
    public void useBean() {System.out.println("使用 Bean..");
    }
    @Override
    public String toString() {return "BeanLife [name =" + name + "]";
    }
}

2、定制加载过程

实现 BeanPostProcessor 接口。

public class BeanLifePostProcessor implements BeanPostProcessor {
    // 初始化之前对 bean 进行增强处理
    @Override
    public Object postProcessBeforeInitialization(Object obj, String beanName) throws BeansException {System.out.println("初始化之前..."+beanName);
        return obj ;
    }
    // 初始化之后对 bean 进行增强处理
    @Override
    public Object postProcessAfterInitialization(Object obj, String beanName) throws BeansException {System.out.println("初始化之后..."+beanName);
        // 改写 Bean 的名称
        if (obj instanceof BeanLife){BeanLife beanLife = (BeanLife)obj ;
            beanLife.setBeanName("myBeanLifeTwo");
            return beanLife ;
        }
        return obj ;
    }
}

3、配置文件

<!-- 加载 Bean 的处理器 -->
<bean class="com.spring.mvc.BeanLifePostProcessor" />
<!-- 指定初始化和销毁方法 -->
<bean id="beanLife" class="com.spring.mvc.entity.BeanLife"
    init-method="initBean" destroy-method="destroyBean">
    <property name="name" value="myBeanLifeOne" />
</bean>

4、测试过程

  • 测试代码
public class Test08 {
    @Test
    public void test01 (){ApplicationContext context = new ClassPathXmlApplicationContext("/bean-value-06.xml");
        BeanLife beanLife = (BeanLife) context.getBean("beanLife");
        System.out.println("测试结果 BeanLife:"+beanLife.getName()) ;
        beanLife.useBean();
        // 关闭容器
        ((AbstractApplicationContext) context).close();}
}
  • 输出结果
1、设置名称:myBeanLifeOne
2、BeanNameAware..SetName:beanLife
3、初始化之前...beanLife
4、初始化 Bean..
5、初始化之后...beanLife
6、BeanNameAware..SetName:myBeanLifeTwo
7、测试结果 BeanLife:myBeanLifeOne
8、使用 Bean..
9、销毁 Bean..

这里梳理 Bean 的生命周期,过程十分清晰。

五、源代码地址

GitHub·地址
https://github.com/cicadasmile/spring-mvc-parent
GitEE·地址
https://gitee.com/cicadasmile/spring-mvc-parent

正文完
 0