Spring注解驱动开发——组件注册

一、使用@Configuration@Bean给容器中注册组件

/**
 * 配置类,使用@Configuration注解标识这是一个配置类
 */
@Configuration
public class MainConfig {

    /*@Bean注解:给容器中注册一个bean
    * class类型为返回值的类型
    * id默认是方法名
    * */
    @Bean(value = "person")
    public Person person(){
        return new Person(1,"lisi");
    }

}

测试配置类

    @Test
    public void testConfiguration(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

        //使用class类型获取bean
        Person person = context.getBean(Person.class);

        //使用id获取bean
        Person person1 = (Person) context.getBean("person");

        System.out.println(person);
        System.out.println(person1);
        
        //容器中都是同一个bean
        System.out.println(person1 == person);
    }

输出结果:
在这里插入图片描述

二、@ComponentScan自动扫描组件

@Configuration
@ComponentScan(value = {"bean","controller","dao","service"})
public class MainConfig {

    /*@Bean注解:给容器中注册一个bean
    * class类型为返回值的类型
    * id默认是方法名
    * */
    @Bean(value = "person")
    public Person person(){
        return new Person(1,"lisi");
    }

}

测试扫描到了容器中哪些组件

    @Test
    public void testComponentScan(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

        String[] beanDefinitionNames = context.getBeanDefinitionNames();

        for (String beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }

输出结果:
在这里插入图片描述
还可以使用@Filter注解指定排除规则

@Configuration
@ComponentScan(value = "spring.annotation",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})
})
public class MainConfig {

    /*@Bean注解:给容器中注册一个bean
    * class类型为返回值的类型
    * id默认是方法名
    * */
    @Bean(value = "person")
    public Person person(){
        return new Person(1,"lisi");
    }

}

输出结果:
在这里插入图片描述

三、自定义TypeFilter指定过滤规则

public enum FilterType {
    ANNOTATION,				
    ASSIGNABLE_TYPE,		
    ASPECTJ,
    REGEX,
    CUSTOM;

    private FilterType() {
    }
}
//按照注解过滤
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class, Service.class})}

//按照指定的类型过滤
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {BookService.class})}

还可以自定义过滤规则,首先必须创建TypeFilter接口的实现类

	/** Filter candidates using a given custom
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM

创建MyTypeFilter自定义过滤规则类

public class MyTypeFilter implements TypeFilter {
    /**
     * 
     * @param metadataReader 读取到的当前正在扫描的类的信息
     * @param metadataReaderFactory 可以获取到其它任何类的信息
     * @return Boolean 返回true或false代表是否匹配成功
     * @throws IOException
     */
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

        //获取当前扫描类的注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();

        //获取当前扫描类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        
        //获取当前扫描类的资源信息
        Resource resource = metadataReader.getResource();
        
        //匹配全类名中包含“er”的类
        if (className.contains("er")){
            return true;
        }
        return false;
    }
}

@ComponentScan扫描组件中指定自定义的过滤规则

@ComponentScan(value = "spring.annotation",includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class})
},useDefaultFilters = false)

输出结果:
在这里插入图片描述

四、@Scope设置组件作用域

容器中bean默认是单实例的

    @Test
    public void testScope(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);

        //容器中bean默认是单实例的
        Object person = context.getBean("person");
        Object person1 = context.getBean("person");

        System.out.println(person == person1);		//true
    }

可以使用@Scope来指定作用域

ConfigurableBeanFactory#SCOPE_PROTOTYPE		//多实例
ConfigurableBeanFactory#SCOPE_SINGLETON		//单实例,默认值
org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST		//同一次请求创建一个实例
org.springframework.web.context.WebApplicationContext#SCOPE_SESSION		//同一个Session创建一个实例

设置为多实例

@Configuration
public class MainConfig2 {
    @Bean
    @Scope(value = "prototype")
    public Person person(){
        return new Person(2,"张三");
    }
}

测试

    @Test
    public void testScope(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);

        //容器中bean默认是单实例的
        Object person = context.getBean("person");
        Object person1 = context.getBean("person");

        System.out.println("person == person1 : " + (person == person1));
    }

输出结果:
在这里插入图片描述
注意:

  • 作用域是单实例,ioc容器启动会调用方法创建对象放到ioc容器中,以后每次获取就直接从ioc容器中拿。
  • 作用域是多实例,ioc容器启动时不会调用方法创建对象,而是在每次获取对象的时候,才会调用方法创建对象。

五、@Lazy懒加载

针对于单实例bean,默认在容器启动的时候就创建好对象。而懒加载就是使得在容器启动的时候暂时不创建对象,等到第一次获取bean的时候,再创建对象并进行初始化。

@Configuration
public class MainConfig2 {
    @Bean
    @Lazy
    public Person person(){
        return new Person(2,"张三");
    }
}

六、@Conditional按照条件注册bean

所有的bean都会默认自动加载到容器中

    @Test
    public void testCondition(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();

        for (String beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }

输出结果:
在这里插入图片描述
可以在方法上加上@Conditional来按照条件将bean加载到容器,首先要创建实现Condition接口的实现类

P1Condition类是当容器中存在p2这个bean的时候,就不会创建p1

public class P1Condition implements Condition {
    /**
     *
     * @param context 判断条件能使用的上下文(环境)
     * @param metadata  注释信息
     * @return  是否匹配
     */
    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[] beanDefinitionNames = registry.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames){
            if ("p2".equals(beanName)){		//容器中存在p2这个bean的时候,就不会创建p1
                return false;		
            }
        }
        return true;
    }
}

P2Condition类是当容器中存在p1这个bean的时候,就不会创建p2

public class P2Condition implements Condition {


    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        BeanDefinitionRegistry registry = context.getRegistry();

        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames) {
            if ("p1".equals(beanName)) {		//容器中存在p1这个bean的时候,就不会创建p2
                return false;
            }
        }
        return true;
    }

}

使用条件注解限制p1和p2同时加载到容器中

@Configuration
public class MainConfig2 {
    @Bean
    //@Scope(value = "prototype")
    @Lazy
    public Person person(){
        return new Person(2,"张三");
    }
    

    @Conditional({P1Condition.class})
    @Bean
    public Person p1(){
        return new Person(1,"person1");
    }
    
    @Conditional({P2Condition.class})
    @Bean
    public Person p2(){
        return new Person(1,"person2");
    }

}

测试

    @Test
    public void testCondition(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();

        for (String beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    

输出结果:
在这里插入图片描述
bean加载顺序与配置类中定义的方法的先后顺序有关

七、@import给容器中快速导入一个组件

给配置类加上@import注解,可以快速到日一个组件到ioc容器中

@Configuration
@Import({Man.class})		//可以导入多个
public class MainConfig2 {
}

查看ioc容器中所有的bean

    @Test
    public void testImport(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);

        String[] beanDefinitionNames = context.getBeanDefinitionNames();

        for (String beanName : beanDefinitionNames){
            System.out.println(beanName);
        }
    }

输出结果:
在这里插入图片描述
还可以通过创建ImportSelector接口的实现类,来自定义需要加载的组件

/**
 * 自定义逻辑。返回需要的组件
 */
public class MyImportSelector implements ImportSelector{

    /**
     * 
     * @param importingClassMetadata    当前标注@Import注解的类的所有注解信息
     * @return  返回值就是要导入到容器中的组件的全类名
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"spring.annotation.bean.Boy","spring.annotation.bean.Woman"};
    }
}

配置类中导入自定义的组件选择器

@Configuration
@Import({Man.class, MyImportSelector.class})
public class MainConfig2 {}

输出结果:
在这里插入图片描述
还可以通过创建ImportBeanDefinitionRegistrar接口的实现类来手动注册bean到容器中

public class MyImportBeanDefinitionRegistar implements ImportBeanDefinitionRegistrar {

    /**
     *
     * @param importingClassMetadata    当前类的注解信息
     * @param registry                  BeanDefinition注册类,
     *                                  可以调用BeanDefinitionRegistry类里面的registerBeanDefinition方法
     *                                  把所有需要bean手动注册到容器中
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean man = registry.containsBeanDefinition("spring.annotation.bean.Man");
        boolean boy = registry.containsBeanDefinition("spring.annotation.bean.Boy");
        if (man && boy){    //如果容器中同时又man和boy两个bean就注册girl这个bean到容器中
            registry.registerBeanDefinition("girl",new RootBeanDefinition(Girl.class));
        }
    }
}

配置类

@Configuration
@Import({Man.class, MyImportSelector.class, MyImportBeanDefinitionRegistar.class})
public class MainConfig2 {

输出结果:
在这里插入图片描述

给容器中注册组件的方式:

  1. 包扫描+组件标注注解(@Controller/@Service/@Repository/@Component)。
  2. 使用@Bean导入第三方包里面的组件,或者自己写的。
  3. 使用@Import快速导入一个组件,包括使用ImportSelector属性来自定义需要导入的组件的全类名数组;还有使用ImportBeanDefinitionRegistrar可以手动注册bean到容器中。

八、@FactoryBean注册组件

使用Spring提供的FactoryBean来注册组件

public class PersonFactoryBean implements FactoryBean<Person>{

    /**
     *
     * @return 返回一个Person对象,对象会添加到容器中
     * @throws Exception
     */
    public Person getObject() throws Exception {
        System.out.println("PersonFactoryBean...getObject");
        return new Person();
    }

    /**
     *
     * @return 返回对象的类型
     */
    public Class<?> getObjectType() {
        return Person.class;
    }

    /**
     *
     * @return 是否是单实例
     */
    public boolean isSingleton() {
        return true;
    }
}

配置类中加载PersonFactoryBean到容器中

    @Bean
    public PersonFactoryBean personFactoryBean(){
        return new PersonFactoryBean();
    }

测试

    @Test
    public void testFactoryBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);

        String[] beanDefinitionNames = context.getBeanDefinitionNames();

        for (String beanName : beanDefinitionNames){
            System.out.println(beanName);
        }

        //工厂bean实际上获取的是调用getObject方法创建的对象
        Object personFactoryBean = context.getBean("personFactoryBean");
        System.out.println("personFactoryBean的类型: " + personFactoryBean.getClass());

        //使用 & 符号可以获取工厂bean类型
        Object bean = context.getBean("&personFactoryBean");
        System.out.println("personFactoryBean的类型: " + bean.getClass());
    }

输出结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值