Spring Boot的出现极大的简化了咱们的开发,让咱们无需再写繁杂的配置文件,其正是利用了注解的便捷性,而Spring Boot又依赖于Spring,所以深刻学习Spring的注解是十分必要的。java
组件注册相关注解
@Configuration
写在类上,声明此类是一个配置类,替代xml文件正则表达式
@Bean
做用:spring
给 IOC 容器中注册一个Bean,通常添加在方法上,组件类型为方法的返回值,id默认为方法名称数组
经常使用属性:session
- value / name:指定组件的名称,若是不指定,默认是方法名
- initMethod:指定初始化方法
- destroyMethod:指定销毁方法
@ComponentScan
做用:app
根据自定义的规则,自动扫描 IOC 容器中全部组件,在 jdk1.8 以后能够在一个类上定义多个@ComponentScan
。ide
还有一个@ComponentScans
注解,也能够在里面定义多个@ComponentScan
学习
经常使用属性:测试
- value / basePackages:指定要扫描的包名
- @Filter:用于指定过滤的规则
- type:过滤类型
- FilterType.ANNOTATION:按照注解的方式
- FilterType.ASSIGNABLE_TYPE:按照给定的类型
- FilterType.ASPECTJ:使用ASPECTJ表达式
- FilterType.REGEX:使用正则表达式
- FilterType.CUSTOM:自定义类型
- value / classes:过滤值
- pattern:过滤规则,根据不一样的过滤类型配置不一样的规则
- type:过滤类型
- useDefaultFilters:是否使用默认过滤规则, 默认是 true
- includeFilters:指定扫描的时候只包含什么组件,须要配置
useDefaultFilters
属性为false - excludeFilters:指定扫描的时候按照什么规则排除哪些组件
- lazyInit:懒加载
如何使用FilterType.CUSTOM
自定义过滤规则?spa
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 自己