组件扫描策略注解 @ComponentScan

事实上,我们可以在定义MainConfig 方法中添加 @ComponentScan 注解来定义扫描的信息,比如,排除的Bean等等

  1. @Configuration
  2. // 配置组件扫描信息的包名
  3. @ComponentScan(value = "com.zhoutao123.spring",
  4. // 定义排除的Bean的过滤器,排除Controller注解的Bean以及BookService类型的Bean
  5. excludeFilters = {
  6. @Filter(classes = Controller.class, type = FilterType.ANNOTATION),
  7. @Filter(classes = BookService.class, type = FilterType.ASSIGNABLE_TYPE),
  8. }, includeFilters = {
  9. // 定义包含过滤器,包含BookService类型的Bean,注意需要设置使用默认过滤器为false,否则不生效
  10. @Filter(classes = BookService.class, type = FilterType.ASSIGNABLE_TYPE)
  11. }, useDefaultFilters = false)
  12. public class MainConfig {
  13. //....
  14. }
  • 过滤类型的枚举包含以下方式
  1. public enum FilterType {
  2. ANNOTATION, // 按照注解的方式过滤
  3. ASSIGNABLE_TYPE, // 按照类型的方式过滤
  4. ASPECTJ, // 按照ASPECTJ方法过滤
  5. REGEX, // 按照正则方法过滤Beand的类型
  6. CUSTOM // 定义过滤,实现 org.springframework.core.type.filter.TypeFilter 接口
  7. }

比如下面可以自定义一个过滤器

  1. public class CustomTypeFilter implements TypeFilter {
  2. /**
  3. * @param metadataReader 读取当前正在扫描类的信息
  4. * @param metadataReaderFactory 读取其他类的信息
  5. * @return 返回True代表匹配过滤
  6. */
  7. @Override
  8. public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
  9. throws IOException {
  10. // 当前类的原信息
  11. ClassMetadata classMetadata = metadataReader.getClassMetadata();
  12. // 类的注解的原信息
  13. AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
  14. // 获取其他类型信息
  15. metadataReaderFactory.getMetadataReader("TestClassName");
  16. return false;
  17. }
  18. }
  19. // 可以在@ComponentmScan中扫描
  20. @ComponentScan(value = "com.zhoutao123.spring", excludeFilters = {
  21. @Filter(classes = Controller.class, type = FilterType.ANNOTATION),
  22. @Filter(classes = CustomTypeFilter.class, type = FilterType.CUSTOM)
  23. })

作用域 @Scope

默认情况下,从上下文Context获取的Bean都是单例的,可以用下面的代码验证

  1. Student bean = context.getBean(Student.class);
  2. Student bean2 = context.getBean(Student.class);
  3. assert bean == bean2;

一些情况下,我们希望每次获取都是一个新的Bean,这时候可以使用 @Scope 注解的方式来实现, @Scope 的value = ‘singleton’ 标识默认单例模式,value=’prototype’ 标识Bean 是多实例模式

  • 单实例对象:在IOC容器创建的时候创建Bean
  • 多实例对象: 在获取Bean的时候,在生成对象
  1. @Scope(value="xxxx") // value取值可以是 singleton,prototype,request 以及 session
  2. @Bean("测试Bean")
  3. public Student student2() {
  4. Student student = new Student();
  5. student.setAge(18);
  6. student.setLessonList(Arrays.asList("Math", "English"));
  7. student.setName("燕归来兮");
  8. return student;
  9. }
  10. // 断言测试不相等
  11. Student bean = context.getBean(Student.class);
  12. Student bean2 = context.getBean(Student.class);
  13. assert bean != bean2;

懒加载 @Lazy

Bean 的懒加载模式,仅针对于单实例模式,Bean将程序第一次获取从上下文中的时候创建Bean对象,通常在有Bean注解的方法添加注解 @Lazy 即可。

  1. @Lazy
  2. @Bean("测试Bean")
  3. public Student student() {
  4. Student student = new Student();
  5. student.setAge(18);
  6. student.setLessonList(Arrays.asList("Math", "English"));
  7. student.setName("燕归来兮");
  8. return student;
  9. }

条件注入 @Conditional

满足条件,则向容器中注入,不满足则不注入,从Spring4.0 开始实现的方式。自定义Condition需要实现 Condition 接口

  1. public class CustomConditional implements Condition {
  2. /**
  3. * @param context 上下文
  4. * @param metadata 注释的的原信息
  5. */
  6. @Override
  7. public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  8. // 获取BeanFactory
  9. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  10. // 获取类加载器
  11. ClassLoader classLoader = context.getClassLoader();
  12. // 获取环境信息
  13. Environment environment = context.getEnvironment();
  14. String osName = environment.getProperty("os.name", "");
  15. if (osName.contains("Windows")) {
  16. return false;
  17. }
  18. return osName.contains("Linux");
  19. }

在Bean上添加注解 @Conditional 注解即可实现动态注入, 标注在类上,具有影响类的全部方式的效果

  1. @Conditional(value = CustomConditional.class)
  2. @Bean("测试Bean")
  3. public Student student() {
  4. return new Student();
  5. }

手动注入 @Import

使用 @Import

使用 @Bean 注解并不是唯一注入 Bean 的方式,在一些简单的的对象,通常可以直接使用@Import来注解使用 无参构造注入器 注入对象,比如下面两种方式结果是一样

  1. @Bean
  2. public Color(){
  3. return new Color();
  4. }
  5. @Import({Color.class})
  6. public MainCoinfig{}

使用 @ImportSelector

@Import 注解可以填写需要进行无参构造注入的Bean的class对象,同时也可以自定义一个 ImportSelector 对象或者 ImportBeanDefinitionRegistrar 来动态的注入所需要的Bean,定义一个CustomerImportSelector需要继承 org.springframework.context.annotation.ImportSelector 然后重写 selectImports 返回一个字符串数组,该数组包含需要Import的全类名,该对象可以返回空数组,但不能返回null,否则会报错 NullPointException

自定义ImportSelector 的方式如下

  1. public class CustomerImportSelector implements ImportSelector {
  2. @Override
  3. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  4. // 演示获取到全类名
  5. String clazzName = Size.class.getName();
  6. return new String[]{clazzName};
  7. }
  8. }
  9. // 在启动的配置类上添加@Import注解,@Import({Color.class, CustomerImportSelector.class})

使用 @ImportBeanDefinitionRegistrar

定义一个自定义的ImportBeanDefinitionRegistrar 需要继承 org.springframework.context.annotation.ImportBeanDefinitionRegistrar 并重写 registerBeanDefinitions 方法,示例代码展示的是如果存在名称为apple的Bean,则注册一个BeanName=size的新的Bean,示例代码如下:

  1. public class CustomerImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
  4. BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
  5. boolean hasAppleComponent = registry.containsBeanDefinition("apple");
  6. if (hasAppleComponent) {
  7. RootBeanDefinition definition = new RootBeanDefinition(Size.class);
  8. registry.registerBeanDefinition("size",definition);
  9. }
  10. }
  11. }
  12. // 同样也需要在 @Import({ImportBeanDefinitionRegistrar.class})

使用 FactoryBean

同样的使用Spring提供的FactoryBean也可以手动注入Bean对象, 创建自定义的SpringFactoryBean 需要继承 org.springframework.beans.factory.FactoryBean

  1. public class CustomerFactoryBean implements FactoryBean<Size> {
  2. @Override
  3. public Size getObject() throws Exception {
  4. return new Size();
  5. }
  6. @Override
  7. public Class<?> getObjectType() {
  8. return Size.class;
  9. }
  10. // 是否是单例模式
  11. @Override
  12. public boolean isSingleton() {
  13. return false;
  14. }
  15. }
  16. // 注入 FactoryBean 对象
  17. @Bean
  18. public CustomerFactoryBean factoryBean(){
  19. return new CustomerFactoryBean();
  20. }

这里需要注意,虽然注入的 CustomerFactoryBean,但是从IOC 容器中获取到 BeanName = ‘factoryBean’ 其类型不是CustomerFactoryBean,而是这个FactoryBean的构造Bean,如果需要获取其对应的FactoryBean对象实例,则需要在BeanNam前面加上 ‘&’ 符号

  1. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
  2. Object bean1 = context.getBean("factoryBean");
  3. assert bean1 instanceof Size;
  4. bean1 = context.getBean("&factoryBean");
  5. assert bean1 instanceof CustomerFactoryBean;