这次我们来研究Bean的相关知识和spring boot自动配置的相关流程
1.Bean的作用域
1概念
在SpringIoC&DI阶段,我们学习了Spring是如何帮助我们管理对象的.
-
通过 @Controller ,@Service , @Repository , @Component , @Configuration , @Bean 来声明Bean对象.
-
通过ApplicationContext 或者BeanFactory 来获取对象
-
通过 @Autowired , Setter ⽅法或者构造⽅法等来为应⽤程序注⼊所依赖的Bean对象
我们编写代码,从spring容器中获取bean
package com.example.springconfig;
import com.example.springconfig.demos.model.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
@SpringBootApplication
public class SpringConfigApplication {
public static void main(String[] args) {
ApplicationContext context=SpringApplication.run(SpringConfigApplication.class, args);
User user1 = context.getBean(User.class);
user1.setName("lisi");
System.out.println( "当前对象地址:"+System.identityHashCode(user1));
User user2 = context.getBean(User.class);
System.out.println( "当前对象地址:"+System.identityHashCode(user2));
}
}
package com.example.springconfig.demos.config;
import com.example.springconfig.demos.model.User;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
public User user1(){
return new User(1,"zhangsan");
}
}
package com.example.springconfig.demos.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private Integer id;
private String name;
}
运行出结果
发现输出对象地址的值是一样的,说明从spring容器中取出的对象都是同一个
这也是"单例模式" 单例模式:确保⼀个类只有⼀个实例,多次创建也不会创建出多个实例
默认情况下,Spring容器中的bean都是单例的,这种⾏为模式,我们就称之为Bean的作⽤域.
Bean的作⽤域是指Bean在Spring框架中的某种⾏为模式.
⽐如单例作⽤域:表⽰Bean在整个Spring中只有⼀份,它是全局共享的.那么当其他⼈修改了这个值之后,那么另⼀个⼈读取到的就是被修改的值、
2.Bean的作用域
在Spring中⽀持6种作⽤域,后4种在SpringMVC环境才⽣效
-
singleton:单例作⽤域
-
prototype:原型作⽤域(多例作⽤域)
-
request:请求作⽤域
-
session:会话作⽤域
-
Application:全局作⽤域
-
websocket:HTTPWebSocket作⽤域
接下来 我们来看看这几个bean的作用域
在这之前我们先来看一下一些注解的详细
堆和栈之间的关系
各个bean作用域中的代码
package com.example.springconfig.demos.controller;
import com.example.springconfig.demos.model.User;
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;
import javax.annotation.Resource;
import javax.jws.soap.SOAPBinding;
@RequestMapping("/scope")
@RestController
public class BeanScopeController {
@Autowired
private ApplicationContext context;
@Resource(name = "singletonUser")
private User singletonUser;
@Resource(name = "prototypeUser")
private User prototypeUser;
@Resource(name = "requestUser")
private User requestUser;
@Resource(name = "sessionUser")
private User sessionUser;
@Resource(name = "applicationUser")
private User applicationUser;
@RequestMapping("/single")
public String single() {
/**
*1.从context中获取对象
* 2.属性注入获取对象
*/
User user = (User) context.getBean("singletonUser");
return "context中获取的对象是:" + user + ",属性注入获取的对象:" + System.identityHashCode(singletonUser);
}
@RequestMapping("/prototype")
public String prototype(){
/**
*1.从context中获取对象
* 2.属性注入获取对象
*/
User user= (User) context.getBean("prototypeUser");
return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(prototypeUser);
}
@RequestMapping("/request")
public String request(){
/**
*1.从context中获取对象
* 2.属性注入获取对象
*/
User user= (User) context.getBean("requestUser");
return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(requestUser);
}
@RequestMapping("/session")
public String session(){
/**
*1.从context中获取对象
* 2.属性注入获取对象
*/
User user= (User) context.getBean("sessionUser");
return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(sessionUser);
}
@RequestMapping("/application")
public String application(){
/**
*1.从context中获取对象
* 2.属性注入获取对象
*/
User user= (User) context.getBean("applicationUser");
return "context中获取的对象是:"+user+",属性注入获取的对象:"+System.identityHashCode(applicationUser);
}
}
package com.example.springconfig.demos.config;
import com.example.springconfig.demos.model.User;
import org.omg.CORBA.PUBLIC_MEMBER;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor;
@Configuration
public class BeanConfig {
@Bean
public User user1(){
return new User(1,"zhangsan");
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
// @Scope("singleton")
public User singletonUser(){
return new User();
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User prototypeUser(){
return new User();
}
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.TARGET_CLASS)
public User requestUser(){
return new User();
}
@Bean
@SessionScope
public User sessionUser(){
return new User();
}
@Bean
@ApplicationScope
public User applicationUser(){
return new User();
}
}
1.singleton
每次获取的都是同一个对象
2.prototype
每次获取的对象是不同的
3.request
请求每次获取的对象都不一样
4.session
在⼀个session中,多次请求,获取到的对象都是同⼀个.
5.application
在⼀个应⽤中,多次访问都是同⼀个对象
Applicationscope就是对于整个web容器来说,bean的作⽤域是ServletContext级别的.这个和singleton有点类似区别在于:Applicationscope是ServletContext的单例,singleton是⼀个ApplicationContext的单例.在⼀个web容器中ApplicationContext可以有多个.(了解即可)
3.Bean的生命周期
⽣命周期指的是⼀个对象从诞⽣到销毁的整个⽣命过程,我们把这个过程就叫做⼀个对象的⽣命周期.Bean的⽣命周期分为以下5个部分:
-
实例化(为Bean分配内存空间)
-
属性赋值(Bean注⼊和装配,⽐如 @AutoWired )
-
初始化
a. 执⾏各种通知,如 BeanNameAware ,BeanFactoryAware ,ApplicationContextAware 的接⼝⽅法.
b. 执⾏初始化⽅法
▪ xml定义 init-method
▪ 使⽤注解的⽅式 @PostConstruct
▪ 执⾏初始化后置⽅法( BeanPostProcessor )
-
使⽤Bean
-
销毁Bean
a. 销毁容器的各种⽅法,如 @PreDestroy ,DisposableBean 接⼝⽅法, destroymethod.
演示代码代码如下
package com.example.springconfig.demos.component;
import com.example.springconfig.demos.model.User;
import org.apache.ibatis.annotations.Update;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeComponent {
private User user;
public BeanLifeComponent() {
System.out.println("执行构造方法");
}
@Autowired
@Qualifier("user1")
public void setUser1(User user1) {
this.user = user;
System.out.println("属性注入");
}
@PostConstruct
public void init(){
System.out.println("执行初始化");
}
public void use(){
System.out.println("执行use");
}
@PreDestroy
public void destroy(){
System.out.println("执行destroy方法");
}
}
4.关于Bean相关的源码
我们举个例子来说一下,我们获取bean对象,并通过不同的方式拿取相关的值,
我们通过结果看到,源码里有类似代理类和目标类的东西,我们通过toString方法拿到的其实是spring代理的时候进行了重写,重写的逻辑是执行目标toString
代理类包含目标类
返回的user其实就是代理类 我们在执行程序的时候,代理类没有发生变化,目标类在发生变化
1.AbstractAutowireCapableBeanFactory
我们翻看AbstractAutowireCapableBeanFactory这个源码,在里面看到了
里面有实例化bean的源码
我们根据bean的周期来看
接下来就看属性注入
在属性注入里面有bean的初始化initializeBean 里面有bean初识化的各种代码
我们可以看到spring的源码是非常复杂的,我们现在也只是浅浅读了一下
5.spring自动配置
SpringBoot的⾃动配置就是当Spring容器启动后,⼀些配置类,bean对象等就⾃动存 ⼊到了IoC容器中,不需要我们⼿动去声明,从⽽简化了开发,省去了繁琐的配置操 作.
SpringBoot⾃动配置,就是指SpringBoot是如何将依赖jar包中的配置类以及Bean加载到SpringIoC容器中的
我们学习主要分以下两个⽅⾯:
-
Spring是如何把对象加载到SpringIoC容器中的
-
SpringBoot是如何实现的
我们先看看@SpringBootApplication这个注解 点进去
2.@SpringBootConfiguration
3.@EnableAutoConfiguration
这里面的注解是实现自动配置的核心
我们再点击进入import中的AutoConfigurationImportSelector.class
这里面的这个方法是主要的方法,我们在点进getAutoConfigurationEntry
标注的这一行是拿到配置信息,我们继续点击查看怎样拿到配置信息
我们搜索这个路径
文件里面就是配置的文件
4.@AutoConfigurationPackage
我们接下来看这个注解
点进进入
我们点进Registrar
里面就是获取包的名字之类的
总结