敞开生长之旅!这是我参与「日新方案 2 月更文挑战」的第 29 天,点击检查活动概况
前言
装备注解,指@Configuration,@ComponentScan,@Scope,@Lazy,@Conditional,@Import等注解,本篇文章将对这些注解的运用进行具体总结。
Springboot版别:2.4.1
Spring版别:5.3.2
正文
一. @Configuration
@Configuration注解润饰的类便是装备类,合作@Bean注解运用,能够用于向容器注册bean。如下是示例(一切类悉数放在同一包目录下)。
事务类如下所示。
public class MyService {}
由@Configuration注解润饰的事务类如下所示。
@Configuration
public class MyConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
}
}
运转测验程序,能够将MyService的bean注册到容器中,并且bean名称为myService,日志打印如下所示。
二. @ComponentScan
@ComponentScan注解用于扫描并注册bean到容器中。如下是示例(一切类悉数放在同一包目录下)。
事务类如下所示。
@Component
public class MyDao {}
@Component
public class MyService {}
装备类如下所示。
@Configuration
@ComponentScan(value = "com.learn.spring.annotation.componentscan",
useDefaultFilters = true)
public class MyConfig {}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
}
}
运转测验程序后,会把com.learn.spring.annotation.componentscan包目录下的MyService和MyDao的bean注册到容器中。上面的装备类的@ComponentScan注解的value指示扫描哪个包目录及其子目录下的类,同时useDefaultFilters字段为true(不显示指定也为true)表明运用默许过滤规矩,默许过滤规矩便是将指定途径下一切由@Controller,@Service,@Repository和@Component注解润饰的类的bean注册到容器中。
现在将useDefaultFilters装备为true,并编写一个自界说过滤器MyTypeFilter,如下所示。
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader 能够获取当时正在操作的类的信息
* @param metadataReaderFactory 能够获取上下文中一切类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 获取当时类的一切注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当时类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当时类的一切资源信息
Resource resource = metadataReader.getResource();
// 完成功能:将类名不包括"Dao"字符串的类注册到容器中
String className = classMetadata.getClassName();
return !className.contains("Dao");
}
}
MyConfig进行如下修正。
@Configuration
@ComponentScan(value = "com.learn.spring.annotation.componentscan",
includeFilters = {@Filter(type = FilterType.CUSTOM, value = {MyTypeFilter.class})},
useDefaultFilters = false)
public class MyConfig {}
再次运转测验程序,打印如下。
MyDao的bean没有被注册到容器中。
三. @Scope
@Scope注解用于设置bean效果域。示例如下(一切类悉数放在同一包目录下)。
事务类如下所示。
public class MyService {}
装备类如下所示。
@Configuration
public class MyConfig {
@Bean
@Scope(value = "prototype")
public MyService myService() {
return new MyService();
}
}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
MyService myService1 = applicationContext.getBean(MyService.class);
MyService myService2 = applicationContext.getBean(MyService.class);
System.out.println(myService1 == myService2);
}
}
运转测验程序,会打印false,这是因为将MyService的bean效果域设置为了prototype,表明原型形式(多例)。
Spring的bean的效果域如下所示。
效果域 | 阐明 |
---|---|
Singleton(单例) | 容器中仅有一个bean实例 |
Prototype(多例) | 每次从容器获取到的都是新的bean实例,容器将bean交给获取方后,就不再管理该bean的生命周期 |
Request(HTTP恳求) | 每次HTTP恳求都会产生一个新的bean实例 |
Session(会话) | 同一次会话共享同一个bean实例,不同会话bean实例不同 |
四. @Lazy
@Lazy注解用于设置bean推迟加载,即初始化容器的时候不会初始化推迟加载的bean,比及运用时才初始化bean。示例如下(一切类悉数放在同一包目录下)。
事务类如下所示。
public class MyService {
public MyService() {
System.out.println("MyService init.");
}
}
装备类如下所示。
@Configuration
public class MyConfig {
@Lazy
@Bean
public MyService myService() {
return new MyService();
}
}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
System.out.println("ApplicationContext initialization complete.");
MyService myService = applicationContext
.getBean(MyService.class);
}
}
运转测验程序,打印如下。
特别注意:延时加载只针对单例生效。
五. @Conditional
@Conditional表明满意一定条件时才会注册为容器中的bean。示例如下(一切类悉数放在同一包目录下)。
两个事务类如下所示。
public class MyWindowsService {}
public class MyLinuxService {}
两个条件类如下所示。
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
return osName != null && osName.contains("Windows");
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String osName = environment.getProperty("os.name");
return osName != null && osName.contains("Linux");
}
}
装备类如下所示。
@Configuration
public class MyConfig {
@Bean
@Conditional(WindowsCondition.class)
public MyWindowsService myWindowsService() {
return new MyWindowsService();
}
@Bean
@Conditional(LinuxCondition.class)
public MyLinuxService myLinuxService() {
return new MyLinuxService();
}
}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
}
}
运转测验程序,打印如下。
可见由于MyWindowsService满意条件,所以容器初始化时将MyWindowsService的bean注入到了容器中。
六. @Import
@Import注解能够向容器注册bean,有如下三种方法(一切类悉数放在同一包目录下)。
1. 直接注册bean
事务类如下所示。
public class MyService {}
装备类如下所示。
@Configuration
@Import({MyService.class})
public class MyConfig {}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
}
}
运转测验程序,打印如下。
可见直接将MyService注册为了容器中的bean。
2. 经过ImportSelector注册bean
新增事务类如下所示。
public class MyDao {}
自界说一个ImportSelector的完成类,如下所示。
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {
"com.learn.spring.annotation.importt.MyDao"
};
}
}
装备类修正如下所示。
@Configuration
@Import({MyService.class, MyImportSelector.class})
public class MyConfig {}
运转测验程序,打印成果如下。
可见经过MyImportSelector将MyDao注册到了容器中。
3. 经过ImportBeanDefinitionRegistrar注册bean
新增事务类如下所示。
public class MyController {}
自界说一个ImportBeanDefinitionRegistrar的完成类,如下所示。
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata 当时类的注解信息
* @param registry 能够注册BeanDefinition
*
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
BeanDefinition beanDefinition = new RootBeanDefinition(MyController.class);
registry.registerBeanDefinition("myController", beanDefinition);
}
}
装备类修正如下所示。
@Configuration
@Import({MyService.class, MyImportSelector.class,
MyImportBeanDefinitionRegistrar.class})
public class MyConfig {}
运转测验程序,打印如下。
可见将MyController注册到了容器中。
七. FactoryBean
FactoryBean并不是装备注解,可是经过FactoryBean能够协助创立杂乱的bean,例如像MyBatis整合Spring中,就大量运用到了FactoryBean。示例如下(一切类悉数放在同一包目录下)。
事务类如下所示。
public class MyService {}
界说一个FactoryBean的完成类,如下所示。
public class MyFactoryBean implements FactoryBean<MyService> {
@Override
public MyService getObject() {
return new MyService();
}
@Override
public Class<?> getObjectType() {
return MyService.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
装备类如下所示。
@Configuration
public class MyConfig {
@Bean
public MyFactoryBean myFactoryBean() {
return new MyFactoryBean();
}
}
测验类如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
Object myService = applicationContext.getBean("myFactoryBean");
System.out.println(myService);
Object myFactoryBean = applicationContext.getBean("&myFactoryBean");
System.out.println(myFactoryBean);
}
}
运转测验程序,打印如下。
在装备类中,将MyFactoryBean注册成了容器中的bean,可是经过姓名myFactoryBean无法将MyFactoryBean的bean从容器中获取出来,而获取出来的是MyService的bean。假如需求将MyFactoryBean的bean从容器中获取出来,需求在bean的姓名前面加&
符号。
经过FactoryBean向容器注册的bean实际上是保存在FactoryBeanRegistrySupport的factoryBeanObjectCache字段中,factoryBeanObjectCache字段是一个Map,key是FactoryBean自己的bean的姓名,value是FactoryBean创立的bean的实例,以此来完成单例。
总结
Spring的装备注解,更多的是为如何向容器注册bean服务,那么这里给出容器注册bean的总结。
- 装备类中运用@Bean注解向容器注册bean;
- 运用@ComponentScan注解扫描指定包途径下的类并注册,默许扫描@Controller,@Service,@Component和@Repository注解润饰的类;
- 运用@Import注解,有如下三种方法
- 运用@Import(类.class)直接向容器注册bean;
- 经过界说ImportSelector接口的完成类;
- 经过界说ImportBeanDefinitionRegistrar接口的完成类.
- 运用BeanFactory封装需求注册的bean的类。
敞开生长之旅!这是我参与「日新方案 2 月更文挑战」的第 29 天,点击检查活动概况