• spring注解之组件注册


    1.组件注册是什么?

    spring组件有十几种,但核心的只有三个:Context、Core和Bean。

    那么这三个组件的关系是什么呢?

    -Context:容器

    -Core :关系

    -bean:实物

    -一句话:在springIOC运行容器(Context)中,通过Core建立维护各个bean之间的关系。

    我们所说的组件注册其实就是:把bean对象交给ioc容器管理

    2.组件注册几种方式:

    2.1@Bean给容器注册组件

    @Configuration
    public class MyConfig {
        @Scope("prototype")
        @Lazy
        @Bean(value = "pp",name = "pp")
        public Person person(){
            return new Person("jiajia",26);
        }

    代码中用到的注解说明:

    @Configuration:配置类 相当于配置文件,告诉spring这是一个配置类

    @Scope:调整作用域 ,可取值【singleton,prototype,request,session】

      -singleton:单实例(默认值):ioc容器启动会调用方法创建对象放到ioc容器中, 以后每次获取就是直接从容器中拿;

      -prototype:多实例,ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象;

      -request:同一次请求创建一个实例

      -session:同一个session创建一个实例

    @Lazy :是只针对单实例。

      单实例Bean:默认在容器启动的时候创建对象;

      懒加载:容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化;

    @Bean:如果不设置value的话默认是采用方法名 person为组件名

    2.2@ComponentScan 组件扫描注解

     2.2.1包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)

    @ComponentScan(value = "com.athome", excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Service.class,Controller.class}),
        @ComponentScan.Filter(type = FilterType.REGEX,pattern = "com\.athome\.core\.message\..*")
    })
    @Configuration
    public class MyConfig {
        @Bean
        public Person person(){
            return new Person("niHao",26);
        }
    }

    分析@ComponentScan注解中内容:

    @Retention(RetentionPolicy.RUNTIME) //运行时期可获得该注解,反射
    @Target(ElementType.TYPE) //该注解可以表示在类中任何元素
    @Documented // 可制作文档
    @Repeatable(ComponentScans.class)// 可重复使用该注解
    public @interface ComponentScan {
        
        @AliasFor("basePackages")
        String[] value() default {}; //指定要扫描的包
    
        @AliasFor("value")
        String[] basePackages() default {};//表示扫描的路径是一个String[]
    
        Class<?>[] basePackageClasses() default {};
        
        Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
    
        Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
    
        ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
        
        String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
    
        boolean useDefaultFilters() default true;//默认的装配规则,意思是扫描所有的
        
        Filter[] includeFilters() default {}; //指定扫描那些组件,还需要禁用默认的装配规则(useDefaultFilters="false")
        
        Filter[] excludeFilters() default {}; //指定不扫描哪些组件
        
        boolean lazyInit() default false;
    
        @Retention(RetentionPolicy.RUNTIME)
        @Target({})
        @interface Filter {// 内注解
    
            FilterType type() default FilterType.ANNOTATION;//过滤类型
        
            @AliasFor("classes")
            Class<?>[] value() default {};
        
            @AliasFor("value")
            Class<?>[] classes() default {}; //class文件
    
            String[] pattern() default {};
        }
    }

    该注解几个重要的属性:

    • basePackages/value 表示扫描的路径是一个String[]
    • includeFilters 包含过滤器Filter[] ,指定扫描符合条件的组件
    • excludeFilters 排除过滤器Filter[] ,不扫描不符合条件的组件
    • useDefaultFilters 默认的扫描策略默认为true,如果想要用自定义的策略 该值要设为false和includeFilters一起使用   

    还有个内注解@Filter

    • FilterType.ANNOTATION:按照注解
    • FilterType.ASSIGNABLE_TYPE:按照给定的类型;
    • FilterType.ASPECTJ:使用ASPECTJ表达式
    • FilterType.REGEX:使用正则指定
    • FilterType.CUSTOM:使用自定义规则

    2.2.2自定义规则--FilterType.CUSTOM的使用案例:

    //自定义过滤组件
    public class MyFilterType  implements TypeFilter {
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            //获取当前类注解信息
            AnnotationMetadata am = metadataReader.getAnnotationMetadata();
            //获取当前正在扫描的类的类信息
            ClassMetadata mr = metadataReader.getClassMetadata();
            //获取当前类的资源信息(类的路径)
            Resource resource = metadataReader.getResource();
            String className  = mr.getClassName();
            System.out.println("--->"+className);
    
            if(className.contains("er")){
                return  true;
            }
    
            return false;
        }
    }

    @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyFilterType.class}
     
     


    2.2.3 @Conditional({condition}) 按条件注册bean

     在spring4中引入,用到带有@Bean注解的方法上,如果给定的条件计算结果为true,则会创建这个bean,否则这个bean就会被忽略。

    案例:

        /**
         * @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean 
         * 如果系统是windows,给容器中注册("bill")
         * 如果是linux系统,给容器中注册("linus")
         */
        @Conditional(WindowsCondition.class)
        @Bean("bill")
        public Person person01(){
            return new Person("Bill Gates",62);
        }
        
        @Conditional(LinuxCondition.class)
        @Bean("linus")
        public Person person02(){
            return new Person("linus", 48);
        }

    判断Windows条件:

    public class WindowsConditional implements Condition{
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            //获取到ioc使用的beanFactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            //获取类的加载器
            ClassLoader classLoader = context.getClassLoader();
            //获取当前环境信息
            Environment environment = context.getEnvironment();
            //获取到bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
            String property = environment.getProperty("os.name");
            if(property.contains("windows")){
                return true;
            }
            return false;
        }
    }

    java中的System的getProperty方法的取值key说明:
    os.name 操作系统的名称 
    os.arch 操作系统的架构 
    os.version 操作系统的版本

    判断Linux条件: -Dos.name=linux

    public class LinuxConditional implements Condition {
        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;
        }
    }

    测试:

       @Test
    public void conditionalTest(){
      AnnotationConfigApplicationContext ac
    = new AnnotationConfigApplicationContext(MyConfig02.class); String[] names = ac.getBeanNamesForType(Person.class); ConfigurableEnvironment environment = ac.getEnvironment(); String osName = environment.getProperty("os.name"); System.out.println(osName); }

    2.2.4 @ComponentScans,装配多个@ComponentScan装配规则

     在Java8以前不允许在一个类上定义两个相同的注解,可以使用@ComponentScans,装配多个@ComponentScan装配规则

    @ComponentScans(
            value = {
                    @ComponentScan(value="com.atguigu",includeFilters = {
                           @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
                            @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
                            @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
                    },useDefaultFilters = false)    
            }
    

    2.2.5 练习报错:excludeFiters遇到问题 

      在使用excludeFiters过滤组件时,比如我们制定排除@Controller注解,但是spring容器还是有该组件,这种情况下原因是:@ComponentScans 多个配置文件使用并且扫描包也有重复

    2.3@import注解导入组件

    @Import主要是用来引用第三方组件,当然也可以导入自定义的组件。这个注解分别可以导入以下注解

    • 导入普通组件:
    public class Color{}
    public class Red{}
    上面两个简单的类
    ---------------------------
    @Configuration @Import({Color.
    class,Red.class}) //@Import导入组件,id默认是组件的全类名 public class MainConfig2 { }
    • 导入ImportSelect的实现类

    ImportSelector 接口:返回需要导入的组件的全类名数组;

    public interface ImportSelector {
        /**
         * 返回值,就是到导入到容器中的组件全类名
         * AnnotationMetadata:当前标注@Import注解的类的所有注解信息
         */
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    }

     自定义实现类:

    //自定义逻辑返回需要导入的组件
    public class MyImportSelector implements ImportSelector {
        //返回值,就是到导入到容器中的组件全类名
        //AnnotationMetadata:当前标注@Import注解的类的所有注解信息
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // TODO Auto-generated method stub
            //importingClassMetadata
            //方法不要返回null值
            return new String[]{"com.athome.bean.Blue","com.athome.bean.Yellow"};
        }
    }

    使用@Import来导入

    @Configuration
    @Import({MyImportSelector .class})
    public class MyConfing {
    }
    • 入ImportBeandeinitionRegistrart

      ImportBeandeinitionRegistrart接口:手动注册bean到容器中

    public interface ImportBeanDefinitionRegistrar {
        /**
         * AnnotationMetadata:当前类的注解信息
         * BeanDefinitionRegistry:BeanDefinition注册类;
         * 把所有需要添加到容器中的bean;调用
         * BeanDefinitionRegistry.registerBeanDefinition手工注册进来
    */ public void registerBeanDefinitions( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }

    自定义实现类:

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        
            boolean definition = registry.containsBeanDefinition("com.athome.bean.Red");
            boolean definition2 = registry.containsBeanDefinition("com.athome.bean.Blue");
            if(definition && definition2){
                //指定Bean定义信息;(Bean的类型,Bean。。。)
                RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
                //注册一个Bean,指定bean名
                registry.registerBeanDefinition("rainBow", beanDefinition);
            }
        }
    }

    使用@Import来导入

    @Configuration
    @Import({MyImportBeanDefinitionRegistrar .class})
    public class MyConfing {
    }

    2.4.FactoryBean工厂注册组件

    使用FactoryBean接口的实现类来给容器中注入组件,该接口里面的getObject()方法可以返回该工厂生产的Bean。

    FactoryBean接口:

    public interface BeanFactory {
    
        String FACTORY_BEAN_PREFIX = "&";
    
        Object getBean(String name) throws BeansException;
    
        <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    
        Object getBean(String name, Object... args) throws BeansException;
    
        <T> T getBean(Class<T> requiredType) throws BeansException;
    
        <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
        <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    
        <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    
        boolean containsBean(String name);
    
        boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    
        boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    
        boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    
        boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    
        @Nullable
        Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    
        String[] getAliases(String name);
    }

    自定义一个bean实现FactoryBean接口:

    //创建一个Spring定义的FactoryBean
    public class ColorFactoryBean implements FactoryBean<Color> {
        //返回一个Color对象,这个对象会添加到容器中
        public Color getObject() throws Exception {
            return new Color();
        }
        public Class<?> getObjectType() {
            return Color.class;
        }
        //是单例?
        //true:这个bean是单实例,在容器中保存一份
        //false:多实例,每次获取都会创建一个新的bean;
        public boolean isSingleton() {
            return false;
        }
    }

    注册ColorFactoryBean :

     @Bean
     public ColorFactoryBean  colorFactoryBean(){
        return new ColorFactoryBean();
     }

    测试:

        @Test
        public void colorFactoryBeansTest(){
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig02.class);
            String[] beansName = ac.getBeanDefinitionNames();
            for (String s: beansName) {
                System.out.println(s);
            }
            //工厂bean获取的是调用getObject创建对象
            Object bean01 = ac.getBean("colorFactoryBean");
            Object bean02 = ac.getBean("colorFactoryBean");
            //+前缀 & 告诉spring 拿工厂bean 本身
            Object bean03 = ac.getBean("&colorFactoryBean").getClass();
    
            System.out.println("bean01的类型: "+bean01.getClass());
            //"修改isSingleton为单实例返回值 true 
            System.out.println( bean01 == bean02);
            System.out.println("+前缀 & 的bean03的类型: "+bean03);
        }

    注意:

    1.默认获取到的是工厂bean调用getObject创建的对象
    2.要获取工厂Bean本身,我们需要给id前面加一个&

    3.小结

         给容器中注册组件:


         1)、包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)[自己写的类]


         2)、@Bean[导入的第三方包里面的组件]


         3)、@Import[快速给容器中导入一个组件]
                 1)、@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名
                 2)、ImportSelector:返回需要导入的组件的全类名数组;
                 3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中


         4)、使用Spring提供的 FactoryBean(工厂Bean);
                 1)、默认获取到的是工厂bean调用getObject创建的对象
                 2)、要获取工厂Bean本身,我们需要给id前面加一个&
     

  • 相关阅读:
    [转]CROSS APPLY 和outer apply 的区别
    第一次WCF部署成功心得(UP+证书)
    .NET 简单实现广播
    【转】 c#注册全局快捷键
    软件人才成长链
    [转]关于VS2005智能设备中无法找到PInvoke DLL问题
    [转]我倡导无政府主义编程—Fred George访谈录
    运行Windows Mobile程序时报错:无法找到 PInvoke DLL SQLite.Interop.065.DLL
    系统分析师考试说明
    [转]Oracle 字符集的查看和修改
  • 原文地址:https://www.cnblogs.com/not-miss/p/10798024.html
Copyright © 2020-2023  润新知