scope属性可以指定bean的作用范围:
在spring中可以通过配置bean标签的scope属性来指定bean的作用域范围,各取值含义参照表如下:
编写spring_test.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="student_one" class="poij.student"><property name="sid" value="101"></property><property name="sname" value="张三"></property><property name="age" value="19"></property><property name="gender" value="男"></property>
</bean>
</beans>
编写其中的测试类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import poij.student;public class scope_test {@Testpublic void scopeText(){ApplicationContext ioc=new ClassPathXmlApplicationContext("spring-scope.xml");student student1=ioc.getBean(student.class);student student2=ioc.getBean(student.class);//通过比较student1和student2是否为同一个进而判断bean对象是否为单例System.out.println(student1.equals(student2));}
}
输出:
true
在本例测试中,我们并没有设置scope属性的值,它与设置scope=“singleton”,所输出结果是一样的,都是表示获取该bean所对应的对象都是同一个
设置scope属性的值是prototype,表示获取该bean所对应的对象都不是同一个
<bean id="student_one" class="poij.student" scope="prototype">
再次进行测试,输出结果如下所示;
false
bean的生命周期:
bean对象创建(调用无参构造)
给bean对象设置属性
bean对象初始化之前操作(由bean的后置处理器负责)
bean对象初始化(需要在配置bean时指定初始化方法)
bean对象初始化之后操作(由bean的后置处理器负责)
bean对象就绪可以使用
bean对象销毁(需要在配置bean时,指定销毁方法)
IOC容器关闭
创建实体类user:
package projo;public class User {private String username;private Integer id;private String password;private Integer age;public String getUsername() {return username;}public Integer getId() {return id;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public void setId(Integer id) {System.out.println("生命周期2:依赖注入");this.id = id;}public void setUsername(String username) {this.username = username;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public User() {System.out.println("生命周期1:实例化");}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", id=" + id +", password='" + password + '\'' +", age=" + age +'}';}public User(int id,String username,String password,int age) {this.age=age;this.password=password;this.id=id;this.username = username;}//创建初始化的过程public void initMethod(){System.out.println("生命周期3:初始化");}//创建销毁的过程public void destroyMethod(){System.out.println("生命周期4:销毁");}
}
spring_lifecycle.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="projo.User"><property name="id" value="1"></property><property name="username" value="张三"></property><property name="password" value="124"></property><property name="age" value="16"></property></bean>
</beans>
创建测试类:
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import projo.User;public class lifecycle {@Testpublic void test(){ApplicationContext ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml");User user=ioc.getBean(User.class);System.out.println(user);}
}
输出:
生命周期1:实例化
生命周期2:依赖注入
User{username='张三', id=1, password='124', age=16}
我们在User类中,不仅创建了生命周期,而且还创建了销毁和初始化的方法,但是在输出结果中,销毁和初始化方法中的内容并没有输出,原因是:如果我们想要调用初始化和销毁的方法,需要在spring.xml文件中通过bean标签中的init-method和destory-method属性指定的,如下所示:
<bean id="user" class="projo.User" init-method="initMethod" destroy-method="destroyMethod">
再次测试,输出结果如下所示:
生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
User{username='张三', id=1, password='124', age=16}
此时的初始化方法被调用了,但销毁方法为什么依然没调用呢?
那么销毁的方法什么时候才会执行呢?我们知道bean对象都是通过IOC容器创建和管理的,那么一旦IOC容器关闭就意味着对象被销毁了
那么容器怎么关闭呢?之前在java中我们都是通过调用close方法区关闭资源,那么这里也可以吗?
一试便知,如下所示,当我们尝试调用close方法想要关闭IOC容器时,却发现没有这个方法
原因是:ApplicationContext类中并没有提供刷新或者关闭容器的功能,而其功能是在子接口[ConfigurableApplicationContext]中提供的
解决方案:
将ApplicationContext类修改为ConfigurableApplicationContext
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import projo.User;public class lifecycle {@Testpublic void test(){//获取IOC容器ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml");//获取beanUser user=ioc.getBean(User.class);//输出beanSystem.out.println(user);//关闭IOCioc.close();//关闭容器}
}
输出如下所示:
当容器关闭时,对象被成功销毁
生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
User{username='张三', id=1, password='124', age=16}
生命周期4:销毁
bean的作用域对生命周期的影响:
对上述的lifecycle类中,我们只保留获取IOC容器的代码,那么会输出什么?
输出如下所示:
生命周期1:实例化
生命周期2:依赖注入
生命周期3:初始化
通过输出结果我们会发现,生命周期的前三个方法是在获取IOC容器时,就执行的,并不是我们在获取bean时,才执行的,原因是,我们当前在IOC容器中配置的bean对象,默认是单例,那么就说明,我们根据这个IOC容器获取到的永远都是一个唯一的bean对象,那么我们根本没必要在获取的时候再去创建它,直接在创建IOC容器的时候就将其创建好,以后使用的都是同一个
既然是与单例还是多例有关,那么如果我们在bean标签中将其设置为多例,bean对象的创建还能否在获取IOC容器时就执行呢?
如下所示:
将当前的bean对象通过scope属性设置为多例:
<bean id="user" class="projo.User" init-method="initMethod" destroy-method="destroyMethod" scope="prototype">
我们再次运行测试类,会发现什么都没输出
那么也就是说,我们通过将bean的作用域设置为多例,每次获取到的对象都是新的,那么也就没有必要在获取IOC容器的时候将其创建好
将获取bean对象的代码添加,我发现和上述不同的时,对象创建有关的三个方法都被调用,那么足以说明,当bean对象的作用域为多例的情况下,对象的创建是在获取bean对象时完成的
由此还有一点不知道大家是否注意到,此时我们即使调用了close方法,但是销毁的方法依然没有被调用,原因是:当我们将bean对象的作用域设置为多例的时候,其销毁的方法就不由IOC容器控制了
bean的后置处理器:
bean的后置处理器会在生命周期的初始化前后添加额外的操作,需要实现BeanPostProcessor接口
,且配置到IOC容器中,需要注意的是,bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有的bean都会执行
创建新的类,使其实现BeanPostProcessor接口:
package process;import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostProcessor implements BeanPostProcessor {
}
仅仅实现该接口,并没有实现其中的方法,我们会发现,也是没有任何的报错的,Ctrl+b跟进源码, 如下所示,我们发现该接口中的方法原来是使用default关键字修饰的,
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package org.springframework.beans.factory.config;import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;public interface BeanPostProcessor {@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}
}
那么这两个方法到底有什么作用呢?
看方法名postProcessBeforeInitialization[初始化前的后置处理],postProcessAfterInitialization[初始化后的后置处理],下面我们对其进行重写,输出一些信息,如下所示:
package process;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("MyBeanPostProcessor--- >后置处理器postProcessBeforeInitialization");//此方法在bean的生命周期初始化之前执行return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("MyBeanPostProcessor--- >后置处理器postProcessAfterInitialization");//此方法在bean的生命周期初始化之后执行return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);}
}
你以为这就可以对bean对象进行处理了吗?当然不是,我们还需要将其配置到IOC容器中,如下所示:
<bean id="myBeanPostProcessor" class="process.MyBeanPostProcessor">
此时进行测试,输出如下:
我们发现上述在MyBeanPostProcessor类中所添加的两条语句都被输出了,那么后置处理器到底是对那个bean起作用呢?答案是:对当前IOC容器中的每个bean起作用,它会对每个bean在初始化前后都添加额外的操作