【深入学习Spring】6——Spring容器中注册组件的几种方式总结

1.@Controller/@Service/@Repository/@Component,结合包扫描

这是我们最常使用的方式,只需要在组件类上加上这四类注解即可。

2.@Bean[导入第三方包的组件]

这种方式在SpringBoot中最为常见。无论是我们自己定义组件,还是引入第三方的组件(比如数据源),都可以使用该方式。 容器中组件的id默认为@Bean对应方法的方法名。

使用@Scope

@Bean
@Scope("prototype")//指定Bean的作用域
public class Apple {
}

使用@Lazy

@Bean
@Lazy //指定Bean为懒加载
public class Banana {
}

使用@Conditional,条件成立才注册

public class MainConfig {
 
    @Bean("Windows")
    @Conditional(value = {WindowsCondition.class})
    public  WindowsBean getWin(){
        return  new WindowsBean();
    }
 
    @Bean("Linux")
    @Conditional(value = {LinuxCondition.class})
    public  LinuxBean getLinus(){
        return new LinuxBean();
    }

public class LinuxCondition implements Condition {

    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment =  conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        if(property.contains("Linux")){
            return  true;
        }
        return false;
    }

public class WindowsCondition implements Condition {
 
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment =  conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
 
        if(property.contains("Windows")){
            return true;
        }
        return false;
    }
    
}

使用@ComponentScan指定扫描类型,使用TypeFilter指定过滤规则

//排除扫描类型
@ComponentScan(value = "com.spring", excludeFilters = {
    //过滤注解类型,@Controller @Repository
    @Filter(type = FilterType.ANNOTATION, classes = { Controller.class,Repository.class })
})

//指定扫描类型
@ComponentScan(value = "com.spring", includeFilters = {
    //过滤注解类型  @Controller @Repository
    @Filter(type = FilterType.ANNOTATION, classes = { Controller.class,Repository.class })
},useDefaultFilters = false)//注意:使用扫描指定类型时,需要禁用默认规则

//【自定义类型】
@ComponentScans(value= {
    @ComponentScan(value = "com.spring", includeFilters = {
        @Filter(type = FilterType.ANNOTATION, classes = { Controller.class,Repository.class }) ,
        @Filter(type = FilterType.ASSIGNABLE_TYPE,classes = (BookService.class)),
        
        @Filter(type = FilterType.CUSTOM,classes = (MyTypeFilter.class)) //自定义类型
    },useDefaultFilters = false)
})

class MyTypeFilter implements TypeFilter {

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
            throws IOException {
        //获取当前类注解的信息
        AnnotationMetadata nnnotationmetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类的资源信息(类的路径)
        Resource resource = metadataReader.getResource();

        String className = classMetadata.getClassName();
        if (className.contains("er")) {
            return true;
        }
        return false;
    }

}

3.@Import[常规类]

@Import

@Import(常规类):为容器导入一个常规类,并将其注册为Bean,id默认为全类名。

@Configuration
@Import({Color.class}) //为容器导入Color组件
public class MainConfig{
}

@Import + ImportSelector

使用ImportSelector指定要导入的组件的全类名

@Configuration
@Import({MyImportSelector.class}))
public class MainConfig{
}

public class MyImportSelector implements ImportSelector {

    //方法返回值就是要导入到容器中的组件的全类名
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.scorpios.bean.Color","com.scorpios.bean.Blue"};
    }
}

@Import + ImportBeanDefinitionRegistrar

使用ImportBeanDefinitionRegistrar手动注册Bean

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

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    /**
     * 调用BeanDefinitionRegistry.registerBeanDefinition,手工来注册Bean
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean definition = registry.containsBeanDefinition("com.scorpios.bean.Color");
        boolean definition2 = registry.containsBeanDefinition("com.scorpios.bean.Blue");
        if(definition && definition2){
            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
            //手动注册Bean。指定bean名
            registry.registerBeanDefinition("rainBow", beanDefinition);
        }
    }
}

 

Spring在动态注册bean时,大部分套路其实就是使用ImportBeanDefinitionRegistrar接口。所有实现了该接口的类都会被ConfigurationClassPostProcessor进行后置处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先与依赖其的bean初始化的。

ImportBeanDefinitionRegistrar需要配合@Configuration和@Import注解来使用。

4.使用Spring提供的FactoryBean

@Configuration
public class MainConfig {

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

}

//定义一个FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

    //getBean("colorFactoryBean")时,会调用该方法
    public Color getObject() throws Exception {
        return new Color();
    }

    
    public Class<?> getObjectType() {
        return Color.class;//指定Bean的类型
    }

    public boolean isSingleton() {
        return false;
    }

}

//虽然指定的是ColorFactoryBean,但实际上获取到的是 Color
Object bean = context.getBean("colorFactoryBean")
System.out.println("Bean的类型" + bean.getClass);//【结果为Color类型】

//要获取FactoryBean本身,只需加个 & 符号
Object bean = context.getBean("&colorFactoryBean")
System.out.println("Bean的类型" + bean.getClass);//【结果为ColorFactoryBean类型】

 

posted @ 2020-02-28 22:38  静水楼台/Java部落阁  阅读(663)  评论(0编辑  收藏  举报