在spring启动时,会默认构建所有的bean并交给IOC容器管理。
获取IOC容器对象
依赖注入的形式获得IOC容器对象,进而获得bean。
private ApplicationContext applicationContext;
获取bean
以类
public class Test{
}
为例
applicationContext.getBean("bean名称-test")
applicationContext.getBean("bean的类型-Test.class")
applicationContext.getBean("bean名称-test","bean类型-Test.class")
bean的作用域
singleton:启动只会创建一个bean(默认),单例bean的初始化以及依赖注入一般都在容器初始化阶段进行。
prototype:每次启动都创建一个新的bean,多例bean 在容器启动时不实例化,即使设置 lazy-init 为 false 也没用,只有调用了getBean()才进行实例化。可以在bean上加@Scope注解来控制bean的作用域,如@Scope(“prototype”)
request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。
global-session:全局session作用域。
第三方bean
//声明第三方bean
@Bean
//将当前方法的返回值对象交给IOC容器管理, 成为IOC容器bean
//通过@Bean注解的name/value属性指定bean名称,如果没指定,默认是方法名
public SAXReader saxReader(){
return new SAXReader();
}
@Bean和@Component有什么区别?
@Component 注解用在类上,表明一个类会作为组件类,并告知Spring要为这个类创建bean,每个类对应一个 Bean。
@Component
public class Student {
private String name = "lkm";
public String getName() {
return name;
}
}
@Configuration
public class WebSocketConfig {
@Bean
public Student student(){
return new Student();
}
}
@Bean 注解用在方法上,表示这个方法会返回一个 Bean。@Bean 需要在配置类中使用,即类上需要加上@Configuration注解。
@Bean 注解可将第三方类装配到 Spring 容器中。
bean的起步依赖
传统的创建或者引用项目要导入大量的包和依赖,并且还要避免导入的资源的版本不兼容问题。所以springboot实现了起步依赖,导入一个打包好的资源,而这个资源里面会配置好需要的全部相关配置,从而避免上述问题。
自动装配可以简单理解为:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能
bean自动装配原理
@ComponentScan
@Import
@Import({Person.class})//导入普通类
@Import({Config.class})//导入配置类
//创建一个注解EnableXXXX 这个注解封装了需要的Import注解
@ComponentScan
/*把用到的资源导入到当前容器中*/
@Import({Person.class})//导入普通类
//@Import({Config.class})//导入配置类
//创建一个注解EnableXXXX 这个注解封装了需要的Import注解
public class App {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBean(Person.class));
context.close();
}
}
@Configuration
public class Config{
@Bean
public Test1 test1(){
return new Test1();
}
@Bean
public Test2 test2(){
return new Test2();
}
}
SpringBootApplication
实现了三个注解
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
@ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包
SpringBootConfiguration注解声明当前也是一个配置类,可以导入第三方Bean
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
EnableAutoConfiguration注解
这个注解实现了import注释
springboot->EnableAutoconfiguration->AutoConfigurationImportSelector.class->重写selectImports方法(加载两个文件,封装到ArrayList当中,最后转成String[])
@Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector类重写了了selectImports方法用以加载合法的Bean,这些类需要被加载到 IOC 容器中
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
为什么说合法的bean,因为bean并不总是同时加载的。可能会被推迟也有可能需要根据某些条件(Conditional注解)才能构建出对应的bean。
@Conditional注解
@ConditionalOnClass(name="类的全类名") 环境中存在这个类才会注册到IOC容器
@Bean
public Test1 test1(){
return new Test1();
}
@ConditionalOnMissingBean
//判断环境中是否有对应的bean,没有才注册到IOC容器 默认代表当前方法的bean,否则要指定参数
@Bean
public Test1 test1(){
return new Test1();
}
@ConditionalOnProperty(name="XXX",havingValue="XXX")
//判断配置文件中是否有对应的属性和值,有才注册bean到IOC
@Bean
public Test1 test1(){
return new Test1();
}
Bean 是线程安全的吗?
Spring 框架中的 Bean 是否线程安全,取决于其作用域和状态。
几乎所有场景的 Bean 作用域都是使用默认的 singleton ,重点关注 singleton 作用域即可。
- prototype 作用域下,每次获取都会创建一个新的 bean 实例,不存在资源竞争问题,所以不存在线程安全问题。
- singleton 作用域下,IoC 容器中只有唯一的 bean 实例,可能会存在资源竞争问题(取决于 Bean 是否有状态)。如果这个
bean 是有状态的话,那就存在线程安全问题(有状态 Bean 是指包含可变的成员变量的对象)。不过,大部分 Bean
实际都是无状态(没有定义可变的成员变量)的(比如 Dao、Service),这种情况下, Bean 是线程安全的。 - 对于有状态单例 Bean 的线程安全问题,常见的有两种解决办法:在 Bean 中尽量避免定义可变的成员变量。在类中定义一个
ThreadLocal 成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。
自定义starter
- 创建starter模块
- 创建autoconfigure模块,在starter中引入该模块
- 在autoconfigure模块中定义自动配置功能,实现XXXAutoConfigure配置类将需要自动配置的Bean都配置在这个文件之中
- 定义自动配置文件META-INF/spring,里面写XXXAutoConfigure配置类的全类名
- 当其他模块导入这个starter起步依赖的时候,就会扫描META-INF/spring 文件,扫描自动配置类