首页 > 代码库 > Spring4--课程笔记3
Spring4--课程笔记3
Bean的装配:
Bean 的装配,即 Bean 对象的创建。容器根据代码要求创建 Bean 对象后再传递给代码的过程,称为 Bean 的装配。
1. 创建Bean对象的方式:
1. 通过 getBean()方式从容器获取指定的 Bean 对象。
Bean的配置:<bean id="someService" class="com.tongji.ba01.SomeServiceImpl"/>
2. 动态工厂Bean:有些时候,项目中需要通过工厂类来创建 Bean 对象,而不能像第一个方式中,直接由 Spring 容器来装配 Bean 对象。使用工厂模式创建 Bean 对象,就会使工厂类与要创建的 Bean 类耦合到一起。
Spring 对于使用动态工厂来创建的 Bean,有专门的属性定义。factory-bean 指定相应的工厂 Bean,由 factory-method 指定创建所用方法。此时配置文件中至少会有两个 Bean 的定义:工厂类的 Bean,与工厂类所要创建的目标类 Bean。而测试类中不再需要获取工厂 Bean对象了,可以直接获取目标 Bean 对象。实现测试类与工厂类间的解耦。
Bean的配置:
<bean id="someFactory" class="com.tongji.ba02.SomeFactory"/>
<!-- 表明someService对象是由someFactory这个工厂Bean的getSomeService()方法创建的 -->
<bean id="someService" factory-bean="someFactory" factory-method="getSomeService"/>
3. 静态工厂Bean:使用工厂模式中的静态工厂来创建实例 Bean 对象。
此时需要注意,静态工厂无需工厂实例,所以不再需要定义静态工厂<bean/>。 而对于工厂所要创建的 Bean,其不是由自己的类创建的,所以无需指定自己的类。但其是由工厂类创建的,所以需要指定所用工厂类。故 class 属性指定的是工厂类而非自己的
类。当然,还需要通过 factory-method 属性指定工厂方法。
Bean的配置:
<!-- 表明someService对象是由someFactory这个静态工厂的getSomeService()方法创建的 -->
<bean id="someService" class="com.tongji.ba03.SomeFactory" factory-method="getSomeService"/>
上述三种创建Bean对象的测试后代码均是:
1 package com.tongji.ba03; 2 3 import org.junit.Test; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class MyTest { 8 9 @Test 10 public void test01() { 11 //创建容器 12 String resource = "com/tongji/ba03/applicationContext.xml"; 13 @SuppressWarnings("resource") 14 ApplicationContext ac = new ClassPathXmlApplicationContext(resource); 15 //静态工厂bean将测试类与工厂类的耦合解决了 16 ISomeService service = (ISomeService) ac.getBean("someService"); 17 service.doSome(); 18 } 19 }
后两种创建方式仍然存在Bean类与工厂类耦合的问题:
1 package com.tongji.ba03; 2 3 //问题:SomeServiceImpl类与工厂类耦合到了一起 4 public class SomeFactory { 5 public static ISomeService getSomeService() { 6 return new SomeServiceImpl(); 7 } 8 }
2. 容器中Bean的作用域:
当通过 Spring 容器创建一个 Bean 实例时,不仅可以完成 Bean 的实例化,还可以通过scope 属性,为 Bean 指定特定的作用域。Spring 支持 5 种作用域。
(1)singleton:单态模式。即在整个 Spring 容器中,使用 singleton 定义的 Bean 将是单例的,只有一个实例。默认为单态的。
(2)prototype:原型模式。即每次使用 getBean 方法获取的同一个<bean />的实例都是一个新的实例。
(3)request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
(4)session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。
(5)global session:每个全局的 HTTP session 对应一个 Bean 实例。典型情况下,仅在使用portlet 集群时有效,多个 Web 应用共享一个 session。一般应用中,global-session 与 session是等同的。
注意:
(1)对于 scope 的值 request、session 与 global session,只有在 Web 应用中使用 Spring 时,该作用域才有效。
(2)对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了。
(3)对于 scope 为 prototype 的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行装配的。
<bean id="someService" class="com.tongji.ba04.SomeServiceImpl" scope="prototype"/>
3. Bean的生命周期:
Bean 实例从创建到最后销毁,需要经过很多过程,执行很多生命周期方法。
Step1:调用无参构造器,创建实例对象。
Step2:调用参数的 setter,为属性注入值。
Step3:若 Bean 实现了 BeanNameAware 接口,则会执行接口方法 setBeanName(String beanId),使 Bean 类可以获取其在容器中的 id 名称。
Step4:若 Bean 实现了 BeanFactoryAware 接口,则执行接口方法 setBeanFactory(BeanFactory factory),使 Bean 类可以获取到 BeanFactory 对象。
Step5:若定义并注册了 Bean 后处理器 BeanPostProcessor,则执行接口方法postProcessBeforeInitialization()。
Step6:若 Bean 实现了 InitializingBean 接口,则执行接口方法 afterPropertiesSet()。该方法在 Bean 的所有属性的 set 方法执行完毕后执行,是 Bean 初始化结束的标志,即 Bean 实例化结束。
Step7:若设置了 init-method 方法,则执行。
Step8:若定义并注册了 Bean 后处理器 BeanPostProcessor,则执行接口方法 postProcessAfterInitialization()。
Step9:执行业务方法。
Step10:若 Bean 实现了 DisposableBean 接口,则执行接口方法 destroy()。
Step11:若设置了 destroy-method 方法,则执行。
补充:
1. Step5和Step8中的Bean后处理器:
Bean 后处理器是一种特殊的 Bean,容器中所有的 Bean 在初始化时,均会自动执行该类的两个方法。由于该 Bean 是由其它 Bean 自动调用执行,不是程序员手工调用,故此 Bean无须 id 属性。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd"> 7 8 <bean id="someService1" class="com.tongji.ba07.SomeServiceImpl" 9 init-method="initPost" destroy-method="preDestory"> 10 <property name="dao1" value="myDao1"/> 11 <property name="dao2" value="myDao2"/> 12 <property name="dao3" value="myDao3"/> 13 </bean> 14 15 <bean class="com.tongji.ba07.MyBeanPostProcessor"/> 16 17 </beans>
需要做的是,在 Bean 后处理器类方法中,只要对 Bean 类与 Bean 类中的方法进行判断,就可实现对指定的 Bean 的指定方法进行功能扩展与增强。方法返回的 Bean 对象,即是增过的对象。
代码中需要自定义 Bean 后处理器类。该类就是实现了接口 BeanPostProcessor 的类。该接口中包含两个方法,分别在目标 Bean 初始化完毕之前与之后执行。它们的返回值为:功能被扩展或增强后的 Bean 对象。
1 package com.tongji.ba07; 2 3 import org.springframework.beans.BeansException; 4 import org.springframework.beans.factory.config.BeanPostProcessor; 5 6 public class MyBeanPostProcessor implements BeanPostProcessor { 7 8 //bean:当前正在被初始化的Bean 9 //beanName:当前正在被初始化的Bean的id 10 //在当前Bean的所有属性均被初始化完毕之前 执行该方法 11 @Override 12 public Object postProcessBeforeInitialization(Object bean, String beanName) 13 throws BeansException { 14 System.out.println("Step5:执行Bean后处理器的before方法"); 15 //返回bean,在下一个bean初始化时继续执行 16 return bean; 17 } 18 19 //在当前bean的所有属性均被初始化完毕之后 执行该方法 20 @Override 21 public Object postProcessAfterInitialization(final Object bean, String beanName) 22 throws BeansException { 23 System.out.println("Step8:执行Bean后处理器的after方法"); 24 return bean; 25 } 26 27 }
Bean 初始化完毕有一个标志:Step6中 afterPropertiesSet() 方法将被执行。即当该方法被执行时,表示该 Bean 被初始化完毕。所以 Bean 后处理器中两个方法的执行,是在这个方法之前之后执行。
2. Step7和Step11中的Bean的定制生命始末:
可以为 Bean 定制初始化后的生命行为,也可以为 Bean 定制销毁前的生命行为。首先,这些方法需要在 Bean 类中事先定义好:是方法名随意的 public void 方法。其次,在配置文件的<bean/>标签中增加如下属性(配置文件见上面的补充1):
init-method:指定初始化方法的方法名
destroy-method:指定销毁方法的方法名
1 package com.tongji.ba07; 2 3 import org.springframework.beans.BeansException; 4 import org.springframework.beans.factory.BeanFactory; 5 import org.springframework.beans.factory.BeanFactoryAware; 6 import org.springframework.beans.factory.BeanNameAware; 7 import org.springframework.beans.factory.DisposableBean; 8 import org.springframework.beans.factory.InitializingBean; 9 10 public class SomeServiceImpl implements ISomeService, BeanNameAware, 11 BeanFactoryAware, InitializingBean, DisposableBean{ 12 private String dao1; 13 private String dao2; 14 private String dao3; 15 16 public SomeServiceImpl() { 17 System.out.println("Step1:调用Bean的无参构造器"); 18 } 19 20 public void setDao1(String dao1) { 21 this.dao1 = dao1; 22 System.out.println("Step2:调用属性的setter(dao1)"); 23 } 24 25 public void setDao2(String dao2) { 26 this.dao2 = dao2; 27 System.out.println("Step2:调用属性的setter(dao2)"); 28 } 29 30 public void setDao3(String dao3) { 31 this.dao3 = dao3; 32 System.out.println("Step2:调用属性的setter(dao3)"); 33 } 34 35 @Override 36 public void setBeanName(String name) { 37 System.out.println("Step3:当前Bean的id为" + name); 38 } 39 40 @Override 41 public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 42 System.out.println("Step4:获取BeanFactory容器"); 43 } 44 45 @Override 46 public void afterPropertiesSet() throws Exception { 47 System.out.println("Step6:Bean的初始化工作全部完成"); 48 } 49 50 public void initPost() { 51 System.out.println("Step7:Bean刚被初始化"); 52 } 53 54 @Override 55 public String doSome() { 56 System.out.println("Step9:执行doSome()"); 57 return "China"; 58 } 59 60 @Override 61 public String doOther() { 62 System.out.println("执行doOther()"); 63 return "abcde"; 64 } 65 66 @Override 67 public void destroy() throws Exception { 68 System.out.println("Step10:Bean销毁前的工作"); 69 } 70 71 public void preDestory() { 72 System.out.println("Step11:Bean马上就要Game Over了"); 73 } 74 }
注意,若要看到 Bean 的 destroy-method 的执行结果,需要满足两个条件:
(1)Bean 为 singleton,即单例
(2)要确保容器关闭。接口 ApplicationContext 没有 close()方法,但其实现类有。所以,可以将 ApplicationContext 强转为其实现类对象,或直接创建的就是实现类对象。
1 package com.tongji.ba07; 2 3 import org.junit.Test; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 7 public class MyTest { 8 9 @Test 10 public void test01() { 11 //创建容器 12 String resource = "com/tongji/ba07/applicationContext.xml"; 13 ApplicationContext ac = new ClassPathXmlApplicationContext(resource); 14 ISomeService someService1 = (ISomeService) ac.getBean("someService1"); 15 someService1.doSome(); 16 //要看到销毁方法的执行,需要两个条件: 17 //1.Bean需要singleton的 18 //2.手工将容器关闭 19 ((ClassPathXmlApplicationContext)ac).close(); 20 21 } 22 }
4. <bean/>标签的 id 属性与 name 属性 :
一般情况下,命名<bean/>使用 id 属性,而不使用 name 属性。在没有 id 属性的情况下,name 属性与 id 属性作用是相同的。但,当<bean/>中含有一些特殊字符时,就需要使用 name属性了。
id 的命名需要满足 XML 对 ID 属性命名规范:必须以字母开头,可以包含字母、数字、下划线、连字符、句话、冒号。且要求名称在容器中必须唯一。
name 则可以包含各种字符,且对名称没有唯一性要求。若名称不唯一,则后面的会覆盖前面的。
Spring4--课程笔记3