【Spring篇】深入浅出的去理解Spring Bean的生命周期及作用域

·  阅读 551

【Spring篇】深入浅出的去理解Spring Bean的生命周期及作用域

###前言

作者:Lucas
微信公众号:走进Java
学习与进步,我们一起来做

在我们Java开发当中,Java是毫无争议的主流开发语言。离不开的是那些牛人开放源代码的J2EE应用程序框架。比如Spring
就是我们从事Java开发的人员必须会一个开源框架。因为它可以与无数的、各种各样的框架相整合,因为它强大的IOC,AOP以及MVC。
复制代码

####1.Spring中的Bean是个什么鬼? 在实际应用Spring当中,Spring为我们提供的IOC容器就是为了管理我们的Bean的。官方解释:在 Spring当中,构成应用程序主干并由 Spring IOC 容器管理的对象称作为Bean。Bean是一个由Spring IOC 容器实例化、组装和管理的对象。

2.Spring中的IOC容器。

IOC:控制反转。程序运行时,依赖对象由【辅助程序】动态生成并注入到被依赖对象中,动态绑定两者的使用关系。

Spring IoC容器就是这样的辅助程序,它负责对象的生成和依赖的注入,让后在交由我们使用。

简而言之,就是:IoC就是一个对象定义其依赖关系而不创建它们的过程。

3.怎么定义一个Bean(创建一个Bean)

1.使用构造型@Component、@Service、@Repository、@Controller

例如这样:

@ Component
class MySpringBean {

}  
复制代码

加了这几个注解的类,就会被我们Spring所管理。他们区别在于:
@Component就跟我们配置文件里面的一样, 用于把我们的类注入到Spring,交给Spring去管理。

而@Service,@Controller,@Repository = @Component + 一些特定的功能。

这些注解常被我们用于分层操作:

有这些分层操作的话,代码之间就实现了松耦合,代码之间的调用也清晰明朗,便于项目的管理;假想一下,如果只用@Controller注解,那么所有的请求转发,业务处理,数据库操作代码都糅合在一个地方,那这样的代码该有多难拓展和维护。

@Repository注解在持久层中,具有将数据库操作抛出的原生异常翻译转化为spring的持久层异常的功能。

@Controller是spring-mvc的注解,具有将请求进行转发,重定向的功能。

@Service是业务逻辑层注解,这个注解只是标注该类处于业务逻辑层。

4.Spring Bean的生命周期

大致流程如下图:

实例化 --- 属性赋值 --- 初始化方法 --- 使用 --- 销毁

1、Spring对bean进行实例化

2、Spring将值和bean的引用注入到bean对应的属性中

3、如果bean实现了BeanNameAware接口, Spring将bean的ID传递给setBeanName()

4、如果bean实现了BeanFactoryAware接口, Spring将调用setBeanFactory(),将BeanFactory容器实例传入

5、如果bean实现了ApplicationContextAware接口, Spring将调用setApplicationContext(),将bean所在的应用上下文的引用传入

进来

6、如果bean实现了BeanPostProcessor接口, Spring将调用它们的postProcessBeforeInitialization()

7、如果bean实现了InitializingBean接口, Spring将调用它们的afterPropertiesSet()。类似地,如果bean使用init-method声

明了初始化方法,该方法也会被调用

8、如果bean实现了BeanPostProcessor接口, Spring将调用它们的postProcessAfterInitialization()

9、此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁

10、如果bean实现了DisposableBean接口, Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了

销毁方法,该方法也会被调用

5.Spring Bean的作用域

作用域限定了Spring Bean的作用范围,在Spring配置文件定义Bean时,通过声明scope配置项,可以灵活定义Bean的作用范围。例如,当你希望每次IOC容器返回的Bean是同一个实例时,可以设置scope为singleton;当你希望每次IOC容器返回的Bean实例是一个新的实例时,可以设置scope为prototype

scope配置项有5个属性,用于描述不同的作用域。

① singleton

使用该属性定义Bean时,IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例。

② prototype

使用该属性定义Bean时,IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例。

③ request

该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。

④ session

该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。

⑤ global-session

该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。

我们这里介绍singleton与prototype作用域,其他三个我们结合后面的知识放到后续的文章当中

5.1 Singleton 作用域

singleton是默认的作用域,当定义Bean时,如果没有指定scope配置项,Bean的作用域被默认为singleton。singleton属于单例模式,在整个系统上下文环境中,仅有一个Bean实例。也就是说,在整个系统上下文环境中,你通过Spring IOC获取的都是同一个实例。

5.2prototype 作用域

每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()

5.3 request 作用域

每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境

5.4 session 作用域

同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境

5.5 global-session 作用域

一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境

跟小Lucas学东西,必须可少的必须是举例说明,下面我们就来写个代码,来看出这些作用域的具体区别: 首先准备一个类

public class Category {
    //属性
    private String name;
    //设置该属性的方法
    public void setName(String name){
        this.name=name;
    }
    //获取该属性的方法
    public void getName(){
        System.out.println(name);
    }
}
复制代码

singleton作用域:

<?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-3.0.xsd">

<!--    id自己命名,class就是需要注入属性的类-->
    <bean id="c" class="Category" scope="singleton">
   </bean>
</beans>
复制代码
public class TestSpring {
    public static void main(String[] args) {
        //Spring的API ApplicationContext。applicationContext.xml就是自己创建的配置文件
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //c就是后面配置文件的id
        Category category=(Category)context.getBean("c");
        //不在配置文件中设置,用set方法设置属性的值
        category.setName("第一次设置");
        category.getName();
        //第二个对象
        Category category1=(Category)context.getBean("c");
        //不用设置,直接获取属性的值
        category1.getName();
    }
}
复制代码

测试类

public class TestSpring {
    public static void main(String[] args) {
        //Spring的API ApplicationContext。applicationContext.xml就是自己创建的配置文件
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        //c就是后面配置文件的id
        Category category=(Category)context.getBean("c");
        //不在配置文件中设置,用set方法设置属性的值
        category.setName("第一次设置");
        category.getName();
        //第二个对象
        Category category1=(Category)context.getBean("c");
        //不用设置,直接获取属性的值
        category1.getName();
    }
}
复制代码
最后的结果是:

第一次设置
第一次设置

两次输出相同的内容,说明id为c的bean只有1个,两次返回的都是同一个值。

prototype作用域:

配置文件中的scope改成prototype。其他所有代码不变,包括测试类

最后输出:

第一次设置
null

说明容器每次调用getBean时返回的是一个新的实例,所以第一次设置对第二个对象无效。
复制代码
分类:
后端
标签: