IOC容器装配Bean(xml方式)

1.Spring 提供配置Bean三种实例化方式 

    1)使用类构造器实例化(默认无参数)

 
   
  1. <bean id="bean1" class="cn.itcast.spring.b_instance.Bean1"></bean>
    2 )使用静态工厂方法实例化 ( 简单工厂模式 )
 
   
  1. //下面这段配置的含义:调用Bean2Factory的getBean2方法得到bean2
  2. <bean id="bean2" class="cn.itcast.spring.b_instance.Bean2Factory" factory-method="getBean2"></bean>
    3 )使用实例工厂方法实例化 ( 工厂方法模式 )
 
   
  1. //先创建工厂实例bean3Facory,再通过工厂实例创建目标bean实例
  2. <bean id="bean3Factory" class="cn.itcast.spring.b_instance.Bean3Factory"></bean>
  3. <bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"></bean>
 

2.Bean的其它属性配置 

<bean>元素的id属性和name属性的区别 

    早期Spring开发中Bean的 id属性 ,遵守xml语法id约束

* id 的命名要满足XMLID属性命名规范 必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号。

* 使用name属性,就可以使用很多特殊字符,早期在struts1spring整合 ,如<bean name="/login" class="....LoginAction" />  name中含有/ ,使用id会报错。 

    **如果元素没有id只有name name 属性值可以作为id 使用 

<bean>元素scope属性 

 
   
  1. * scope="singleton" 单例 ,在Spring IoC容器中仅存在一个Bean实例 (默认的scope)
  2. * scope="prototype" 多例 ,每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时 ,相当于执行new XxxBean()
  3. * scope="request" 用于web开发,将Bean放入request范围 ,request.setAttribute("xxx") , 在同一个request 获得同一个Bean
  4. * scope="session" 用于web开发,将Bean 放入Session范围,在同一个Session 获得同一个Bean 
  5. * scope="globalSession" 一般用于Porlet应用环境 , 分布式系统存在全局session概念 ,如果不是porlet环境,globalSession 等同于Session 
在开发中主要使用  scope="singleton" 、  scope="prototype" 

如果在applicationContext.cfg.xml配置文件中的bean,未指定scope属性,那么默认为singleton 


3.Bean的生命周期 

    1)在配置 <bean> 元素,通过 init-method 指定Bean的初始化方法,通过 destroy-method 指定Bean销毁方法 

 
   
  1. <bean id="lifeCycleBean" class="cn.itcast.spring.d_lifecycle.LifeCycleBean" init-method="setup" destroy-method="teardown"></bean>
在JavaBean中书写在,<bean>元素里面定义的2个方法setup和teardown
 
   
  1. package cn.itcast.spring.d_lifecycle;
  2. public class LifeCycleBean {
  3. public void setup() {
  4. System.out.println("初始化...");
  5. }
  6. public void teardown() {
  7. System.out.println("销毁....");
  8. }
  9. }
    进行测试:
 
    
  1. @Test
  2. // 测试Spring 生命周期
  3. public void demo() {
  4.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  5.     LifeCycleBean lifeCycleBean = (LifeCycleBean) applicationContext.getBean("lifeCycleBean");
  6.     System.out.println(lifeCycleBean);
  7. }
    运行结果:

    我们发现:运行完程序,销毁方法没有执行

解析:这个程序运行了,但是Spring容器并不知道何时销毁。

举个例子:例如把一个Spring容器交给tomcat管理时,tomcat停止时,他就会自动调用destroy方法。那么我们就自己来调用这个方法:applicationContext.close();

此时,就会调用销毁方法:

需要注意的问题:

    *  destroy-method 只对 scope="singleton" 有效  

    *  销毁方法,必须关闭ApplicationContext对象(手动调用),才会被调用

 
   
  1. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  2. applicationContext.close();

     2)Bean的完整生命周期 (十一步骤)【了解内容,但是对于spring内部操作理解有一定帮助

instantiate bean对象实例化

populate properties 封装属性

如果Bean实现BeanNameAware 执行 setBeanName

如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext

如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization,BeanPostProcessor接口提供钩子函数,用来动态扩展修改Bean(程序自动调用后处理Bean)

 
   
  1. public class MyBeanPostProcessor implements BeanPostProcessor {
  2. public Object postProcessAfterInitialization(Object bean, String beanName)
  3. throws BeansException {
  4. System.out.println("第八步:后处理Bean,after初始化。");
  5. //后处理Bean,在这里加上一个动态代理,就把这个Bean给修改了。
  6. return bean;//返回bean,表示没有修改,如果使用动态代理,返回代理对象,那么就修改了。
  7. }
  8. public Object postProcessBeforeInitialization(Object bean, String beanName)
  9. throws BeansException {
  10. System.out.println("第五步:后处理Bean的:before初始化!!");
  11. //后处理Bean,在这里加上一个动态代理,就把这个Bean给修改了。
  12. return bean;//返回bean本身,表示没有修改。
  13. }
  14. }
  15. 注意:这个前处理Bean和后处理Bean会对所有的Bean进行拦截。
如果 Bean 实现 InitializingBean  执行  afterPropertiesSet 

调用<bean init-method="init"> 指定初始化方法 init

如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization

执行业务处理

如果Bean实现 DisposableBean 执行 destroy

调用<bean destroy-method="customerDestroy"> 指定销毁方法 customerDestroy


* 为了能够比较清晰的看到上面的每一个步骤,我们模拟真实开发场景,定义一个接口和一个实现类

 
   
  1. // 用户数据库操作
  2. public interface UserDAO {
  3. public void add();
  4. public void search();
  5. }
实现类:
 
    
  1. // 实现DAO 方法
  2. public class UserDAOImpl implements UserDAO, BeanNameAware, ApplicationContextAware, InitializingBean,DisposableBean {
  3. private String company;
  4. public UserDAOImpl() {
  5. System.out.println("第一步 Bean的实例化 ...");
  6. }
  7. // 设置company
  8. public void setCompany(String company) {
  9. System.out.println("第二步 设置Bean的属性");
  10. this.company = company;
  11. }
  12. //如果实现了BeanNameAware接口,那么会将bean的那么设置到程序中,也就是userDao
  13. public void setBeanName(String beanName) {
  14. System.out.println("第三步 将xml配置Bean的name设置到程序中:" + beanName);
  15. // <bean id="userDAO" class="cn.itcast.spring.d_lifecycle.UserDAOImpl"></bean>
  16. }
  17. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  18. System.out.println("第四步 将整合工厂上下文对象设置到 Bean中 ");
  19. }
  20. public void afterPropertiesSet() throws Exception {
  21. System.out.println("第六步 属性设置完成后...");
  22. }
  23. public void setup() {
  24. System.out.println("第七步 配置初始化方法...init-method='setup'");
  25. }
  26. //Bean初始化完毕,如果有业务方法,那么就开始执行,以下方法模拟业务方法。

  27. //这是在接口中定义的业务操作方法
  28. public void add() {
  29. System.out.println("第九步 业务操作 .... 添加");
  30. }
  31. //这是在接口中定义的业务操作方法
  32. public void search() {
  33. System.out.println("第九步 业务操作 .... 查询");
  34. }
  35. //destroy方法必须自己调用closed方法后才会执行。
  36. public void destroy() throws Exception {
  37. // 这个destroy无需配置,实现这个接口,就会自动的去调用destroy方法。
  38. System.out.println("第十步 无需配置的销毁方法");
  39. }
  40. public void teardown() {
  41. System.out.println("第十一步 通过配置设置销毁方法...");
  42. }
  43. }
其中少了第五步和第八步,此项内容在上面对应序号位置找。

配置文件applicationContext.cfg.xml:

 
   
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- 引入约束 来自xsd-config.html文件 -->
  3. <beans xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:p="http://www.springframework.org/schema/p"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="
  7. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  8. <bean id="userDAO" class="cn.itcast.spring.d_lifecycle.UserDAOImpl" init-method="setup" destroy-method="teardown">
  9. <!--第二步,设置bean的属性-->
  10. <property name="company" value="itcast"></property>
  11. </bean>
  12. <!-- 必须配置后处理Bean , bean没有id 因为由 Spring框架内部调用 -->
  13. <bean class="cn.itcast.spring.d_lifecycle.MyBeanPostProccessor"></bean>
  14. </beans>
编写测试类:
 
    
  1. @Test
  2. // 测试Spring 生命周期
  3. public void demo2() {
  4.     ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  5.     UserDAO userDAO = (UserDAO) applicationContext.getBean("userDAO");
  6. //执行业务方法
  7.     userDAO.add();
  8.     userDAO.search();
  9.     // 关闭工厂
  10.     applicationContext.close();
  11. }
运行结果:


分析:

    前面前处理Bean和后处理Bean被执行多次,表示:钩子函数会对每个bean进行拦截(前面已经配置了其他的几个Bean,每个Bean都执行2à前处理Bean后处理bean)故而执行多次,反复连续的输出五,八。

第三步和第四步,使我们写的Bean了解Spring容器 

第五步和第八步,使用BeanPostProcessor 就是钩子函数,作用用来对Bean对象进行扩展。

问题: 在userDAO对象所有方法上 添加运行时间监控  【用后处理bean对目标bean在构造时进行代理,对原有方法进行扩展增强!

    我们可以利用后处理bean(BeanPostProcessor)与动态代理一起完成此功能,我们只需要在后处理bean的postProcessAfterInitialization方法里面改动代码即可

 
   
  1. /**
  2. * bean 就是对象实例 beanName 就是xml 配置Bean的id 或者 name
  3. */
  4. public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
  5.     System.out.println("第八步 执行后处理Bean 的初始化完成后方法...");
  6. if (beanName.equals("userDAO")) {
  7.     // 需要进行时间监控Bean
  8. Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
  9.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10.     if (method.getName().equals("search")) {
  11.     // 增强search方法
  12. System.out.println("开始时间:" + System.currentTimeMillis());
  13. Object result = method.invoke(bean, args);
  14. System.out.println("结束时间:" + System.currentTimeMillis());
  15. return result;
  16. } else {
  17. // 不加强
  18. return method.invoke(bean, args);
  19. }
  20. }
  21. });
  22. return proxy;
  23.     }
  24. return bean;
  25. }
运行测试结果:
 
我们发现:在业务方法search的前后环绕了增强的功能!

==========================================================================================================================

4.SpringBean属性的依赖注入


*spring支持构造器注入和setter方法注入 

    第一种 构造器注入,通过 <constructor-arg> 元素完成注入  

 
   
  1. /**
  2. * 轿车 (构造函数注入属性)
  3. */
  4. public class Car {
  5. private String name;
  6. private double price;
  7. public Car(String name, double price) {
  8. super();
  9. this.name = name;
  10. this.price = price;
  11. }
  12. @Override
  13. public String toString() {
  14. return "Car [name=" + name + ", price=" + price + "]";
  15. }
  16. }
    配置文件:
 
   
  1. <!-- 构造器注入 -->
  2. <bean id="car" class="cn.itcast.spring.e_di.Car">
  3. <!-- 通过构造器参数,完成属性注入 -->
  4. <constructor-arg index="0" type="java.lang.String" value="保时捷" ></constructor-arg> <!-- 第一个参数 String类型参数 -->
  5. <constructor-arg index="1" type="double" value="1000000"></constructor-arg>
  6. </bean>
 

第二种 setter方法注入, 通过<property> 元素完成注入  【开发中常用方式】

 
   
  1. /**
  2. * 通过setter方法完成属性注入
  3. */
  4. public class Car2 {
  5. private String name;
  6. private double price;
  7. // 注入属性时 只需要提供set方法
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public void setPrice(double price) {
  12. this.price = price;
  13. }
  14. @Override
  15. public String toString() {
  16. return "Car2 [name=" + name + ", price=" + price + "]";
  17. }
  18. }
    配置文件:
 
    
  1. <!-- setter方法注入 -->
  2. <bean id="car2" class="cn.itcast.spring.e_di.Car2">
  3.     <!-- 通过 property 元素完成属性注入 -->
  4.     <property name="name" value="宝马"></property>
  5.     <property name="price" value="500000"></property>
  6. </bean>
    *  使用  <property>  元素  ref 属性,引入另一个 Bean 对象,完成 Bean 之间注入 
 
   
  1. // 员工类
  2. public class Employee {
  3. private String name;
  4. // 引入Car2对象
  5. private Car2 car2;
  6. public void setName(String name) {
  7. this.name = name;
  8. }
  9. public void setCar2(Car2 car2) {
  10. this.car2 = car2;
  11. }
  12. @Override
  13. public String toString() {
  14. return "Employee [name=" + name + ", car2=" + car2 + "]";
  15. }
  16. }
    配置文件:
 
    
  1. <bean id="employee" class="cn.itcast.spring.e_di.Employee">
  2.     <property name="name" value="张三"></property>
  3. <!--ref引用其他Bean的id或者name-->
  4.     <property name="car2" ref="car2"></property>
  5. </bean>
 

名称空间 p的使用  (Spring2.5 新特性)

spring2.5版本 引入名称空间p, 简化属性注入的配置

    p:<属性名>="xxx" 引入常量值

    p:<属性名>-ref="xxx" 引用其它Bean对象

1)引入p名称空间 

 
   
  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:p="http://www.springframework.org/schema/p"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
2 ) 改写 <property> 注入为  p 名称空间注入 
 
   
  1. <bean id="car2" class="cn.itcast.spring.e_di.Car2">
  2.     <!-- 通过 property 元素完成属性注入 -->
  3.     <property name="name" value="宝马"></property>
  4.     <property name="price" value="500000"></property>
  5. </bean>
  6. <bean id="employee" class="cn.itcast.spring.e_di.Employee">
  7.     <property name="name" value="张三"></property>
  8.     <property name="car2" ref="car2"></property> <!-- ref引用其他Bean的id或者name -->
  9. </bean>
改写
 
   
  1. <bean id="car2" class="cn.itcast.spring.e_di.Car2" p:name="宝马" p:price="1000000"></bean>
  2. <bean id="employee" class="cn.itcast.spring.e_di.Employee" p:name="李四" p:car2-ref="car2"></bean>
 

* spring3.0之后引入 spEL 表达式

1) 完成对象之间注入 

 
   
  1. <property name="car2" ref="car2"></property>
改写为
 
   
  1. <property name="car2" value="#{car2}"></property>

2)  使用另一个 Bean 属性完成注入 
 
    
  1. // 单独数据Bean
  2. public class CarInfo {
  3. public String getName() {
  4. return "奇瑞QQ";
  5. }
  6. public double caculatePrice() {
  7. return 200000;
  8. }
  9. }
配置:
 
   
  1. <bean id="carInfo" class="cn.itcast.spring.e_di.CarInfo"></bean>

 
   
  1. <bean id="car2_2" class="cn.itcast.spring.e_di.Car2">
  2.     <property name="name" value="#{carInfo.name}"></property>
  3. </bean>

3) 使用另一个Bean方法完成注入

 
   
  1. <bean id="carInfo" class="cn.itcast.spring.e_di.CarInfo"></bean>
  2. <bean id="car2_2" class="cn.itcast.spring.e_di.Car2">
  3.     <property name="name" value="#{carInfo.name}"></property>
  4.     <property name="price" value="#{carInfo.caculatePrice()}"></property>
  5. </bean>

5. 集合属性的注入 

spring提供专门标签完成 ListSetMapProperties 等集合元素属性注入 

1) 注入List (数组)

 
   
  1. <property name="hobbies">
  2.     <list>
  3.         <!-- <value>注入简单类型,<ref />注入复杂类型 -->
  4.         <value>音乐</value>
  5.         <value>体育</value>
  6.     </list>
  7. </property>

2)  注入Set

 
   
  1. <property name="numbers">
  2.     <set>
  3.         <value>10</value>
  4.         <value>6</value>
  5.         <value>15</value>
  6.     </set>
  7. </property>

3) 注入Map

 
   
  1. <property name="map">
  2.     <map>
  3.         <!-- 复杂类型<entry key-ref="" value-ref=""></entry> -->
  4.         <entry key="name" value="itcast"></entry>
  5.         <entry key="address" value="北京"></entry>
  6.     </map>
  7. </property>
 

4)  注入Properties 

* java.utils.Properties 类继承 java.utils.HashTable 

    Properties keyvalue都是String类型 

例如:

 
   
  1. <property name="properties">
  2.     <props>
  3.         <prop key="company">传智播客</prop>
  4.         <prop key="pnum">100</prop>
  5.     </props>
  6. </property>

6.Spring框架中引入多个XML配置文件 

    第一种 并列引入多个XML 

 
   
  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans1.xml", "beans2.xml");
    
    第二种 引入总 xml 文件,在总 xml 文件引入 子 xml 文件 
 
   
  1. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    *  applicationContext.xml  中 
 
   
  1. <import resource="classpath:bean1.xml"/>
  2. <import resource="classpath:bean2.xml"/>

在开发中主要使用第二种 , 将配置文件分离配置,便于维护管理

猜你喜欢

转载自blog.csdn.net/a_blackmoon/article/details/80194272