Spring Boot 的出现极大的简化了我们的开发, 让我们无需再写繁杂的配置文件, 其正是利用了注解的便捷性, 而 Spring Boot 又依赖于 Spring, 因此深入学习 Spring 的注解是十分必要的.
组件注册相关注解
@Configuration
写在类上, 声明此类是一个配置类, 替代 xml 文件
@Bean
作用:
给 IoC 容器中注册一个 Bean, 一般添加在方法上, 组件类型为方法的返回值, id 默认为方法名称
常用属性:
value / name: 指定组件的名称, 如果不指定, 默认是方法名
initMethod: 指定初始化方法
destroyMethod: 指定销毁方法
@ComponentScan
作用:
根据自定义的规则, 自动扫描 IoC 容器中所有组件, 在 jdk1.8 之后可以在一个类上定义多个 @ComponentScan.
还有一个 @ComponentScans 注解, 也可以在里面定义多个 @ComponentScan
常用属性:
value / basePackages: 指定要扫描的包名
@Filter: 用于指定过滤的规则
type: 过滤类型
FilterType.ANNOTATION: 按照注解的方式
FilterType.ASSIGNABLE_TYPE: 按照给定的类型
FilterType.ASPECTJ: 使用 ASPECTJ 表达式
FilterType.REGEX: 使用正则表达式
FilterType.CUSTOM: 自定义类型
value / classes: 过滤值
pattern: 过滤规则, 根据不同的过滤类型配置不同的规则
useDefaultFilters: 是否使用默认过滤规则, 默认是 true
includeFilters: 指定扫描的时候只包含什么组件, 需要配置 useDefaultFilters 属性为 false
excludeFilters: 指定扫描的时候按照什么规则排除哪些组件
lazyInit: 懒加载
如何使用 FilterType.CUSTOM 自定义过滤规则?
- public class MyTypeFilter implements TypeFilter {
- /**
- * 匹配方法, 确定此过滤器是否与给定元数据描述的类匹配
- * @param metadataReader 读取到的当前正在扫描的类的信息
- * @param metadataReaderFactory 可以获取到其他任何类的信息
- * @return true: 匹配, false: 不匹配
- * @throws IOException
- */
- @Override
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- // 获取当前类的注解信息
- AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
- // 获取当前正在扫描的类的信息
- ClassMetadata classMetadata = metadataReader.getClassMetadata();
- // 获取当前类资源 (类的路劲)
- Resource resource = metadataReader.getResource();
- // 自定义匹配规则
- String className = classMetadata.getClassName();
- if(className.contains("Controller")){
- return true;
- }
- return false;
- }
- }
- @Scope
作用:
调整作用域
常用参数:
value / scopeName
singleton: 单实例, IoC 容器启动的时候就会调用方法, 创建 bean 对象, 以后每次获取都是直接从 IoC 容器中取 (map.get()).
prototype: 多实例, IoC 容器启动的时候不会去调用, 当从 IoC 容器中获取 bean 对象的时候才会创建.
request: 同一次请求创建一个实例.
session: 同一个 session 范围创建一个实例.
global session: 全局 session 范围创建一个实例, 一般用于集群.
@Lazy
懒加载, 一般用于单例模式, 容器启动的时候不会创建 bean, 第一次调用的时候才创建
@Conditional
作用:
按照一定条件进行判断, 满足条件才注册 bean, 可以放在方法或类上, 此注解在 Spring Boot 底层大量使用
常用参数:
value: 传入一个继承了 Condition 接口的类 (可传入多个), 类中定义自己需要的条件
@Import
作用:
给 IoC 容器中导入指定的组件
常用参数:
value: 传入指定类, id 默认是全类名. 可传入自定义类, ImportSelector 和 ImportBeanDefinitionRegistrar
用法:
在配置类上添加如下形式的注解即可
- @Import({
- Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class
- })
- ImportSelector
传入实现了 ImportSelector 接口的类, 返回一个全类名数组, 好处就是可以自定义需要导入的组件
- public class MyImportSelector implements ImportSelector {
- /**
- * 自定义导入的组件
- * @param importingClassMetadata 当前标注了 @Import 注解的类的所有注解信息
- * @return 返回全类名
- */
- @Override
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- return new String[]{"com.spring.color.Blue", "com.spring.color.Yellow"};
- }
- }
打断点 debug 一下, 可以看到参数的信息, 的确是当前标注 @Import 的类上的注解信息
ImportBeanDefinitionRegistrar
手动注册 bean 到容器中, 调用 registerBeanDefinition() 方法
- public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
- /**
- *
- * @param importingClassMetadata 当前标注了 @Import 注解的类的所有注解信息
- * @param registry 容器中已注册组件的信息
- */
- @Override
- public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
- int count = registry.getBeanDefinitionCount();
- if(count> 10){
- BeanDefinition beanDefinition = new RootBeanDefinition(Ten.class);
- registry.registerBeanDefinition("ten", beanDefinition);
- }
- }
- }
使用 FactoryBean 注册组件
自定义一个类, 实现 FactoryBean 接口
- public class FoodFactoryBean implements FactoryBean<Food> {
- /**
- * 获取实例对象
- * @return
- * @throws Exception
- */
- @Override
- public Food getObject() throws Exception {
- return new Food();
- }
- /**
- * 获取实例类型
- * @return
- */
- @Override
- public Class<?> getObjectType() {
- return Food.class;
- }
- /**
- * 是否单例, true: 单例 false: 多例
- * @return
- */
- @Override
- public boolean isSingleton() {
- return true;
- }
- }
使用 @Bean 注册到容器
- @Bean
- public FoodFactoryBean foodFactoryBean(){
- return new FoodFactoryBean();
- }
测试一下
- @Test
- public void test2(){
- Object bean = applicationContext.getBean("foodFactoryBean");
- System.out.println("foodFactoryBean 的类型:" + bean.getClass());
- }
运行结果如下, 发现类型竟然不是 FoodFactoryBean , 使用 @Bean 注册的组件类型不是方法的返回值吗? 实际上, FoodFactoryBean 注册的时候调用的了 getObject() 方法, 所以注册的是 Food 类
foodFactoryBean 的类型: class com.spring.bean.Food
那么如果想要获得 FoodFactoryBean 类怎么办呢?
看一下 BeanFactory 的源码, 定义了一个成员变量 FACTORY_BEAN_PREFIX
这个变量用于取消引用 FactoryBean 实例, 并将其与由 FactoryBean 创建的 bean 区别开.
例如, 如果名为 test 的 bean 是 FactoryBean, 则获取 &test 将返回工厂, 而不是工厂返回的实例.
所以在 getBean 的时候, 在 id 前加上 & 即可
- @Test
- public void test2(){
- Object bean = applicationContext.getBean("&foodFactoryBean");
- System.out.println("foodFactoryBean 的类型:" + bean.getClass());
- }
总结
注册组件的方式:
包扫描 (@ComponentScan) + 组件注解 (@Component / @Controller / @Service / @Repository)
@Bean [导入第三方包里的组件]
@Import [快速给容器中导入组件]
普通类: 直接注册, id 默认是全类名
ImportSelector: 返回需要注册组件的全类名数组
ImportBeanDefinitionRegistrar: 手动注册组件到容器中
使用 spring 提供的 FactoryBean(工厂 Bean)
默认获取的是 FactoryBean 调用 getObject 方法返回的对象
可以在获取 Bean 的时候在 id 前面加上 & 符号, 获取 FactoryBean 本身
来源: https://www.cnblogs.com/songjilong/p/12513501.html