Bean的作用域和生命周期

Bean的作用域

我们先来看下面这段代码

首先是一个Dog类  (此处使用lombok来完成setter、getter、toString方法)

@Setter
@Getter
public class Dog {
    private String name;

}

然后在DogBeanConfig类里面写一个返回Dog的方法,并将这个方法的返回对象存入Spring容器

@Component
public class DogBeanConfig {
    @Bean
    public Dog singleDog() {
        Dog dog = new Dog();
        return dog;
    }
}

 然后在类里分别注入这个Dog对象

RestController
public class DogController {
    @Autowired
    private Dog singleDog;

@RequestMapping("/single")
    public String single(){
        Dog contextDog = (Dog)applicationContext.getBean("singleDog");
        return "dog:"+singleDog.toString()+",contextDog:"+contextDog;
    }
}

结果

多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean() 也是同⼀个对象.(在打印对象时,Java的Object.toString()方法(如果类没有重写这个方法)会返回对象的类名加上这个哈希码的十六进制表示) 快速查看对象的类型或唯一性时,这种输出非常有用。

这就是Bean的不同作⽤域了


Bean的六种作用域

(在Spring中⽀持6种作⽤域,后4种在Spring MVC环境才⽣效)

1. 单例模式:singleton(默认)-- 每次注入都是同一份对象。默认为这种模式也是Spring为了性能的考虑。

2. 原型模式:prototype -- 每次注入都是一个新的对象。

3. 请求作用域(适用于Spring MVC / Spring Web):request -- 每次http请求中共享一份Bean对象。

4. 回话作用域(适用于Spring MVC / Spring Web):session -- 每次会话(session)共享一份Bean对象。

5. 全局作用域(适用于Spring MVC / Spring Web):applicaton -- 每个http servlet context中共享一份Bean对象。

6. websocket:适用于Spring WebSocket项目。


Bean的作用域设置

Bean设置作用域使用 @Scope 注解

使用方法:

@Scope 注解里加上作用域名,比如:@Scope("prototype") 此时被该注解修饰的类为原型模式。

定义⼏个不同作⽤域的Bean

package demo.beanscope;

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 22652
 * Date: 2024-08-03
 * Time: 13:37
 */
@Component
public class DogBeanConfig {
    @Bean
    public Dog dog() {
        Dog dog = new Dog();
        dog.setName("旺旺");
        return dog;
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
    public Dog singleDog() {
        Dog dog = new Dog();
        return dog;
    }

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Dog prototypeDog() {
        Dog dog = new Dog();
        return dog;
    }

    @Bean
    @RequestScope
    public Dog requestDog() {
        Dog dog = new Dog();
        return dog;
    }

    @Bean
    @SessionScope
    public Dog sessionDog() {
        Dog dog = new Dog();
        return dog;
    }

    @Bean
    @ApplicationScope
    public Dog applicationDog() {
        Dog dog = new Dog();
        return dog;
    }
}

@RequestScope 等同于 @Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)其他同理

 测试不同作⽤域的Bean取到的对象是否⼀样

package demo.beanscope;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 22652
 * Date: 2024-08-03
 * Time: 13:41
 */
@RestController
public class DogController {
    @Autowired
    private Dog singleDog;
    @Autowired
    private Dog prototypeDog;
    @Autowired
    private Dog requestDog;
    @Autowired
    private Dog sessionDog;
    @Autowired
    private Dog applicationDog;
    @Autowired
    private ApplicationContext applicationContext;

    @RequestMapping("/single")
    public String single(){
        Dog contextDog = (Dog)applicationContext.getBean("singleDog");
        return "dog:"+singleDog.toString()+",contextDog:"+contextDog;
    }
    @RequestMapping("/prototype")
    public String prototype(){
        Dog contextDog = (Dog)applicationContext.getBean("prototypeDog");
        return "dog:"+prototypeDog.toString()+",contextDog:"+contextDog;
    }
    @RequestMapping("/request")
    public String request(){
        Dog contextDog = (Dog)applicationContext.getBean("requestDog");
        return
                "dog:"+requestDog.toString()+",contextDog:"+contextDog.toString();
    }

    @RequestMapping("/session")
    public String session(){
        Dog contextDog = (Dog)applicationContext.getBean("sessionDog");
        return
                "dog:"+sessionDog.toString()+",contextDog:"+contextDog.toString();
    }
    @RequestMapping("/application")
    public String application(){
        Dog contextDog = (Dog)applicationContext.getBean("applicationDog");
        return
                "dog:"+applicationDog.toString()+",contextDog:"+contextDog.toString();
    }
}

观察Bean的作⽤域

单例作⽤域: http://127.0.0.1:8080/single 多次访问, 得到的都是同⼀个对象, 并且 @Autowired 和 applicationContext.getBean() 也是同⼀个对象。

多例作⽤域: http://127.0.0.1:8080/prototype 观察ContextDog, 每次获取的对象都不⼀样(注⼊的对象在Spring容器启动时, 就已经注⼊了, 所以多次 请求也不会发⽣变化)

请求作⽤域: http://127.0.0.1:8080/request 在⼀次请求中, @Autowired 和 applicationContext.getBean() 也是同⼀个对象. 但是每次请求, 都会重新创建对象

 

会话作⽤域: http://127.0.0.1:8080/session 在⼀个session中, 多次请求, 获取到的对象都是同⼀个.

 换⼀个浏览器访问, 发现会重新创建对象.(另⼀个Session)

Application作⽤域: http://127.0.0.1:8080/application在⼀个应⽤中, 多次访问都是同⼀个对象 

 Application scope就是对于整个web容器来说, bean的作⽤域是ServletContext级别的. 这个和 singleton有点类似,区别在于: Application scope是ServletContext的单例, singleton是⼀个 ApplicationContext的单例. 在⼀个web容器中ApplicationContext可以有多个. (了解即可)

Bean的生命周期 

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

1. 实例化(为Bean分配内存空间)

2. 属性赋值(Bean注⼊和装配, ⽐如 @AutoWired )

3. 初始化

        a. 执⾏各种通知, 如 BeanNameAware , BeanFactoryAware , ApplicationContextAware 的接⼝⽅法.

        b. 执⾏初始化⽅法:

  • xml定义 init-method
  • 使⽤注解的⽅式 @PostConstruct
  • 执⾏初始化后置⽅法( BeanPostProcessor )

4. 使⽤Bean

5. 销毁Bean

        a. 销毁容器的各种⽅法, 如 @PreDestroy , DisposableBean 接⼝⽅法, destroymethod.

如图

//通知需要BeanNameAware接口
public class BeanComponent implements BeanNameAware {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行了通知: " + s);
    }
 
    public void myInit() {
        System.out.println("XML方式的初始化方法");
    }
 
    @PostConstruct
    public void doPostConstruct() {
        System.out.println("注解方法的初始化方法");
    }
 
    public void myDestroy() {
        System.out.println("XML方式的销毁方法");
    }
    
    @PreDestroy
    public void doPreDestroy() {
        System.out.println("注解方式的销毁方法");
    }
 
    
    public void sayHi() {
        System.out.println("执行 sayHi方法");
    }
}

下面我们来执行这个类。

public static void main(String[] args) {
        //启动容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        //从容器获取Bean对象
        BeanComponent beanComponent = context.getBean("beanComponent", BeanComponent.class);
        //执行Bean对象的内容
        beanComponent.sayHi();
        //销毁Bean对象的方法
        context.destroy();
    }

和上述的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 构造⽅法");
        }
    }

我们可以发现很可能是为了避免空指针异常。

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值