码迷,mamicode.com
首页 > 编程语言 > 详细

spring注解之组件注册

时间:2019-05-03 09:57:53      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:boolean   rop   span   判断   ebean   exception   手工   default   extend   

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前面加一个&
 

 

spring注解之组件注册

标签:boolean   rop   span   判断   ebean   exception   手工   default   extend   

原文地址:https://www.cnblogs.com/not-miss/p/10798024.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!