Bean的作用域和生命周期

目录

前景引入:

作用域定义

Bean的六种作用域

单例作用域(Singleton)VS 全局作用域(application)

如何设置 Bean 作用域?

Bean的生命周期 


前景引入:

假设现在有⼀个公共的 Bean,提供给 A ⽤户和 B ⽤户使⽤,当A把这个公共Bean修改之后,会导致 B ⽤户获取到这个Bean的时候是修改之后的Bean。

案例如下:

公共Bean:

@Component
public class Users {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java"); // 【重点:名称是 Java】
        return user;
    }
}

A用户使用时,进行了修改操作: 

@Controller
public class BeanScopesController {
    @Autowired
    private User user1;
    public User getUser1() {
        User user = user1;
        System.out.println("Bean 原 Name:" + user.getName());
        user.setName("悟空"); // 【重点:进⾏了修改操作】
        return user;
    }
}

B用户使用公共Bean:

@Controller
public class BeanScopesController2 {
    @Autowired
    private User user1;
    public User getUser1() {
        User user = user1;
        return user;
    }
}

 打印A用户和B用户所持有的公共Bean:


public class BeanScopesTest {
    public static void main(String[] args) {
        ApplicationContext context = 
                new ClassPathXmlApplicationContext("spring-config.xml");
        BeanScopesController beanScopesController = 
                context.getBean(BeanScopesController.class);
        System.out.println("A 对象修改之后 Name:" + 
                beanScopesController.getUser1().toString());
        BeanScopesController2 beanScopesController2 = 
                context.getBean(BeanScopesController2.class);
        System.out.println("B 对象读取到的 Name:" + 
                beanScopesController2.getUser1().toString());
    }
}

运行结果: 

Bean的作用域和生命周期_第1张图片

原因分析:

操作以上问题的原因是因为 Bean 默认情况下是单例状态(singleton),也就是所有⼈的使⽤的都是同⼀个对象,之前我们学单例模式的时候都知道,使⽤单例可以很⼤程度上提⾼性能,所以在 Spring 中Bean 的作⽤域默认也是 singleton 单例模式。
 

作用域定义

        限定程序中变量的可⽤范围叫做作⽤域,或者说在源代码中定义变量的某个区域就叫做作⽤域。
        ⽽ Bean 的作⽤域是指 Bean 在 Spring 整个框架中的某种⾏为模式,⽐如 singleton 单例作⽤域,就表示 Bean 在整个 Spring 中只有⼀份,它是全局共享的,那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值

Bean的六种作用域

Spring 容器在初始化⼀个 Bean 的实例时,同时会指定该实例的作⽤域。Spring有 6 种作⽤域,最后四种是基于 Spring MVC ⽣效的:

  • singleton:单例作⽤域
  • prototype:原型作⽤域(多例作⽤域)
  • request:请求作⽤域
  • session:回话作⽤域
  • application:全局作⽤域
  • websocket:HTTP WebSocket 作⽤域

Singleton

  • 官⽅说明:(Default) Scopes a single bean definition to a single object instance for eachSpring IoC container.
  • 描述:该作⽤域下的Bean在IoC容器中只存在⼀个实例:获取Bean(即通过applicationContext.getBean等⽅法获取)及装配Bean(即通过@Autowired注⼊)都是同⼀个对象。
  • 场景:通常⽆状态的Bean使⽤该作⽤域。⽆状态表示Bean对象的属性状态不需要更新
  • 备注:Spring默认选择该作⽤域

prototype

  • 官方说明: Scopes a single bean definition to any number of object instances
  • 描述: 每次对该作用域下的 Bean 的请求都会创建新的实例: 获取 Bean (即通过applicationContext.getBean 等方法获取) 及装配 Bean (即通过 @Autowired 注入) 都是新的对象实例。
  • 场景: 通常有状态的 Bean 使用该作用域

request

  • 官方说明: Scopes a single bean definition to the lifecycle of a single HTTP requestThat is, each HTTP request has its own instance of a bean created off the back of asingle bean definition. Only valid in the context of a web-aware SpringApplicationContext
  • 描述: 每次 Http 请求会创建新的 Bean 实例,类似于 prototype。
  • 场景:一次 Http 的请求和响应的共享 Bean.
  • 备注: 限定 Spring MVC 框架中使用。

session

  • 官方说明: Scopes a single bean definition to the lifecycle of an HTTP Session. Onlyvalid in the context of a web-aware Spring ApplicationContext.
  • 描述: 在一个 Http Session 中,定义一个 Bean 实例。
  • 场景: 用户会话的共享 Bean,比如: 记录一个用户的登陆信息。
  • 备注: 限定 Spring MVC 框架中使用。

application

  • 官方说明: Scopes a single bean definition to the lifecycle of a ServletContext. Onlvalid in the context of a web-aware Spring ApplicationContext.
  • 描述: 在一个 Http Servlet Context 中,定义一个 Bean 实例。
  • 场景: Web 应用的上下文信息,比如: 记录一个应用的共享信息。
  • 备注: 限定 Spring MVC 框架中使用。

单例作用域(Singleton)VS 全局作用域(application)

  • singleton 是 Spring Core 的作⽤域;application 是 Spring Web 中的作⽤域。
  • singleton 作⽤于 IoC 的容器,⽽ application 作⽤于 Servlet 容器。

如何设置 Bean 作用域?

答: 可以通过 @Scope 注解来设置 Bean 的作用域,它的设置方式有以下两种

  • 直接设置作用域的具体值,如: @Scope("prototype");
  • 设置 ConfigurableBeanFactory 和 WebApplicationContext 提供的 SCOPE XXX 变量,如@Scope(ConfigurableBeanFactory.SCOPE PROTOTYPE).
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
public class Users {
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @Bean(name = "u1")
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java"); // 【重点:名称是 Java】
        return user;
    }
}

Bean的生命周期 

所谓的⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期。
Bean 的⽣命周期分为以下 5 ⼤部分:

  1. 实例化 Bean(从无到有,为Bean分配内存空间,调用Bean的构造方法):实例化≠初始化
  2. 设置属性(注入属性):在创建实例后,Spring将会通过依赖注入(DI)的方式将Bean的属性赋值。
  3. 初始化 Bean:执行各种 Aware 通知的⽅法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接⼝⽅法;调用初始化前置方法 -> 会调用所有实现BeanPostProcessor接口的类的postProcessBeforeInitialization方法。执行初始化方法(注解方式:@PostConstruct,XML方法:init-methodf方法),执行初始化后置方法->调用所有实现了BeanPostProcessor接口的类的postProcessAfterInitialization方法。
  4. 使⽤ Bean,在初始化后,Bean可以被使用,在这个阶段,Bean执行它的业务逻辑。
  5. 销毁 Bean:销毁Bean的实例。

需要注意的是:第二步和第三步不能进行调换位置,因为可能在初始化时候用到Bean的属性。

如下所示,可能Bean会在初始化时候调用自己的方法,如果没有设置属性,那么就会导致程序出错。

    @Service
    public class UserService {
        public UserService(){
            System.out.println("调⽤ User Service 构造⽅法");
        }
        public void sayHi(){
            System.out.println("User Service SayHi.");
        }
    } 
    @Controller
    public class UserController {
        @Resource
        private UserService userService;
        @PostConstruct
        public void postConstruct() {
            userService.sayHi();
            System.out.println("执⾏ User Controller 构造⽅法");
        }
    }

下面举个买房子的例子,方便理解Bean的生命周期:

  1. 先买房(实例化,从⽆到有);
  2. 装修(设置属性);
  3. 买家电,如洗⾐机、冰箱、电视、空调等([各种]初始化);
  4. ⼊住(使⽤ Bean);
  5. 卖出去(Bean 销毁)。

以下为代码演示Bean的生命周期过程:

spring-config.xml如下:(bean为BeanComponent)



    
    

Bean(BeanComponent)如下:

package com.java.demo.component;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.BeanPostProcessor;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class BeanCompont implements BeanNameAware, BeanPostProcessor {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行了通知 BeanName -> " + s);
    }

    /**
     * xml 方式的初始化方法
     */
    public void myInit() {
        System.out.println("XML 方式初始化");
    }

    @PostConstruct
    public void doPostConstruct() {
        System.out.println("注解初始化方法");
    }

    public void sayHi() {
        System.out.println("执行 sayHi()");
    }

    @PreDestroy
    public void doPreDestroy() {
        System.out.println("do PreDestroy");
    }
    
    // 初始化前置方法
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("do postProcessBeforeInitialization");
        return bean;
    }
    // 初始化后置方法
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("do postProcessAfterInitialization");
        return bean;
    }
}

调用类:

package com.java.demo;

import com.java.demo.component.BeanCompont;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("spring-config.xml");
        BeanCompont compont = context.getBean("beanCompont", BeanCompont.class);
        compont.sayHi();
        context.close();
    }
}

执行结果如下:

说明:这里同时用注解和xml方式进行Bean的初始化,注解方式的优先级是比较高的。

Bean的作用域和生命周期_第2张图片

你可能感兴趣的:(java,spring,bean)