spring -- 注解形式的组件注册

@Configuration&@Bean给容器中注册组件
原来我们向容器中添加一个bean的时候,我们是这么做的


            <bean id="person" class="main.java.pojo.Person">
                <constructor-arg name="name" value="kevin"></constructor-arg>
                <constructor-arg name="age" value="100"></constructor-arg>
            </bean>

然后再在这样获取容器中的bean

         ClassPathXmlApplicationContext classPathXmlApplicationContext1 = new ClassPathXmlApplicationContext("main/resource/beans.xml");
         Person person1 =(Person) classPathXmlApplicationContext1.getBean("person");

我们也可以通过注解驱动+配置类的方式向容器中注册一个bean

1.编写MyConfig配置类

    @Configuration //加入@Configuration 注解表示告诉spring这个类是一个配置类,spring容器在启动时会加载里边的信息
    public class MyConfig {
    
        @Bean //@Bean相当于我们原来xml配置中的<Bean>标签 id:默认是方法名,当然也可以自定义,类型是返回值类型,将方法的返回值放入到ioc容器中
        public Person getPerson(){
            return new Person("kevin",21);
        }
    }

2.测试

    	    // 用到一个新的ApplicationContext的实现类
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
            Person person = (Person) applicationContext.getBean("getPerson");
            // 获取bean的id
            String[] beanNames = applicationContext.getBeanNamesForType(Person.class);
            for (String name : beanNames){
                System.out.println(name);
            }
            System.out.println(person);

@ComponentScan-自动扫描组件&指定扫描规则
原来我们还可以配置一个包扫描,可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进Spring容器中管理


    		<!-- 包扫描 -->
            <context:component-scan base-package="main.java.*"></context:component-scan>

现在也可以在以上配置类中加上注解配置

    @ComponentScan(value = "main.java.*") //value:指定要扫描的包

加上controller层,service层测试:

    	    // 获取ioc容器中的所有bean id
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for(String beanName : beanDefinitionNames ){
                System.out.println(beanName);
            }

结果:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    myConfig
    personController
    personDao
    personService
    getPerson

原来<context:component-scan base-package=“main.java.*”></context:component-scan>会提供两个子标签让我们根据需要向容器中导入相关组件

 <context:include-filter>
 <context:exclude-filter>
 Use-dafault-filters=”false”的情况下:
 <context:exclude-filter>指定的不扫描
 <context:include-filter>指定的扫描

现在同样可以通过上述注解形式配置

    @ComponentScans(value = {
            @ComponentScan(value = "main.java.*",includeFilters ={
                    @ComponentScan.Filter(type = FilterType.ANNOTATION , classes = {Controller.class, Service.class})
            },useDefaultFilters = false //禁用默认的扫描规则
            )
    
    })
    //@ComponentScan  value:指定要扫描的包
    //excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
    //includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件
    //FilterType.ANNOTATION:按照注解
    //FilterType.ASSIGNABLE_TYPE:按照给定的类型;
    //FilterType.ASPECTJ:使用ASPECTJ表达式
    //FilterType.REGEX:使用正则指定
    //FilterType.CUSTOM:使用自定义规则

结果:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.annotation.internalRequiredAnnotationProcessor
    org.springframework.context.annotation.internalCommonAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    myConfig
    personController
    personService
    getPerson

@Scope-设置组件作用域 和 懒加载

        @Scope("prototype")//默认是单实例
        @Bean
        public Person getPerson(){
            System.out.println("创建对象");
            return new Person("kevin",21);
        }

测试:

    		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
            Person person1 = (Person) applicationContext.getBean("getPerson");
            Person person2 = (Person) applicationContext.getBean("getPerson");
            // 获取bean的id
            String[] beanNames = applicationContext.getBeanNamesForType(Person.class);
            for (String name : beanNames){
                System.out.println(name);
            }
            System.out.println(person1);
            System.out.println(person2);
            System.out.println(person1 == person2);

结果:

    创建对象
    创建对象
    getPerson
    Person{name='kevin', age=21}
    Person{name='kevin', age=21}
    false

注意点:
1.多实例情况下,ioc容器启动并不会去调用方法实例化对象放在容器中,而是每次获取时采取调用方法

    	    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
            System.out.println("ioc容器启动");
           //我先注释掉调用代码
            /*Person person1 = (Person) applicationContext.getBean("getPerson");
            Person person2 = (Person) applicationContext.getBean("getPerson");
            System.out.println(person1);
            System.out.println(person2);
            System.out.println(person1 == person2);*/

结果:

    ioc容器启动
    getPerson

从结果可以看到,ioc容器启动后只是知道了有这个bean,但是却没调用方法

2.单实例的(默认值):ioc容器启动会调用方法创建对象放到ioc容器中。以后每次获取就是直接从容器(map.get())中拿

//@Scope("prototype")//默认是单实例

结果:

创建对象
ioc容器启动
getPerson

如果我们希望在单实例情况下像多实例一样,当调用时才会去创建对象,这时候就需要用到懒加载

        //@Scope("prototype")//默认是单实例
        @Lazy
        @Bean
        public Person getPerson(){
            System.out.println("创建对象");
            return new Person("kevin",21);
        }

结果;

ioc容器启动
getPerson

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

@Conditional-按照条件注册

如果有任务要求我们在不同环境下加载不同的bean到容器中,比如在window环境下加载对象1,Linux环境下加载对象2,这是可以使用@Conditional注解加上相关类完成,@Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean

        @Conditional(value = {WinCondition.class})
        @Bean
        public Person getPerson(){
            System.out.println("创建对象1");
            return new Person("kevin1",21);
        }
    
        @Conditional(value = {LinuxConfition.class})
        @Bean
        public Person getPerson1(){
            System.out.println("创建对象2");
            return new Person("kevin2",2456);
        }

编写相关类实现 Condition接口

    public class LinuxConfition implements Condition {
        /**
         * ConditionContext:判断条件能使用的上下文(环境)
         * AnnotatedTypeMetadata:注释信息
         */
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
            // TODO是否window系统
            //1、能获取到ioc使用的beanfactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            //2、获取类加载器
            ClassLoader classLoader = context.getClassLoader();
            //3、获取当前环境信息
            Environment environment = context.getEnvironment();
            //4、获取到bean定义的注册类
            BeanDefinitionRegistry registry = context.getRegistry();
    
            String property = environment.getProperty("os.name");
    
            //可以判断容器中的bean注册情况,也可以给容器中注册bean
            boolean definition = registry.containsBeanDefinition("person");
            if(property.contains("linux")){
                return true;
            }
    
            return false;
        }
    }

另外一个同理,就省略了

     	    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
            System.out.println("ioc容器启动");
            // 获取bean的id
            String[] beanNames = applicationContext.getBeanNamesForType(Person.class);
            for (String name : beanNames){
                System.out.println(name);
            }

结果:

ioc容器启动
getPerson1

@Import-给容器中快速导入一个组件
之前我们每次导入一个组件都需要使用@bean注解,其实,我们也可以使用这个注解实现快速导入第三包

新建一个color

public class Color {
}

使用@Import导入

@Import({Color.class})

结果:

myConfig
personController
personService
main.java.pojo.Color

@Import(要导入到容器中的组件);容器中就会自动注册这个组件,id默认是全类名

除此之外,还可以在@Import-使用ImportSelector

我们还可以使用ImportSelector导入color

@Import({MyImportSelector.class})

我们需要自定义导入规则

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

结果还是跟上边一样

也可以在@Import-使用ImportBeanDefinitionRegistrar完成手工注册bean

@Import({MyImportBeanDefinitionRegistrar.class})

自定义的ImportBeanDefinitionRegistrar

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
       /**
		 * AnnotationMetadata:当前类的注解信息
		 * BeanDefinitionRegistry:BeanDefinition注册类;
	 	 * 		把所有需要添加到容器中的bean;调用
	 	 * 		BeanDefinitionRegistry.registerBeanDefinition手工注册进来
		 */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
            // 定义bean的注册信息
            RootBeanDefinition beanDefinition = new RootBeanDefinition(Color.class);
            // 注册bean
            beanDefinitionRegistry.registerBeanDefinition("Color",beanDefinition);
        }
    }

结果还是一样

使用FactoryBean注册组件

 public class PersonBeanFactory implements FactoryBean {
    
        //返回一个Color对象,这个对象会添加到容器中
        @Override
        public Object getObject() throws Exception {
            return new Person("kekk",6465);
        }
    
        @Override
        public Class<?> getObjectType() {
            return Person.class;
        }
    
        //是单例?
        //true:这个bean是单实例,在容器中保存一份
        //false:多实例,每次获取都会创建一个新的bean;
        @Override
        public boolean isSingleton() {
            return false;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值