1、 Bean的生命周期
Spring容器中的Bean主要有singleton Bean和prototype Bean。
singleton Bean两个生命周期行为
(1)出生之后:为他配置各种资源。
①用init-method来指定。
②实现InitializingBean,该接口中afterPropertiesSet将会被自动作为生命周期的初始化方法
①用destory-method来指定。
②实现DisposableBean,该接口中destroy将会被自动作为生命周期的销毁方法
eg:
Puppy.java
public class Puppy implements ApplicationContextAware,BeanNameAware,
InitializingBean,DisposableBean
{
String kind;
private int age;
private ApplicationContext ctx;
private String beanId;
public String getKind() {
return kind;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setKind(String kind) {
System.out.println("----setKind方法----");
this.kind = kind;
}
public void bark(){
System.out.println("提前获取的id: "+beanId);
System.out.println(ctx.getMessage("bark", null,Locale.getDefault(Category.FORMAT)));
}
@Override
public String toString() {
return "Puppy [kind=" + kind + ", age=" + age + "]";
}
@Override
public void setBeanName(String beanId) {
System.out.println("调用setBeanName方法");
this.beanId = beanId;
}
@Override
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
System.out.println("调用setApplicationContext方法");
this.ctx = ctx;
}
//第一种初始化,需要在beans.xml中使用init-method来指定
public void init(){
System.out.println("模拟打开数据库资源");
System.out.println("------init()执行--------");
}
//第一种释放
public void close(){
System.out.println("模拟关闭资源");
System.out.println("------close()执行-------");
}
//第二种初始化
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("模拟打开数据库资源");
System.out.println("------afterPropertiesSet()执行------");
}
//第二种释放
@Override
public void destroy() throws Exception {
System.out.println("模拟关闭资源");
System.out.println("---------destroy()执行---------");
}
}
beans.xml
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>mess</value>
</list>
</property>
</bean>
<bean id="puppy"
class="cony.domain.Puppy"
p:kind="牧羊犬"
p:age="9"
init-method="init"
destroy-method="close"
/>
测试:
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml");
Puppy puppy = appContext.getBean("puppy",Puppy.class);
puppy.bark();
System.out.println();
测试结果:
可以看出,测试结果并没有执行释放资源的方法。这是因为singleton Bean容器创建产生,容器销毁灭亡。而容器并没有随程序的结束而销毁。为了让容器销毁,分2种情况。
- 对于Java Web项目,项目关闭时容器会销毁。
所以将测试代码改为:
// AbstractApplicationContext实现了ApplicationContext接口
AbstractApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml");
appContext.registerShutdownHook();// 注册关闭钩子
Puppy puppy = appContext.getBean("puppy",Puppy.class);
puppy.bark();
再次运行测试代码:
总结:由上面运行结果可以看出Bean的生命周期为:
创建实例→注入依赖关系(setter)→调用afterPropertiesSet→调用init-method→运行阶段→调用destroy→调用destory-method
2、作用域不同步的Bean
singleton Bean好处是:性能较好;缺点是:并发时存在线程安全问题。
不要将prototype注入singleton Bean,singleton bean会把prototype Bean变成singleton行为
(1)错误案例:
beans.xml
<bean id="puppy"
class="cony.domain.Puppy"
p:kind="牧羊犬"
p:age="9"
scope="prototype"
/>
<!-- user拥有prototype的puppy -->
<bean id="user" class="cony.domain.User"
p:name="啊呆"
p:age="5"
p:puppy-ref="puppy"
/>
测试:
User user1 = appContext.getBean("user",User.class);
System.out.println(user1.getPuppy());
User user2 = appContext.getBean("user",User.class);
System.out.println(user2.getPuppy());
测试结果:singleton的user拥有的dog也变成了singleton
(2)解决方案:
①让singleton Bean持有容器,每次需要使用prototype Bean时,总是通过容器去获取。
缺点:造成代码污染!编程应该尽可能使用POJO。
优点:容易理解。
eg:
User.java
public class User implements ApplicationContextAware{
private String name;
private int age;
private ApplicationContext ctx;
public User(){
System.out.println("调用构造器");
}
public User(String name,int age,Puppy dog){
super();
System.out.println("调用构造器");
this.name = name;
this.age = age;
}
public Puppy getFromCtx(){
return ctx.getBean("puppy",Puppy.class);
}
public void hunt(){
Puppy puppy = getFromCtx();
System.out.println("黄豆带着"+puppy);
puppy.bark();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ""
+ "]";
}
@Override
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
this.ctx = ctx;
}
}
beans.xml:
<bean id="puppy"
class="cony.domain.Puppy"
p:kind="牧羊犬"
p:age="9"
scope="prototype"
/>
<bean id="user" class="cony.domain.User"
p:name="啊呆"
p:age="5"
/>
测试:
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml");
User user1 = appContext.getBean("user",User.class);
user1.hunt();
User user2 = appContext.getBean("user",User.class);
user2.hunt();
测试结果:
②使用lookup方法注入。
User.java(该类变成抽象类)
public abstract class User {
private String name;
private int age;
public User(){
System.out.println("调用构造器");
}
public User(String name,int age){
super();
System.out.println("调用构造器");
this.name = name;
this.age = age;
}
//定义一个创建Dinosaur实例的抽象方法给子类实现
public abstract Dinosaur getDinosaur();
public void walk(){
Dinosaur dinosaur = getDinosaur();
System.out.println(name+dinosaur+"散步");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age
+ ", nicknames=" + "]";
}
}
bean.xml
<!-- 无论数rawLookup还是Lookup都要设置 被注入类scope="prototype" -->
<bean id="dinosaur" class="cony.domain.Dinosaur" p:age="29"
p:kind="翼龙" scope="prototype" />
<bean id="user" class="cony.domain.User" p:age="20" p:name="小方">
<!-- lookup-method会告诉Spring使用CGLIB工具去动态生成User的子类
该子类必须实现name属性所指定的抽象方法。实现该方法的只有2行代码:
1. 获取Spring容器。
2. return ctx.getBean(bean属性值);
-->
<lookup-method name="getDinosaur" bean="dinosaur" />
</bean>
测试:
ApplicationContext appContext = new ClassPathXmlApplicationContext("beans.xml");
User user1 = appContext.getBean("user",User.class);
user1.walk();
System.out.println(user1.getClass());
User user2 = appContext.getBean("user",User.class);
user2.walk();
测试结果: