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 { @Test public 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的生命周期:
Spring容器负责管理bean的生命周期,可以在配置文件中通过配置来控制bean的创建
、初始化和销毁
。同时,也可以通过实现BeanPostProcessor接口来自定义bean的初始化和销毁过程中的操作
第一阶段:实例化:Spring容器会根据配置信息创建bean的实例。可以通过构造函数实例化或者通过工厂方法实例化。 第二阶段:属性赋值:Spring容器会将配置文件中定义的属性值或者引用注入到bean实例中。可以通过setter方法注入属性值,或者通过@Autowired注解、@Resource注解等自动注入属性。 第三阶段:初始化:Spring容器会调用bean的初始化方法,可以自定义初始化方法。可以通过实现InitializingBean接口的afterPropertiesSet()方法,或者在配置文件中使用init-method属性指定初始化方法。 第四阶段:使用:bean对象可以被其他对象使用,执行业务逻辑。 第五阶段:销毁:Spring容器会调用bean的销毁方法,可以自定义销毁方法。可以通过实现DisposableBean接口的destroy()方法,或者在配置文件中使用destroy-method属性指定销毁方法。 第六阶段: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:实例化"); } @Override public 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 { @Test public 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 { @Test public void test(){ //获取IOC容器 ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("spring_lifecycle.xml"); //获取bean User user=ioc.getBean(User.class); //输出bean System.out.println(user); //关闭IOC ioc.close();//关闭容器 } }
输出如下所示:
当容器关闭时,对象被成功销毁
生命周期1:实例化 生命周期2:依赖注入 生命周期3:初始化 User{username='张三', id=1, password='124', age=16} 生命周期4:销毁
bean的作用域对生命周期的影响:
对上述的lifecycle类中,我们只保留获取IOC容器的代码[如下所示],那么会输出什么?
ConfigurableApplicationContext ioc=new ClassPathXmlApplicationContext("applications.xml");
输出如下所示:
生命周期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="destroyMe
我们再次运行测试类,会发现什么都没输出
那么也就是说,我们通过将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关键字修饰的,
package org.springframework.beans.factory.config; import org.springframework.beans.BeansException; import org.springframework.lang.Nullable; public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default 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 { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("MyBeanPostProcessor--- >后置处理器postProcessBeforeInitialization"); //此方法在bean的生命周期初始化之前执行 return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName); } @Override public 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在初始化前后都添加额外的操作,而不是针对某一个。