您的位置:首页 > 编程语言 > Java开发

Spring IoC控制反转(2)

2019-09-21 22:58 106 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/qq_40298351/article/details/101124851

一、Spring Bean的作用域

    在Spring中,bean作用域用于确定哪种类型的bean实例应该从Spring容器中返回给调用者。目前Spring Bean的作用域或者说范围主要有五种。

作用域 描述
singleton 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
request 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
session 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
application 限定一个Bean的作用域为
ServletContext
的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

(1)singleton

    如果bean的作用域的属性被声明为singleton,那么Spring Ioc容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那么Spring在每次需要时都返回同一个bean实例。

    Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,singleton作用域是Spring中的缺省作用域。你可以在 bean 的配置文件中设置作用域的属性为 singleton,如下所示:

[code]<bean id="book" class="com.buba.pojo.Book" scope="singleton"></bean>

使用注解定义作用域 

[code]@Component
@Scope("singleton")
public class Book {

private Integer bid;
private String bname;
[code]<!-- 	spring注解扫描 -->
<context:component-scan base-package="com.buba.*" />

举个栗子: 

[code]public class Book {

private Integer bid;
private String bname;

// ....省略get set

@Override
public String toString() {
return "Book [bid=" + bid + ", bname=" + bname + "]";
}

}

测试:

[code]@Test
public void selBookTest() {
//获取ApplicationContext上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Book book = (Book) context.getBean("book");
book.setBid(1);
book.setBname("冰菓");
Book book2 = (Book) context.getBean("book");
System.out.println(book2);
}

(2)prototype

    当一个bean的作用域为prototype,表示一个bean定义对应多个对象实例。声明为prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
举个栗子:

[code]<bean id="book" class="com.buba.pojo.Book" scope="prototype">
</bean>

在执行测试代码:

(3)request请求作用域

    request,session和application这三个作用域都是基于web的Spring WebApplicationContext实现的,只有在web环境下(比如XmlWebApplicationContext)中才能使用。 如果开发者仅仅在常规的Spring IoC容器中比如ClassPathXmlApplicationContext中使用这些作用域,那么将会抛出一个IllegalStateException来说明使用了未知的作用域。也就是当用户使用Spring的WebApplicationContext时,除了使用常规的singleton和prototype作用域之外,还可以使用另外3种Bean的作用域,即request,session和application。

    在使用Web应用环境相关的Bean作用域时,必须在Web容器中进行一些额外的配置:

    1.如果开发者使用了Spring Web MVC框架的话,每一个请求都会通过Spring的DispatcherServlet来处理,也就没有其他特殊的初始化配置,就不需要配置了。DispatcherServlet已经包含了相关的状态。

    2.如果开发者使用的是低版本Web容器比如Servlet 2.5的web容器,请求不是通过Spring的DispatcherServlet(比如JSF或者Struts)来处理的。那么开发者需要注册org.springframework.web.context.request.RequestContextListener或者ServletRequestListener。可以在web.xml中增加如下的Listener声明: 

[code]<listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
 </listener>


    ServletContextListener只负责监听web容器启动和关闭的事件,而RequestContextListener实现了ServletRequestListener监听器接口,该监听器监听http请求事件。Web服务器接收到的每一次请求都会通知该监听器。 

而在Servlet 3.0以后,这些都能够通过WebApplicationInitializer接口来实现配置。

    3.如果不使用Listener,也可以考虑使用Spring的RequestContextFilter,通过http过滤器进行配置,在url-pattern中对所有的页面进行过滤。也是在web.xml中进行配置。

[code]<filter>
        <filter-name&
7ff7
gt;requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

接下来就可以使用另外三种作用域了

[code]<!-- request请求作用域 -->
<bean id="loginController" class="com.buba.controller.LoginController" scope="request"/>

    Spring容器会在每次用到LoginController来处理每个HTTP请求的时候都会创建一个新的LoginController实例。也就是说,LoginController Bean的作用域是HTTP Request级别的。 当http请求调用作用域为request的bean的时候,每增加一个HTTP请求,Spring就会创建一个新的bean,在请求处理完成之后便及时销毁这个bean。开发者可以随意改变实例的状态,因为通过LoginController请求来创建的其他实例根本看不到开发者改变的实例状态,所有创建的Bean实例都是根据独立的请求来的。

(4)session会话作用域

[code]<bean id="user" class="com.buba.pojo.User" scope="session"/>

    Spring容器会在每次调用到user时,在一个单独的HTTP会话周期来创建一个新的user实例。换言之,user的作用域是HTTP Session级别的。Session中所有http请求共享同一个请求的bean实例。Session结束后就销毁bean。 在request Scope作用域的Bean上,开发者可以随意的更改实例的状态。同样,使用从同一个user bean定义创建的其他HTTP Session实例在看不到不是自己的内部状态的修改,因为他们是单个的HTTP会话。每个Session请求都会创建新的user实例,所以开发者更改一个Bean的状态,对于其他的Bean仍然是不可见的。

(5)application全局作用域

[code]<bean id="book" class="com.buba.pojo.Book" scope="application"/>

    Spring容器会在整个web应用范围使用到book的时候创建一个新的book的实例。也就是说,book是在ServletContext级别的,作为常规的ServletContext属性。这种作用域在一些程度上来说和Spring的单例作用域相似,但是也有如下不同之处:

    1.application作用域是每个ServletContext中包含一个,而不是每个SpringApplicationContext之中包含一个(某些应用中可能包含不止一个ApplicationContext)。

    2.application作用域仅仅作为ServletContext的属性可见,单例Bean是ApplicationContext可见。

二、Spring容器的生命周期

    假设一个Bean实现了所有的接口,大的概况一下Bean的生命周期:

    1.实例化BeanFactoryPostProcessor:处理的对象是BeanFactory级别

    2.实例化BeanPostProcessor实现类

    3.实例化InstantiationAwareBeanPostProcessorAdapter实现类,注:该类是BeanPostProcessor的扩展

    4.执行InstantiationAwareBeanPostProcessorAdapter类的postProcessBeforeInstantiation方法

    5.Bean的构造方法

    6.执行InstantiationAwareBeanPostProcessorAdapter类的postProcessPropertyValues

    7.为Bean注入属性,即依赖注入

    8.调用BeanNameAware的setBeanName方法

    9.调用BeanNameAware的setBeanFactory方法

    10.执行BeanPostProcessor的后置处理器,postProcessAfterInitialization方法

    11.调用InitializingBean的afterPropertiesSet方法

    12.调用bean的init-method初始化方法

    13.执行BeanPostProcessor的postProcessAfterInitialization

    14.执行InstantiationAwareBeanPostProcessorAdapter的后置方法,postProcessAfterInitialization

    15.Bean的使用

    16.调用DiposibleBean的destory方法

    17.调用bean指定的destory-method方法
 

测试:

[code]package com.buba.pojo;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

/**
* 测试Spring Bean的生命周期
*/
public class Book implements InitializingBean, DisposableBean, BeanNameAware, BeanFactoryAware {

private Integer bid;
private String bname;
// 实现了BeanNameAware接口,Spring可以将BeanName注入该属性中
private String beanName;
// 实现了BeanFactory接口,Spring可将BeanFactory注入该属性中
private BeanFactory beanFactory;

public Book() {
System.out.println("【Bean构造方法】book的构造方法");
}

public Integer getBid() {
return bid;
}

public void setBid(Integer bid) {
System.out.println("【set注入】注入id属性");
this.bid = bid;
}

public String getBname() {
return bname;
}

public void setBname(String bname) {
System.out.println("【set注入】注入name属性");
this.bname = bname;
}

@Override
public String toString() {
return "Book [bid=" + bid + ", bname=" + bname + "]";
}

/**
* 自己编写的初始化方法
*/
public void myInit() {
System.out.println("【init-method】调用init-method属性配置的初始化方法");
}

/**
* 自己编写的销毁方法
*/
public void myDestroy() {
System.out.println("【destroy-method】调用destroy-method属性配置的销毁方法");
}

/**
* BeanFactoryAware接口的方法
*
* @param beanFactory
* @throws BeansException
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
System.out.println("【BeanFactoryAware接口】调用BeanFactoryAware的setBeanFactory方法得到beanFactory引用");
}

/**
* BeanNameAware接口的方法
*
* @param name
*/
public void setBeanName(String name) {
this.beanName = name;
System.out.println("【BeanNameAware接口】调用BeanNameAware的setBeanName方法得到Bean的名称");
}

/**
* InitializingBean接口的方法
*
* @throws Exception
*/
public void afterPropertiesSet() throws Exception {
System.out.println("【InitializingBean接口】调用InitializingBean接口的afterPropertiesSet方法");
}

/**
* DisposableBean接口的方法
*
* @throws Exception
*/
public void destroy() throws Exception {
System.out.println("【DisposableBean接口】调用DisposableBean接口的destroy方法");
}

}

MyBeanFactoryPostProcessor工厂处理器类:

[code]/*
BeanFactoryPostProcessor是beanFactory的后置处理器
在BeanFactory标准初始化之后调用,这时所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建
来定制和修改BeanFactory的内容,如覆盖或添加属性
*/
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

public MyBeanFactoryPostProcessor() {
System.out.println("【BeanFactoryPostProcessor接口】调用BeanFactoryPostProcessor实现类构造方法");
}

/**
* 重写BeanFactoryPostProcessor接口的postProcessBeanFactory方法,可通过该方法对beanFactory进行设置
*/
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("【BeanFactoryPostProcessor接口】调用BeanFactoryPostProcessor接口的postProcessBeanFactory方法");
}

}

 MyInstantiationAwareBeanPostProcessor类:

[code]/*
InstantiationAwareBeanPostProcessor 执行时机bean实例化(Instantiation)阶段,
典型用于替换bean默认创建方式,例如aop通过拓展接口生成代理对应,主要用于基础框架层面。
如果日常业务中需要拓展该,spring推荐使用适配器类InstantiationAwareBeanPostProcessorAdapter。
*/
public class MyInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {

public MyInstantiationAwareBeanPostProcessor() {
System.out.println("【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor构造方法");
}

/**
* 实例化Bean之前调用
*/
public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
System.out.println(
"【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法");
return null;
}

/**
* 实例化Bean之后调用
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(
"【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInitialization方法");
return bean;
}

/**
* 设置某个属性时调用
*/
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean,
String beanName) throws BeansException {
System.out.println(
"【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues方法");
return pvs;
}
}

MyBeanPostProcessor类:

[code]/*
BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口。
接口提供了两个方法,分别是初始化前和初始化后执行方法,具体这个初始化方法指的是什么方法,
类似我们在定义bean时,
定义了init-method所指定的方法<bean id = "xxx" class = "xxx" init-method = "init()">

这两个方法分别在init方法前后执行,需要注意一点,我们定义一个类实现了BeanPostProcessor,
默认是会对整个Spring容器中所有的bean进行处理。
这个的处理是发生在Spring容器的实例化和依赖注入之后。
*/
public class MyBeanPostProcessor implements BeanPostProcessor {

public MyBeanPostProcessor(){
System.out.println("【BeanPostProcessor接口】调用BeanPostProcessor的构造方法");
}
/*
Bean初始化方法前被调用
方法中有两个参数。类型分别为Object和String,第一个参数是每个bean的实例,
第二个参数是每个bean的name或者id属性的值。所以我们可以通过第二个参数,
来确认我们将要处理的具体的bean。
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【BeanPostProcessor接口】调用postProcessBeforeInitialization方法,这里可对"+beanName+"的属性进行更改。");
return bean;
}
/*
Bean初始化方法后被调用
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("【BeanPostProcessor接口】调用postProcessAfterInitialization方法,这里可对"+beanName+"的属性进行更改。");
return bean;
}
[code]<!-- 生命周期测试 -->
<!--配置Bean的后置处理器 -->
<bean id="beanPostProcessor"
class="com.buba.test.MyBeanPostProcessor">
</bean>

<!--配置instantiationAwareBeanPostProcessor -->
<bean id="instantiationAwareBeanPostProcessor"
class="com.buba.test.MyInstantiationAwareBeanPostProcessor">
</bean>

<!--配置BeanFactory的后置处理器 -->
<bean id="beanFactoryPostProcessor"
class="com.buba.test.MyBeanFactoryPostProcessor">
</bean>

<bean id="book" class="com.buba.pojo.Book"
init-method="myInit" destroy-method="myDestroy" scope="singleton">
<property name="bid" value="1"></property>
<property name="bname" value="冰菓"></property>
</bean>
[code]@Test
public void selBookTest() {
System.out.println("--------------【初始化容器】---------------");

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("-------------------【容器初始化成功】------------------");
//得到studentBean,并显示其信息
Book book = context.getBean("book",Book.class);
System.out.println(book);

System.out.println("--------------------【销毁容器】----------------------");
((ClassPathXmlApplicationContext)context).registerShutdownHook();

}

执行结果:

[code]--------------【初始化容器】---------------
九月 21, 2019 11:33:22 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
九月 21, 2019 11:33:24 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
【BeanFactoryPostProcessor接口】调用BeanFactoryPostProcessor实现类构造方法
【BeanFactoryPostProcessor接口】调用BeanFactoryPostProcessor接口的postProcessBeanFactory方法
2019-09-21 23:33:24 [main] INFO  PostProcessorRegistrationDelegate$BeanPostProcessorChecker:330 - Bean 'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0' of type [org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
【BeanPostProcessor接口】调用BeanPostProcessor的构造方法
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor构造方法
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues方法
【BeanPostProcessor接口】调用postProcessBeforeInitialization方法,这里可对bookMapper的属性进行更改。
【BeanPostProcessor接口】调用postProcessAfterInitialization方法,这里可对bookMapper的属性进行更改。
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInitialization方法
-------------------【容器初始化成功】------------------
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessBeforeInstantiation方法
【Bean构造方法】book的构造方法
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessPropertyValues方法
【set注入】注入id属性
【set注入】注入name属性
【BeanNameAware接口】调用BeanNameAware的setBeanName方法得到Bean的名称
【BeanFactoryAware接口】调用BeanFactoryAware的setBeanFactory方法得到beanFactory引用
【BeanPostProcessor接口】调用postProcessBeforeInitialization方法,这里可对book的属性进行更改。
【InitializingBean接口】调用InitializingBean接口的afterPropertiesSet方法
【init-method】调用init-method属性配置的初始化方法
【BeanPostProcessor接口】调用postProcessAfterInitialization方法,这里可对book的属性进行更改。
【InstantiationAwareBeanPostProcessor接口】调用InstantiationAwareBeanPostProcessor接口的postProcessAfterInitialization方法
Book [bid=1, bname=冰菓]
--------------------【销毁容器】----------------------
【DisposableBean接口】调用DisposableBean接口的destroy方法
【destroy-method】调用destroy-method属性配置的销毁方法

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: