美文网首页spring
[2]Spring 5 注解编程基础组件-配置类注解

[2]Spring 5 注解编程基础组件-配置类注解

作者: AC编程 | 来源:发表于2022-09-09 07:27 被阅读0次

    连载地址

    总览、配置组件 Configure Components

    配置组件

    一、 @Configuration 注解

    1.1 功能说明

    把一个类作为一个IOC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。以前我们写在配置文件,配置一个个bean,现在使用这个注解即可。

    1.2 要点说明
    • @Bean 默认用方法名作为bean的key,如果要自定义key,可以设置@Bean的value值。

    • person方法里使用new 对象其实不是真的new出来的,是使用原型模式生成的,所以new几次都是同一实例,只会调用一次,注入拿到的Person都是同一个。

    1.3 源码

    @Configuration注解源码:

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.core.annotation.AliasFor;
    import org.springframework.stereotype.Component;
    
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
        @AliasFor(
            annotation = Component.class
        )
        String value() default "";
    }
    

    @Bean源码:

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    import org.springframework.beans.factory.annotation.Autowire;
    import org.springframework.core.annotation.AliasFor;
    
    @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Bean {
        @AliasFor("name")
        String[] value() default {};
    
        @AliasFor("value")
        String[] name() default {};
    
        Autowire autowire() default Autowire.NO;
    
        String initMethod() default "";
    
        String destroyMethod() default "(inferred)";
    }
    
    1.4 demo

    MyConfiguration注解配置类:

    @Configuration
    public class MyConfiguration {
    
        /**
         * 1、只会调用一次,注入拿到的Cat都是同一个
         * 2、默认用方法名作为bean的key,获取bean时,默认用方法名cat
         * @return
         */
        @Bean
        public Cat cat(){
            return new Cat("小花猫",2);
        }
    
        /**
         * 1、只会调用一次
         * 2、自定义bean的key,设置value值为duck
         * @return
         */
        @Bean("duck")
        public Duck duck2(){
            return new Duck("唐老鸭",1);
        }
    }
    

    MyTest测试类:

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyConfiguration.class);
    
            System.out.println("默认用方法名取bean---------------");
            //默认用方法名取bean
            Object bean1 = app.getBean("cat");
            Object bean2 = app.getBean("cat");
    
            //注入拿到的Cat都是同一个
            Boolean e = bean1 == bean2;
    
            System.out.println(bean1);
            System.out.println(bean2);
            System.out.println(e);
    
            System.out.println();
            System.out.println("用class type取bean---------------");
            //用class type取bean
            Cat cat = app.getBean(Cat.class);
            System.out.println(cat);
            System.out.println(bean1==cat);
    
            System.out.println();
            System.out.println("用自定义key取bean---------------");
            Object duck = app.getBean("duck");
            System.out.println(duck);
        }
    }
    

    运行结果:

    默认用方法名取bean---------------
    Cat{name='小花猫', age=2}
    Cat{name='小花猫', age=2}
    true
    
    用class type取bean---------------
    Cat{name='小花猫', age=2}
    true
    
    1.5 原始xml方式写法

    application.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="cat" class="com.ac.annotation.demo.configures.configuration.Cat">
            <property name="name" value="小花猫"/>
            <property name="age" value="2"/>
        </bean>
    
        <bean id="duck" class="com.ac.annotation.demo.configures.configuration.Duck">
            <property name="name" value="唐老鸭"/>
            <property name="age" value="1"/>
        </bean>
    </beans>
    

    二、 @ComponentScan

    2.1 功能说明

    就跟之前xml 中的<context:component-scan>效果一样,注解在某类上时会扫描项目下的所有组件。

    如果你定义了包路径的话就扫描该路径下的所有组件,记住是组件,不是所有类,比如@Controller@Service@Repository这种,或者@Component这种。

    总的来说就是扫描一些类,将这些类的实例放到IoC容器中,然后我要用的时候不需要new,使用依赖注入即可。

    2.2 要点说明
    2.2.1 Filter类型:
    public enum FilterType {
        ANNOTATION,  //按照注解
        ASSIGNABLE_TYPE, //扫描指定的类型
        ASPECTJ,
        REGEX,   //使用正则表达式
        CUSTOM   //自定义过滤规则
    }
    
    2.3 源码

    @ComponentScan注解源码:

    
    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Repeatable;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.beans.factory.support.BeanNameGenerator;
    import org.springframework.core.annotation.AliasFor;
    import org.springframework.core.type.filter.TypeFilter;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Repeatable(ComponentScans.class)
    public @interface ComponentScan {
        
        @AliasFor("basePackages")
        String[] value() default {};
        
        @AliasFor("value")
        String[] basePackages() default {};
        
        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 {};
        
        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 {};
            
            String[] pattern() default {};
        }
    }
    
    2.4 demo
    2.4.1 辅助类

    1、普通java类PersonVO

    package com.ac.annotation.demo.configures.componentscan;
    
    public class PersonVO {
    }
    

    2、PersonController类

    package com.ac.annotation.demo.configures.componentscan;
    
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class PersonController {
    }
    
    2.4.2 测试类

    1、注解在某类上时会扫描该类所在包下的所有类

    @Configuration
    @ComponentScan
    public class MyComponentScan {
        // 注解在某类上时会扫描该类所在包下的所有类
    }
    

    2、定义了包路径的话就扫描该路径下的所有组件

    @Configuration
    @ComponentScan("com.ac.annotation.demo.configures.componentscan")
    public class MyComponentScan2 {
        // 定义了包路径的话就扫描该路径下的所有组件,记住是组件,不是所有类,比如`@Controller`、`@Service`、`@Repository`这种,或者`@Component`这种。
    }
    

    3、只扫描配置了指定注解的类,比如配置了@Controller注解的类

    @Configuration
    @ComponentScan(
            value = "com.ac.annotation.demo.configures.componentscan",
            includeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value= Controller.class)},
            useDefaultFilters=false)
    public class MyComponentScan3 {
        // 只扫描配置了指定注解的类,比如配置了@Controller注解的类
    }
    

    4、扫描指定的类型

    @Configuration
    @ComponentScan(
            value = "com.ac.annotation.demo.configures.componentscan",
            useDefaultFilters = false,
            includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {PersonVO.class, Person.class, Worker.class})}
    )
    public class MyComponentScan4 {
        // 扫描指定的类型
        // 注意:PersonVO只是一个普通的java类,没有任何注解,但也能被扫描出来
    }
    

    5、自定义过滤规则
    MyComponentScan5:

    @Configuration
    @ComponentScan(
            value = "com.ac.annotation.demo.configures.componentscan",
            includeFilters={@ComponentScan.Filter(type= FilterType.CUSTOM,value= MyScanFilter.class)},
            useDefaultFilters=false)
    @Component
    public class MyComponentScan5 {
        // 自定义过滤规则
    }
    

    MyScanFilter:

    public class MyScanFilter implements TypeFilter {
    
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory){
            // 获取当前类所有的注解信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
            // 获取当前扫描到的类的信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            // 获取到当前类的所有资源
            Resource resource = metadataReader.getResource();
            String className = classMetadata.getClassName();
            if (className.contains("Controller")) {
                return true;
            }
            return false;
        }
    }
    

    6、排除过滤器

    @Configuration
    @ComponentScan(
            value = "com.ac.annotation.demo.configures.componentscan",
            excludeFilters={@ComponentScan.Filter(type= FilterType.ANNOTATION,value= Controller.class)},
            useDefaultFilters=false)
    public class MyComponentScan6 {
        // 排除过滤器,排除指定注解的类,比如配置了@Controller注解的类
    }
    
    2.4.3 测试类

    MyTest测试类

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyComponentScan.class);
            printBeanNames(app);
    
            /**
             * 运行结果:
             * myComponentScan
             * myComponentScan2
             * myComponentScan3
             * myComponentScan4
             * myComponentScan5
             * myComponentScan6
             * personController
             * person
             * personVO
             * worker
             */
        }
    
        @Test
        public void test2(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyComponentScan2.class);
            printBeanNames(app);
    
            /**
             * 运行结果:
             * myComponentScan2
             * myComponentScan
             * myComponentScan3
             * myComponentScan4
             * myComponentScan5
             * myComponentScan6
             * personController
             * person
             * personVO
             * worker
             */
    
            /**
             * 说明:按理说personVO是不会被扫描出来的,这运行结果里却有personVO,
             * 是因为MyComponentScan4里把personVO扫描出来了,而MyComponentScan2又扫描了MyComponentScan4,所以personVO就出来了。
             * 如果MyComponentScan4注释掉,personVO就不会出来了。
             */
        }
    
        @Test
        public void test3(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyComponentScan3.class);
            printBeanNames(app);
    
            /**
             * 运行结果:
             * myComponentScan3
             * personController
             */
        }
    
        @Test
        public void test4(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyComponentScan4.class);
            printBeanNames(app);
    
            /**
             * 运行结果:
             * myComponentScan4
             * personVO
             * person
             * worker
             */
        }
    
        @Test
        public void test5(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyComponentScan5.class);
            printBeanNames(app);
    
            /**
             * 运行结果:
             * myComponentScan5
             * personController
             */
        }
    
        @Test
        public void test6(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyComponentScan6.class);
            printBeanNames(app);
    
            /**
             * 运行结果:
             * myComponentScan6
             */
        }
    
        private void printBeanNames(ApplicationContext app){
            String[] beanNames = app.getBeanDefinitionNames();
            for(String beanName : beanNames){
                if(!beanName.contains("springframework")){
                    System.out.println(beanName);
                }
            }
        }
    }
    
    2.5 原始xml方式写法
    <context:component-scan base-package="com.ac.annotation"/>
    

    三、@Scope 注解

    3.1 功能说明

    该注解用于指定作用域,用在类上。

    3.2 要点说明

    Scope有四种范围:

    • singleton:单例,默认scope
    • prototype:原型,多例,每次都会是新的一个对象
    • request:主要用于web模块,同一次请求值创建一个实例
    • session:主要用于web模块, 同一个session只创建一个实例
    3.3 源码

    @Scope注解源码

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import org.springframework.core.annotation.AliasFor;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Scope {
        
        @AliasFor("scopeName")
        String value() default "";
        
        @AliasFor("value")
        String scopeName() default "";
        
        ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
    }
    
    3.4 demo

    MyScope 注解配置类:

    @Configuration
    public class MyScope {
    
        @Scope("singleton")
        @Bean
        public Boss singletonBoss() {
            return new Boss("AlanChen", 18);
        }
    
        @Scope("prototype")
        @Bean
        public Boss prototypeBoss() {
            return new Boss("AC", 30);
        }
    }
    

    MyTest测试类:

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyScope.class);
    
            System.out.println("singleton测试---------------");
            Object bean1 = app.getBean("singletonBoss");
            Object bean2 = app.getBean("singletonBoss");
            System.out.println(bean1 == bean2);
    
            System.out.println("prototype测试---------------");
            Object bean3 = app.getBean("prototypeBoss");
            Object bean4 = app.getBean("prototypeBoss");
            System.out.println(bean3 == bean4);
        }
    }
    

    运行结果:

    singleton测试---------------
    true
    
    prototype测试---------------
    false
    

    四、@Lazy 注解

    4.1 功能说明

    延时初始化

    4.2 要点说明
    • 默认不延时加载,如果加上此注解则使当前类延时加载。
    • 延时加载只针对单例Bean(即@Scope为singleton的Bean)起作用。
    • 加上此注解后容器启动时不创建对象,调用对象的功能时才创建对象。
    4.3 源码

    @Lazy 注解源码

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Lazy {
        boolean value() default true;
    }
    
    4.4 demo

    MyLazy 注解配置类:

    @Configuration
    public class MyLazy {
    
        @Bean
        public Dog normalDog() {
            System.out.println("将normalDog添加到IOC容器中");
            return new Dog("灰灰", 1);
        }
    
        @Lazy
        @Bean
        public Dog lazyDog() {
            //延迟加载,调用此对象时才会去创建
            System.out.println("将lazyDog添加到IOC容器中");
            return new Dog("毛毛", 2);
        }
    }
    

    MyTest测试类:

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyLazy.class);
            System.out.println("IOC容器已创建---------------");
    
            Object bean1 = app.getBean("normalDog");
            Object bean2 = app.getBean("lazyDog");
    
            System.out.println(bean1 == bean2);
        }
    }
    

    运行结果:

    将normalDog添加到IOC容器中
    IOC容器已创建---------------
    将lazyDog添加到IOC容器中
    false
    

    五、@Conditional 注解

    5.1 功能说明

    Spring4开始提供,作用是按照一定的条件进行判断,如果满足条件则给容器注册Bean。

    5.2 要点说明

    使用@Conditional注解要配合Condition类

    5.3 源码

    @Conditional 注解源码

    package org.springframework.context.annotation;
    
    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Conditional {
        Class<? extends Condition>[] value();
    }
    

    Condition接口源码

    package org.springframework.context.annotation;
    
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    @FunctionalInterface
    public interface Condition {
        boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
    }
    
    5.4 demo
    5.4.1 驱动condition

    1、WindowsCondition

    public class WindowsCondition implements Condition {
    
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Environment environment = context.getEnvironment();
            String osName = environment.getProperty("os.name");
            System.out.println("WindowsCondition,当前系统环境为:"+osName);
            if (osName.contains("Windows")) {
                return true;
            }
            return false;
        }
    }
    

    2、LinuxCondition

    public class LinuxCondition implements Condition {
    
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Environment environment = context.getEnvironment();
            String osName = environment.getProperty("os.name");
            System.out.println("LinuxCondition,当前系统环境为:"+osName);
            if (osName.contains("Linux")) {
                return true;
            }
            return false;
        }
    }
    
    5.4.2 MyConditional
    @Configuration
    public class MyConditional {
        
        @Conditional(WindowsCondition.class)
        @Bean("printDrive")
        public PrintDrive windowsDrive(){
            System.out.println("创建windows打印驱动");
           return new PrintDrive("windows","windows打印驱动");
        }
    
        @Conditional(LinuxCondition.class)
        @Bean("printDrive")
        public PrintDrive linuxDrive(){
            System.out.println("创建linux打印驱动");
            return new PrintDrive("linux","linux打印驱动");
        }
    }
    
    5.4.3 PrintDrive
    public class PrintDrive {
    
        private String os;
    
        private String name;
    
        public PrintDrive() {
        }
    
        public PrintDrive(String os, String name) {
            this.os = os;
            this.name = name;
        }
    
        public String getOs() {
            return os;
        }
    
        public void setOs(String os) {
            this.os = os;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "PrintDrive{" +
                    "os='" + os + '\'' +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    5.4.4 MyTest
    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyConditional.class);
            System.out.println("IOC容器已创建---------------");
    
            PrintDrive drive = app.getBean(PrintDrive.class);
            System.out.println(drive);
        }
    }
    

    运行结果:

    WindowsCondition,当前系统环境为:Linux
    LinuxCondition,当前系统环境为:Linux
    创建linux打印驱动
    IOC容器已创建---------------
    PrintDrive{os='linux', name='linux打印驱动'}
    
    5.5 配置VM参数

    运行测试前,配置VM参数:

    -ea -Dos.name=Linux
    
    配置VM参数

    六、@Import 注解

    6.1 功能说明

    导入外部资源,相当于手动指定第三方资源,把其实例加载到IOC容器中。

    6.2 要点说明
    • 导入的类必须存在无参的构造方法

    • @Import导入的组件命名是类的全限定名

    • 通过@Bean导入的就是方法名

    6.3 @Import导入的三种类型

    1、导入类

    • A、导入普通类(4.2版本之后)
    • B、导入带有@Configuration的配置类(4.2 版本之前只可以导入配置类)

    2、ImportSelector的实现。
    3、ImportBeanDefinitionRegistrar的实现。

    6.4 源码

    @Import 注解源码

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Import {
    
        Class<?>[] value();
    }
    
    6.5 demo
    6.5.1 导入普通类

    1、新建一个普通的java类:UserEntity

    /**
     * 注意:这是一个普通Java类,没有任何注解
     * @author AlanChen
     */
    public class UserEntity {
    
        public void run(String str) {
            System.out.println(str);
        }
    
        public void printName() {
            System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
        }
    }
    

    注意:UserEntity是一个普通Java类,没有任何注解。

    2、新建一个MyImport,在类上面加上@Configuration,加上@Configuration是为了能让Spring扫描到这个类,并且直接通过@Import引入UserEntity类。

    @Import({UserEntity.class})
    @Configuration
    public class MyImport {
    
    }
    

    3、测试
    UserEntity是一个普通的类,现在可以通过ApplicationContext获取到然后后调用,就直接说明已经被Spring注入并管理了。

    @Test
    public void test(){
        ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
        UserEntity bean = app.getBean(UserEntity.class);
        bean.printName();
        bean.run("run userEntity");
    }
    

    运行结果:

    类名 :com.ac.annotation.demo.configures.import2.UserEntity
    run userEntity
    
    6.5.2 导入带有@Configuration的配置类

    1、新建导入带有@Configuration的配置类:UserConfig

    @Configuration
    public class UserConfig {
    
        public void run(String str) {
            System.out.println(str);
        }
    
        public void printName() {
            System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
        }
    }
    

    2、在MyImport.class里面直接引入UserConfig

    @Import({UserEntity.class,UserConfig.class})
    @Configuration
    public class MyImport {
    
    }
    

    3、测试

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserEntity bean = app.getBean(UserEntity.class);
            bean.printName();
            bean.run("run userEntity");
        }
    
        @Test
        public void test2(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserConfig bean = app.getBean(UserConfig.class);
            bean.printName();
            bean.run("run UserConfig");
        }
    }
    

    执行test2方法,运行结果:

    类名 :com.ac.annotation.demo.configures.import2.UserConfig
    run UserConfig
    
    6.5.3 通过ImportSelector方式导入类

    1、新建UserComponent类

    public class UserComponent {
    
        public void run(String str) {
            System.out.println(str);
        }
    
        public void printName() {
            System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
        }
    }
    

    2、新建MyImportSelector.class实现ImportSelector接口,注入UserComponent.class

    public class MyImportSelector implements ImportSelector {
    
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{"com.ac.annotation.demo.configures.import2.UserComponent"};
        }
    }
    

    3、MyImport上面引入MyImportSelector.class

    @Import({UserEntity.class,UserConfig.class,MyImportSelector.class})
    @Configuration
    public class MyImport {
    
    }
    

    4、测试

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserEntity bean = app.getBean(UserEntity.class);
            bean.printName();
            bean.run("run userEntity");
        }
    
        @Test
        public void test2(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserConfig bean = app.getBean(UserConfig.class);
            bean.printName();
            bean.run("run UserConfig");
        }
    
        @Test
        public void test3(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserComponent bean = app.getBean(UserComponent.class);
            bean.printName();
            bean.run("run UserComponent");
        }
    }
    

    执行test3方法,运行结果:

    类名 :com.ac.annotation.demo.configures.import2.UserComponent
    run UserComponent
    
    6.5.4 通过ImportBeanDefinitionRegistrar方式导入类

    1、新建类UserDao

    public class UserDao {
    
        public void run(String str) {
            System.out.println(str);
        }
    
        public void printName() {
            System.out.println("类名 :" + Thread.currentThread().getStackTrace()[1].getClassName());
        }
    }
    

    2、新建MyImportBeanDefinitionRegistrar.class,实现接口ImportBeanDefinitionRegistrar,注入UserDao.class

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            boolean hadEntity = registry.containsBeanDefinition("com.ac.annotation.demo.configures.import2.UserEntity");
            System.out.println("hadEntity=" + hadEntity);
    
            //假装加了一段逻辑,当然也可以直接注入UserDao
            if (hadEntity) {
                RootBeanDefinition root = new RootBeanDefinition(UserDao.class);
                registry.registerBeanDefinition("userDao", root);
            }
        }
    }
    

    3、MyImport类上加上导入MyImportBeanDefinitionRegistrar.class

    @Import({UserEntity.class,UserConfig.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
    @Configuration
    public class MyImport {
    
    }
    

    4、测试

    public class MyTest {
    
        @Test
        public void test(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserEntity bean = app.getBean(UserEntity.class);
            bean.printName();
            bean.run("run userEntity");
        }
    
        @Test
        public void test2(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserConfig bean = app.getBean(UserConfig.class);
            bean.printName();
            bean.run("run UserConfig");
        }
    
        @Test
        public void test3(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserComponent bean = app.getBean(UserComponent.class);
            bean.printName();
            bean.run("run UserComponent");
        }
    
        @Test
        public void test4(){
            ApplicationContext app = new AnnotationConfigApplicationContext(MyImport.class);
            UserDao bean = app.getBean(UserDao.class);
            bean.printName();
            bean.run("run UserDao");
        }
    }
    

    执行test4方法,运行结果:

    hadEntity=true
    类名 :com.ac.annotation.demo.configures.import2.UserDao
    run UserDao
    

    七、生命周期控制

    生命周期控制有四种实现方式,分别是:
    1、方式一
    使用@Bean注解的生命周期参数

    @Bean(initMethod = "start",destroyMethod = "stop")
    

    2、方式二
    实现接口InitializingBean, DisposableBean

    3、方式三
    在方法上使用生命周期注解:

    • @PostConstruct用于指定初始化方法(用在方法上)
    • @PreDestroy用于指定销毁方法(用在方法上)
    • @DependsOn定义Bean初始化和销毁的顺序

    4、 方式四
    实现接口BeanPostProcessor

    7.1 方式一:使用@Bean注解的生命周期参数
    7.1.1 代码
    public class BaoMaCar {
    
        public void start() {
            System.out.println("宝马汽车启动......");
        }
    
        public void run() {
            System.out.println("宝马汽车行驶中......");
        }
    
        public void stop() {
            System.out.println("宝马汽车熄火......");
        }
    }
    
    @Configuration
    public class LifeOneConfig {
    
        @Bean(initMethod = "start",destroyMethod = "stop")
        public BaoMaCar baoMaCar(){
            return new BaoMaCar();
        }
    }
    
    public class MyTest {
    
        @Test
        public void test(){
            AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(LifeOneConfig.class);
            BaoMaCar bean = app.getBean(BaoMaCar.class);
            bean.run();
    
            //关闭content,会调用bean的destroyMethod
            app.close();
        }
    }
    
    7.1.2 运行结果
    宝马汽车启动......
    宝马汽车行驶中......
    九月 08, 2022 6:27:55 下午 org.springframework.context.support.AbstractApplicationContext doClose
    信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3ecf72fd: startup date [Thu Sep 08 18:27:55 CST 2022]; root of context hierarchy
    宝马汽车熄火......
    
    7.2 方式二:实现接口InitializingBean, DisposableBean
    7.2.1 代码
    public class BenChiCar implements InitializingBean, DisposableBean {
    
        /**
         * 重写接口方法
         */
        public void destroy() {
            stop();
        }
    
        /**
         * 重写接口方法
         */
        public void afterPropertiesSet() {
            start();
        }
    
        public void start() {
            System.out.println("奔驰汽车启动......");
        }
    
        public void run() {
            System.out.println("奔驰汽车行驶中......");
        }
    
        public void stop() {
            System.out.println("奔驰汽车熄火......");
        }
    }
    
    @Configuration
    public class LifeTwoConfig {
    
        @Bean
        public BenChiCar benChiCar(){
            return new BenChiCar();
        }
    }
    
    public class MyTest {
    
        @Test
        public void test(){
            AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(LifeTwoConfig.class);
            BenChiCar bean = app.getBean(BenChiCar.class);
            bean.run();
    
            //关闭content,会调用bean的destroyMethod
            app.close();
        }
    }
    
    7.2.2 运行结果
    奔驰汽车启动......
    奔驰汽车行驶中......
    九月 08, 2022 6:30:25 下午 org.springframework.context.support.AbstractApplicationContext doClose
    信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3ecf72fd: startup date [Thu Sep 08 18:30:25 CST 2022]; root of context hierarchy
    奔驰汽车熄火......
    
    7.3 方式三:在方法上使用生命周期注解
    7.3.1 代码
    public class AoDiCar {
    
        @PostConstruct
        public void start() {
            System.out.println("奥迪汽车启动......");
        }
    
        public void run() {
            System.out.println("奥迪汽车行驶中......");
        }
    
        @PreDestroy
        public void stop() {
            System.out.println("奥迪汽车熄火......");
        }
    }
    
    @Configuration
    public class LifeThreeConfig {
    
        @Bean
        public AoDiCar aoDiCar(){
            return new AoDiCar();
        }
    }
    
    public class MyTest {
    
        @Test
        public void test(){
            AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(LifeThreeConfig.class);
            AoDiCar bean = app.getBean(AoDiCar.class);
            bean.run();
    
            //关闭content,会调用bean的destroyMethod
            app.close();
        }
    }
    
    7.3.2 运行结果
    奥迪汽车启动......
    奥迪汽车行驶中......
    九月 08, 2022 6:32:01 下午 org.springframework.context.support.AbstractApplicationContext doClose
    信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3ecf72fd: startup date [Thu Sep 08 18:32:01 CST 2022]; root of context hierarchy
    奥迪汽车熄火......
    
    7.4 方式四:实现接口BeanPostProcessor

    这种方式最灵活,这样写定义类,可以在其中获取所有bean,可以针对性的处理

    7.4.1 代码
    */
    public class FuTeCar{
    
       public void start() {
           System.out.println("福特汽车启动......");
       }
    
       public void run() {
           System.out.println("福特汽车行驶中......");
       }
    
       public void stop() {
           System.out.println("福特汽车熄火......");
       }
    }
    
    @Configuration
    @ComponentScan
    public class LifeFourConfig {
    
        @Bean
        public FuTeCar fuTeCar() {
            return new FuTeCar();
        }
    }
    
    @Configuration
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
        /**
         * 重写接口
         */
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            System.out.println(beanName);
            if (bean instanceof FuTeCar) {
                FuTeCar fuTeCar = (FuTeCar) bean;
                fuTeCar.start();
            }
            return bean;
        }
    
        /**
         * 重写接口
         */
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            if (bean instanceof FuTeCar) {
                FuTeCar fuTeCar = (FuTeCar) bean;
                fuTeCar.run();
            }
            return bean;
        }
    }
    
    public class MyTest {
    
        @Test
        public void test() {
            AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(LifeFourConfig.class);
            System.out.println("IOC容器创建完成");
    
            //关闭content,会调用bean的destroyMethod
            app.close();
        }
    }
    
    7.4.2 运行结果
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    lifeFourConfig
    fuTeCar
    福特汽车启动......
    福特汽车行驶中......
    IOC容器创建完成
    

    相关文章

      网友评论

        本文标题:[2]Spring 5 注解编程基础组件-配置类注解

        本文链接:https://www.haomeiwen.com/subject/waclnrtx.html