zoukankan      html  css  js  c++  java
  • Spring注解-组件注册(一)

    Bean

    • 我们之前使用xml时, 需要使用<bean>标签
    1. 配置
          <bean class="test.bean.Person" id="person">
              <property name="age" value="18"></property>
              <property name="name" value="zhangsan"></property>
          </bean>
    2. 使用
              ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("beans.xml");
      
              Person bean = cac.getBean("person", Person.class);
              System.out.println(bean);
      
              cac.close();
    • 使用注解配置
    1. 需要我们手写配置类, 就相当于以前的配置文件.
      • 配置类上加入@Configuration注解, 告诉Spring这是一个配置类.
      • 在里面使用@Bean注解: 给容器注册一个额Bean, 类型为返回值类型, id默认是方法名.
        • 其中, value属性可以修改id
          @Configuration
          public class MainConfig {
          
              // 给容器中注册一个Bean; 类型为返回值的类型, id默认是方法名
              @Bean(value = "person01")
              public Person person() {
          
                  return new Person("lisi", 20);
              }
          }
    2. 使用
      • 使用AnnotationConfigApplicationContext, 包含配置类类型.
        public class MainTest {
        
            public static void main(String[] args) {
        
                AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
                Person bean2 = ac.getBean(Person.class);
                Person bean3 = ac.getBean(Person.class);
                System.out.println(bean2 == bean3);
        
                ac.close();
            }
        }
    3. @Scope注解
      • @Bean默认是单实例的, 我们可以使用@Scope注解来调整作用域
      • scope
        • prototype: 多实例 -> IOC容器启动不会调用方法创建对象, 而是每次获取的时候调用方法创建对象.
        • singleton: 单实例(默认值) -> IOC容器启动就会调用方法创建对象放到容器中. 以后获取直接从从容器中拿.
        • request: 同一次请求创建一个实例
        • sesson: 同一个session创建一个实例(后两个都web环境)
    4. 懒加载 -> 只针对于单实例
      • 单实例原本是在容器启动的时候就创建对象的.
      • 懒加载: 容器启动后, 先不创建对象, 第一次使用(获取)Bean的时候创建对象, 并初始化.
      • @Lazy注解
        @Configuration 
        @ComponentScan(value = "test") 
        public class MainConfig {
        
            // 给容器中注册一个Bean; 类型为返回值的类型, id默认是方法名
            @Bean(value = "person01")
            @Lazy
            @Scope("singleton")
            public Person person() {
        
                return new Person("lisi", 20);
            }
        }

    包扫描

    • xml方式
    1. 使用<context:component-scan>
          <!-- 包扫描: 只要标注了@Component, @Service, @Repository, @Controller, 就会被自动扫描加进容器 -->
          <context:component-scan base-package="test"></context:component-scan>
    • 注解方式
    1. 使用@ComponentScan注解, 这是一个可重复注解, 也就是我们可以在一个类上写多次实现多种配置规则
      • value属性填写要扫描的包.
      • excludeFilters = Filter[]: 指定扫描的时候按照什么规则排除哪些组件
      • includeFilters = Filter[]: 指定扫描的时候包含哪些组件
      • useDefaultFilters属性: 是否使用默认配置规则, 默认值为true, 使用includeFilters时需配置成false.
        @ComponentScan(value = "test", excludeFilters = {
            @Filter(type=FilterType.ANNOTATION, classes = {Controller.class, Service.class})
        })
        @ComponentScan(value = "test", includeFilters = {
                @Filter(type=FilterType.ANNOTATION, classes = {Repository.class})
        })
    2. 测试
      • AnnotationConfigApplicationContext有一个getBeanDefinitionNames()获取加载进Spring的组件.
    3. @Filter注解
      • FilterType.ANNOTATION: 按照注解
        • classes属性中填组件的类型.
      • FilterType.ASSIGNABLE_TYPE: 按照给定的类型
        • classes中填写具体类的类型
          @Filter(type = FilterType.ASSIGNABLE_TYPE, classes= {BookService.class})
      • FilterType.ASPECTJ: 使用ASPECTJ表达式
      • FilterType.REGEX: 使用正则表达式
      • FilterType.CUSTOM: 使用自定义规则
        • 自定义TypeFilter接口的实现类
          public class MyTypeFilter implements TypeFilter {
          
              /**
               * metadataReader: 读取到当前正在扫描的类的信息
               * metadataReaderFactory: 可以获取到其他任何类信息的.
               * 
               * 返回false是一个也不匹配.
               */
              @Override
              public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
                      throws IOException {
                  
                  //获取当前类注解的信息
                  AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
                  //获取当前正在扫描的类的类信息
                  ClassMetadata classMetadata = metadataReader.getClassMetadata();
                  String className = classMetadata.getClassName();
                  System.out.println(className);
                  //获取当前类资源(类的路径等信息)
                  Resource resource = metadataReader.getResource();
                  
                  //类名中有"er"就会被扫描进容器.
                  if(className.contains("er")) {
                      return true;
                  }
                  
                  return false;
              }
          
          }
        • 使用
          @Filter(type=FilterType.CUSTOM, classes = {MyTypeFilter.class})

    @Conditional注解 

    1. 作用: 按照一定条件进行判断, 满足条件才给容器中注册bean.
    2. @Conditional(): 中填写Condition数组.
      • Condition是个接口, 所以我们要写实现类放入@Conditional中.
      • MaleCondition
        public class MaleCondition implements Condition {
        
            /**
             * ConditionContext: 判断条件能使用的上下文(环境)
             * AnnotatedTypeMetadata: 注释信息
             * 返回true则允许注册.
             */
            @Override
            public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
                
                //是否为男
                //1.能获取到ioc使用的beanfactory
                ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
                
                //2.获取类加载器
                ClassLoader classLoader = context.getClassLoader();
                
                //3.获取当前环境信息
                Environment environment = context.getEnvironment();
                
                //获取到bean定义的注册类
                BeanDefinitionRegistry registry = context.getRegistry();
                
                String property = environment.getProperty("os.name");
                //当前系统是否为Windows
                if(property.contains("Windows")) {
                    return true;
                }
                return false;
            }
        }
      • FemaleCondition
        public class FemaleCondition implements Condition {
        
            @Override
            public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        
                Environment environment = context.getEnvironment();
                
                String property = environment.getProperty("os.name");
                if(property.contains("Linux")) {
                    return true;
                }
                return false;
            }
        }
      • 使用
            @Conditional({ MaleCondition.class })
            @Bean("ming")
            public Person person001() {
        
                return new Person("xiaoming", 16);
            }
        
            @Conditional({ FemaleCondition.class })
            @Bean("hong")
            public Person person002() {
                return new Person("xiaohong", 15);
            }
    3. 不仅仅能放在方法上, 也能放在类上
      • 只有满足了当前条件, 这个类中配置的所有bean注册才能生效.

     @Import注解

    • 给容器中注册组件
    1. 包扫描+组件注解标注(@Controller等...)
      • 但有局限, 只能注册我们自己写的类.
    2. @Bean
      • 导入的第三方包里面的组件
    3. @Import
      • 快速给容器中导入一个组件
      • @Import(要导入到容器中的组件), 容器中就会自动注册这个组件, id默认是全类名
        @Configuration
        @ComponentScan(value = "test")
        @Import({Color.class, Red.class}) //导入组件, id默认是组件的全类名
        public class MainConfig {
        
            //...
        }
    4. ImportSelector
      • 自定义逻辑, 返回要导入的组件, 这是一个接口, 我们要在实现类中操作.
        //自定义逻辑, 返回需要导入的组件
        public class MyImportSelector implements ImportSelector {
        
            /**
             * 返回值就是导入到容器中的组件的全类名 
             * AnnotationMetadata: 当前标注@Import的注解类的所有注解信息.
             */
            @Override
            public String[] selectImports(AnnotationMetadata importingClassMetadata) {
                
                // 方法不能返回null值, 可以返回一个空数组
                return new String[] {"test.bean.Blue", "test.bean.Yellow"};
            }
        
        }
      • 当然, 我们需要把实现类写入@Import中.
        @Configuration
        @ComponentScan(value = "test")
        @Import({Color.class, Red.class, MyImportSelector.class}) //导入组件, id默认是组件的全类名
        public class MainConfig {
        
            //...
        }
    5. ImportBeanDefinitionRegistrar
      • 手工注册bean到容器中, 需要实现ImportBeanDefinitionRegistrar接口.
        public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        
            /**
             * BeanDefinitionRegistry: BeanDefinition注册类, 把所有需要添加到容器中的bean, 
             *   调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
             */
            @Override
            public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                
                //是否包含"red"这个组件
                boolean definition = registry.containsBeanDefinition("red");
                boolean definition2 = registry.containsBeanDefinition("blue");
                if(definition && definition2) {
                    //指定Bean信息: (Bean的类型, Scope等)
                    RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
                    //指定bean名
                    registry.registerBeanDefinition("rainBow", beanDefinition);
                }
            }
        
        }
      • 当然, 我们需要把实现类写入@Import中.
        @Configuration
        @ComponentScan(value = "test")
        @Import({MyImportSelector.class, MyImportBeanDefinitionRegistrar.class}) //导入组件, id默认是组件的全类名
        public class MainConfig {
        
            //...
        }

    FactoryBean(工厂Bean)

    1. 实现FactoryBean接口, 让工厂帮我们创建对象.
      //创建一个Spring定义的FactoryBean
      public class ColorFactoryBean implements FactoryBean<Color> {
      
          // 返回一个Color对象, 这个对象会添加到容器中
          @Override
          public Color getObject() throws Exception {
              // TODO Auto-generated method stub
              return new Color();
          }
      
          @Override
          public Class<?> getObjectType() {
              // TODO Auto-generated method stub
              return Color.class;
          }
      
          // 是否单例
          @Override
          public boolean isSingleton() {
      
              return false;
          }
      
      }
    2. 装配
          @Bean
          public ColorFactoryBean colorFactoryBean() {
              return new ColorFactoryBean();
          }
    3. 测试
      • 虽说装配的是ColorFactoryBean, 但我们实际获取的却是其泛型中的实体对象.
            @Test
            public void test() {
                AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
                Object bean = ac.getBean("colorFactoryBean");
                System.out.println(bean);
                Object bean2 = ac.getBean("&colorFactoryBean");
                System.out.println(bean2);
            }
      • 但我们在id前加上&符, 就能获取工厂Bean本身.
  • 相关阅读:
    java mybatis 新增记录 与 insertSelective 保存问题
    01 开发环境搭建
    2021年:系列文章总结
    在win10上安装MTK驱动(附驱动下载链接)
    Gerrit 大量代码提交流程优化
    mysqldump的使用
    配置 Gerrit 迁移
    解决:编译安卓源码时 JDK 报错 error='Not enough space' (errno=12)
    修改Git Commit提交记录的用户名Name和邮箱Email
    Android 各层架构
  • 原文地址:https://www.cnblogs.com/binwenhome/p/13064849.html
Copyright © 2011-2022 走看看