• Spring注解驱动笔记【更新ing】


    学习来源:B站尚硅谷《Spring注解驱动开发》
    内容较多,请结合CSDN网页右侧的目录组件阅读本文
    文章未完结。如有错误欢迎批评指正,感谢!

    J2EE

    @Retention

    @Target

    @Documented

    @Repeatable

    @SuppressWarnings

    Spring IoC

    组件扫描

    @ComponentScan

    注解的value参数指明要扫描的包路径,比如value="cn.louzen",即cn.louzen路径下的所有注解了 @Component 及其子注解如 @Controller、@Service、@Repository、@Configuration等 的类都会被扫描并放入容器中
    作用域:类
    JDK8及以上,@ComponentScan 是可以重复放在同一个类上的,或者用@ComponentScans
    
    @ComponentScan(basePackages={"cn.louzen"})
    public class MainConfig {}
    
    看它扫描到的所有组件:
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String definitionName : definitionNames) {
            System.out.println(definitionName);
        }
    
    可以在@ComponentScan中指定要扫描(includeFilters参数)或不扫描(excludeFilters参数)哪个包,
    其中,type指明依据哪种类型过滤,classes指明要过滤的内容,
    指定不扫描:
        @ComponentScan(basePackages={"cn.louzen"}, excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
        })
        public class MainConfig {}
    指定要扫描:
        @ComponentScan(basePackages={"cn.louzen"}, includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
        }, useDefaultFilters = false)
        public class MainConfig {}
    指定要扫描时要设置 useDefaultFilters = false,让默认过滤规则不生效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    注解形式

    
    设置默认过滤规则不生效:use-default-filters="false"
    
    • 1
    • 2

    @ComponentScan.@Filter

    指定过滤规则
    参数:
        type:(过滤类型,取值是 org.springframework.context.annotation.FilterType 枚举类型)
            ANNOTATION:按照注解过滤(常用)
            ASSIGNABLE_TYPE:按照指定具体哪个类过滤(常用)
            ASPECTJ:按照ASPECTJ表达式过滤
            REGEX:按照正则表达式过滤
            CUSTOM:自定义规则,规则类必须是org.springframework.core.type.filter.TypeFilter接口的实现类
        classes:(对应type的值)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    @ComponentScans

    可以放多个 @ComponentScan
    @ComponentScans(value = {
            @ComponentScan(basePackages={"cn.louzen"})
    })
    
    • 1
    • 2
    • 3
    • 4

    组件注册

    注册的方式有哪些

    给容器中注册组件:(详情见下文对每个注解的解释)
        1、包扫描 + 组件表注注解:(@ComponentScan扫描,@Component及其子注解注册组件)
        2、@Bean:导入第三方包里的组件
        3、@Import:快速给容器中导入一个组件,前提是组件的创建就是运行无参构造等简单的方式,不需要配置属性等操作
            3.1、@Import({要注册进容器中的组件类.class}):容器中就会自动注册这个组件,id默认是组件类的全限定名(见下面@Import的内容)
            3.2、@Import(ImportSelector实现类.class):ImportSelector接口,方法selectImports,方法返回需要导入的组件的全限定名的字符串数组(见下面@Import的内容)
            3.3、@Import(ImportBeanDefinitionRegistrar实现类.class):BeanDefinition注册类,把所有需要添加到容器中的bean,可以调用 ImportBeanDefinitionRegistrar 的 registerBeanDefinition 方法手动注册进来(见下面@Import的内容)
        4、使用Spring提供的FactoryBean(工厂Bean)接口,定义一个方法返回值是工厂类型,方法+@Bean可实现工厂的产品对象进容器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    @Bean

    被@Bean修饰的方法,参数中的对象类型是从Spring上下文中获取的
    作用域:方法、注解
    属性:
        value(= name):bean名,xml配置中对应的id属性
        name(= value):与value互为别名,一样的
        initMethod:指定初始化方法
        destroyMethod:指定销毁方法
    
    对普通的方法,bean类型为方法返回值的类型,id默认是方法名(@Bean的参数可以指定id):
        @Bean
        public Person person() {
            return new Person("lisi", 20);
        }
    
    获取对象:
        AnnotationConfigApplicationContext applicationContext = 
            new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
    
    同时,我们可以通过 applicationContext 上下文对象的一系列方法获取上下文的内容
    比如获取一个类(type)在上下文中的所有bean name:
        String[] beanNamesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String beanName : beanNamesForType) {
            System.out.println(beanName);
        }
    
    对工厂方法,方法返回值虽然是工厂类型,但创建并放入容器的bean的类型为工厂方法的产品的类型,id默认还是方法名(可定义):
        @Bean
        public ColorFactoryBean colorFromFactory() {
            return new ColorFactoryBean();
        }
    工厂类需要实现FactoryBean接口
    ColorFactoryBean工厂类的定义见下面代码块“ ColorFactoryBean工厂类的定义 ”
    注意:通过 applicationContext.getBean("工厂方法名,如colorFromFactory") 的方式默认获得的是产品类型的对象,想获取工厂类实例的话,就要在前面加&,如 getBean("&colorFromFactory") 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    ColorFactoryBean工厂类的定义

    public class ColorFactoryBean implements FactoryBean<Color> {
        // 返回一个Color对象,这个对象会添加进容器中
        @Override
        public Color getObject() throws Exception {
            System.out.println("ColorFactoryBean...getObject...");
            return new Color();
        }
    
        @Override
        public Class<?> getObjectType() {
            return Color.class;
        }
    
        // 控制是否单例,
        // true:此bean单实例,在容器中保存一份;false:多实例,每次获取每次new,不保存进容器
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Bean的生命周期

    bean的生命周期:
        bean的创建(construct) -> 初始化(初始化方法) -> 销毁(销毁方法)
    容器管理bean的生命周期:
        我们可以自定义初始化和销毁方法,容器在bean进行到当前生命周期的时候会调用我们自定义的这些方法
    创建对象的时机:
        单实例:容器启动的时候创建,可以设置为懒加载
        多实例:用到的时候才创建
    初始化:
        对象创建完成,并赋值好,再调用初始化方法
    销毁:
        单实例:容器关闭的时候销毁
        多实例:容器不会管理这个bean,容器不会调用销毁方法
    
    方式一:
        通过设置@Bean的initMethod、destroyMethod 指定初始化方法、指定销毁方法
        用例见下面代码块“ @Bean设置initMethod、destroyMethod ”(singleton)
    方式二:
        通过让bean实现InitializingBean接口,实现方法afterPropertiesSet来定义初始化方法
        通过让bean实现DisposableBean接口,实现方法destroy来定义销毁方法
        用例见下面代码块“ 通过接口实现bean初始化销毁 ”
    方式三:
        通过注解实现
        @PostConstruct:
            在bean创建完成并且属性赋值完成后,执行初始化
            作用域:方法(bean的初始化方法)
        @PreDestroy:
            在bean将要被从容器中移除之前,执行销毁
            作用域:方法(bean的销毁方法)
        用例见下面代码块“ 注解实现bean初始化销毁 ”
        实现原理见下面代码块“ Spring底层使用BeanPostProcessor ”
    方式四:
        通过实现bean的后置处理器接口BeanPostProcessor实现,后置处理器作用是在bean初始化前后进行一些处理工作
        org.springframework.beans.factory.config.BeanPostProcessor
        接口方法:
            postProcessBeforeInitialization(用于初始化前):
                运行时机是在前三种bean的初始化方式运行之前(initMethod、afterPropertiesSet、@PostConstruct)
            postProcessAfterInitialization(用于初始化后):
                运行时机是在前三种bean的初始化方式运行之后(initMethod、afterPropertiesSet、@PostConstruct)
        注意:BeanPostProcessor接口的实现类是单独的,不是bean类实现的,这不同于前面的实现方式
        注意:BeanPostProcessor接口的两个方法都是针对初始化的,没有销毁的方法
        注意:后置处理器的接口不用跟bean产生直接联系,后置处理器实现类创建并放入容器后会自动在bean的初始化过程中发挥作用
        用例见下面代码块“ 后置处理器接口实现初始化 ”
    
    几种方式的运行顺序:
        见下面代码块“ 初始化注销方式执行顺序 ”
    
    bean初始化过程源码:
        见下面代码块“ bean初始化过程源码 ”
    
    Spring底层对BeanPostProcessor的使用:
        见下面代码块“ Spring底层使用BeanPostProcessor ”
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    @Bean设置initMethod、destroyMethod

    // 实体类
    public class Car {
        public Car() {
            System.out.println("car constructor");
        }
        public void init() {
            System.out.println("car ... init .... ");
        }
        public void destroy() {
            System.out.println("car ... destroy .... ");
        }
    }
    
    // 组建注册
    @Bean(initMethod = "init", destroyMethod = "destroy")
    public Car car() {
        return new Car();
    }
    
    // 测试用例
    @Test
    public void test01() {
        // 这里会输出 car constructor
        AnnotationConfigApplicationContext applicationContext =
                new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
        // 这里会输出 car ... init ....
        System.out.println("容器创建完成");
        applicationContext.close();
        // 这里会输出 car ... destroy ....
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    通过接口实现bean初始化销毁

    // 输出内容与 “ @Bean设置initMethod、destroyMethod ” 代码块中的一样
    @Component
    public class Cat implements InitializingBean, DisposableBean {
        public Cat() {
            System.out.println("cat constructor");
        }
        @Override
        public void destroy() throws Exception {
            System.out.println("cat...destroy...");
        }
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("cat...afterPropertiesSet...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    注解实现bean初始化销毁

    // 输出内容与 “ @Bean设置initMethod、destroyMethod ” 代码块中的一样
    @Component
    public class Dog {
        public Dog() {
            System.out.println("Dog constructor");
        }
        // 对象创建并赋值之后调用
        @PostConstruct
        public void init() {
            System.out.println("Dog ... PostConstruct ...");
        }
        // 容器移除对象之前
        @PreDestroy
        public void destroy() {
            System.out.println("Dog ... PreDestroy ...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    后置处理器接口实现初始化

    // 在初始化前后进行处理
    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
        public MyBeanPostProcessor() {
            System.out.println("MyBeanPostProcessor...constructor...");
        }
        // 参数bean:容器刚创建的实例
        // 参数beanName:bean的名字
        // 返回值:要返回的实例,默认是直接返回bean,我们也可以返回经过处理后的
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("MyBeanPostProcessor...postProcessBeforeInitialization...");
            return bean;
        }
        // 参数、返回值含义:同上
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("MyBeanPostProcessor...postProcessAfterInitialization...");
            return bean;
        }
    }
    /*
    * MyBeanPostProcessor 初始化的输出: 
    *     MyBeanPostProcessor...constructor...
    *     MyBeanPostProcessor...postProcessBeforeInitialization...
    *     MyBeanPostProcessor...postProcessAfterInitialization...
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    初始化注销方式执行顺序

    // 这里的bean以cat为例
    cat constructor  // cat构造方法
    MyBeanPostProcessor...postProcessBeforeInitialization...  // 后置处理器,初始化前的方法
    cat...@PostConstruct注解指定的方法
    cat...InitializingBean接口的afterPropertiesSet方法
    cat...@Bean注解的initMethod参数
    MyBeanPostProcessor...postProcessAfterInitialization...  // 后置处理器,初始化后的方法
    容器创建完成
    cat...@PreDestroy注解指定的方法
    cat...DisposableBean接口的destroy方法
    cat...@Bean注解的destroyMethod参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    bean初始化过程源码

    populateBean(beanName, mbd, instanceWrapper) // 为bean的属性赋值
    initializeBean(beanName, exposedObject, mbd) // 初始化bean
    {
        applyBeanPostProcessorsBeforeInitialization(bean, beanName); // 后置处理器,初始化前方法
        {
            遍历得到容器中所有的BeanPostProcessor,
            挨个执行其 postProcessBeforeInitialization(result, beanName) 方法,
            一旦有BeanPostProcessor的此方法返回null,就不会再执行后面的BeanPostProcessor,
        }
        invokeInitMethods(beanName, wrappedBean, mbd); // 执行初始化
        applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); // 后置处理器,初始化后方法
        {
            过程类似 applyBeanPostProcessorsBeforeInitialization 
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Spring底层使用BeanPostProcessor

    Spring底层有很多对BeanPostProcessor的使用
    应用举例:
        ApplicationContextAwareProcessor:
            是BeanPostProcessor的子接口,我们的bean实现此接口后可以注入Spring的应用上下文applicationContext
            用例及原理见下面代码块“ 使用ApplicationContextAwareProcessor ”
        AsyncAnnotationBeanPostProcessor:
            用于@Async注解的处理
        ApplicationContextAwareProcessor:
            可以帮我们组件中注入IOC容器对象
        BeanValidationPostProcessor:
            对象创建完给bean赋值后,进行数据校验
        InitDestroyAnnotationBeanPostProcessor:
            处理@PostConstruct和@PreDestroy注解的
            原理:InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization方法(对应@PostConstruct)和postProcessBeforeDestruction方法(对应@PreDestroy)
        AutowiredAnnotationBeanPostProcessor:
            在对象创建完后处理@Autowired标注的内容,将组件从IOC中拿出来进行注入
    
    Spring底层用BeanPostProcessor实现了:
        bean赋值,注入其他组件,@Autowired,生命周期注解功能,@Async,xxx BeanPostProcessor
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用ApplicationContextAwareProcessor

    @Component
    public class Dog implements ApplicationContextAware {
        private ApplicationContext applicationContext;
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            // applicationContext 就是IOC容器对象
            this.applicationContext = applicationContext;
        }
    }
    // 原理:
    //    ApplicationContextAwareProcessor类的postProcessBeforeInitialization方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @Bean的注解形式

        
            
            
        
    
    获取对象:
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Person person = (Person) applicationContext.getBean("person");
    指定对象是单实例还是多实例:中 scope="singleton" 或 scope="prototype"
    默认对象是单实例,设置懒加载:中 lazy-init="true"
    指定初始化方法:中 init-method="",方法必须不能带参数,但可以抛异常
    指定销毁方法:中 destroy-method="",方法必须不能带参数,但可以抛异常
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    @Bean相关注解

    @Scope

    定义bean的作用域和生命过程
    作用域:类,方法
    
    用例:在@Bean修饰的方法上加@Scope,指定方法产生的对象是单实例还是多实例
    
    参数 value 取值:
        singleton:单实例(默认值),= ConfigurableBeanFactory#SCOPE_SINGLETON
            默认在IOC容器启动时调用方法创建对象放进容器,以后每次获取该类型对象都直接从容器中拿(map.get)
            可以配置为懒加载,即容器启动的时候不创建对象,而是在第一次使用(获取)这个对象的时候创建对象并初始化,放进容器,以后每次使用都用同一个这个对象,使用@Lazy
        prototype:多实例,= ConfigurableBeanFactory#SCOPE_PROTOTYPE
            IOC容器启动时不会调用方法创建对象,对象也不会放进容器,而是我们每次获取对象时会调用方法创建一个新的给我们
        request:同一次请求创建一个实例,= org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
        session:同一个session创建一个实例,= org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
    注意:request和session开发中不会使用
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    @Lazy

    用于对象的懒加载,只针对单例有效
    
    因为Spring的对象默认在启动时就要集中创建出来并放入IOC容器中,导致启动时会消耗大量资源,所以有了懒加载
    
    用例:在@Bean修饰的方法上加@Lazy
    
    • 1
    • 2
    • 3
    • 4
    • 5

    @PostConstruct

    用在bean类中的方法上,用于指定初始化方法
    
    • 1

    @PreDestroy

    用在bean类中的方法上,用于指定销毁方法
    
    • 1

    @Component

    表名是组件,被扫描到后会被创建并放入IOC容器中
    @Component及其子注解,注册的对象的名字默认是“首字母小写的类名”
    
    • 1
    • 2

    @Controller

    表名是接口类,@Component 的子注解
    
    • 1

    @Service

    表名是服务层类,@Component 的子注解
    
    • 1

    @Repository

    表名是持久层类,@Component 的子注解
    现在持久层框架一般是用 Mybatis 或 Mybatis-Plus,@Repository 用 @Mapper 代替
    
    • 1
    • 2

    @Configuration

    表名是配置类,@Component 的子注解
    
    • 1

    @Import

    作用域:类
    
    方法一:
        @Import({要注册进容器中的组件类.class}):容器中就会自动注册这个组件,id默认是组件类的全限定名
    使用举例:
        @Configuration
        @Import({Color.class})
        public class MainConfig2 {}
    
    方法二:
        @Import(ImportSelector实现类.class),ImportSelector接口,方法selectImports,方法返回需要导入的组件的全限定名的字符串数组
    使用举例:
        @Configuration
        @Import({MyImportSelector.class})
        public class MainConfig2 {}
    ImportSelector实现类代码见下面代码块
    org.springframework.context.annotation.ImportSelector
    
    方法三:
        @Import(ImportBeanDefinitionRegistrar实现类.class):BeanDefinition注册类,把所有需要添加到容器中的bean,可以调用 ImportBeanDefinitionRegistrar 的 registerBeanDefinition 方法手动注册进来
    使用举例:
        @Configuration
        @Import({MyImportBeanDefinitionRegistrar.class})
        public class MainConfig2 {}
    见下面代码块“ ImportBeanDefinitionRegistrar实现类 ”
    
    
    注意:使用ImportSelector实现类的方式,实现方法selectImports的返回值不能是null会报错,可以是空字符串数组
    注意:如果类上还有 @Conditional({Condition实现类.class}) 有条件地注册,如果Condition实现类中的条件不符合,这个类就不会被执行,@Import也不会执行,相应的类不会被注册进容器
    注意:由于MyImportSelector实现了ImportSelector接口,所以此类的对象不会注册进容器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    ImportSelector实现类

    public class MyImportSelector implements ImportSelector {
        // AnnotationMetadata:包含当前标注@Import注解的类的所有注解信息
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // 返回值不能是null,会报错
            return new String[]{"cn.louzen.bean.Color", "cn.louzen.bean.Red", "cn.louzen.bean.Person"};
        }
    
        @Override
        public Predicate<String> getExclusionFilter() {
            return ImportSelector.super.getExclusionFilter();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ImportBeanDefinitionRegistrar实现类

    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
            ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry, importBeanNameGenerator);
        }
    
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            boolean definition = registry.containsBeanDefinition("cn.louzen.bean.Red");
            boolean definition2 = registry.containsBeanDefinition("cn.louzen.bean.Blue");
    
            if (definition && definition2) {
                // 指定Bean的定义信息(Bean的类型、Scope等)
                RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
                // 注册Bean到容器,这里可以指定组件id
                registry.registerBeanDefinition("rainBow", beanDefinition);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注入条件

    按照条件判断要不要创建某对象并放入IOC容器

    @Conditional

    按照一定条件判断,满足条件给容器中注册bean
    作用域:类,方法,这里是可以被注册进容器中的类、方法(被@Component及其子注解、@Bean等修饰的类、方法)
    参数:
        value:
            类型:Class[]  // 这里要用到Condition接口的实现类
    
    使用举例:(用在方法上)
        @Conditional({LinuxCondition.class})
        @Bean("linus")
        public Person person02() {
            return new Person("linus", 48);
        }
    使用举例:(用在类上)
        @Conditional({WindowsCondition.class})
        @Configuration
        public class MainConfig2 {}
    
    Condition 是一个接口(org.springframework.context.annotation.Condition),
    接口返回true就运行其所修饰的类、方法,否则不运行
    有一个方法 boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata):
        context:判断条件能使用的上下文(环境)
        medadata:注解信息
    Condition实现类见下面代码段
    
    注意:Condition的matches方法的逻辑不仅只返回true或false,还可以在逻辑中创建bean并放进容器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    Condition实现类举例:

    public class LinuxCondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            // TODO 判断是否Linux系统
            // 1. 能获取到IOC使用的beanfactory
            ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
            // 2. 获取类加载器
            ClassLoader classLoader = context.getClassLoader();
            // 3. 获取当前环境信息
            Environment environment = context.getEnvironment();
            // 4. 获取容器中注册的类(可以创建新对象并放入这里面)
            BeanDefinitionRegistry registry = context.getRegistry();
    
            // 这里通过判断环境变量决定方法返回值,也可以通过判断context中的其他的内容判断
            String property = environment.getProperty("os.name");
            return (Objects.nonNull(property) && property.contains("linux"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    @ConditionalOnClass

    @ConditionalOnBean

    @ConditionalOnSingleCandidate

    @ConditionalOnMissingBean

    @ConditionalOnProperty

    不配置havingValue的值
    1.假如没有配置这个name的配置
    1.1@ConditionalOnProperty(prefix = “app”,name=“name”,matchIfMissing = false)
    假如没有配置这个name的配置,若matchIfMissing为false,则不会加载此配置类
    假如配置了这个name的配置,若matchIfMissing为false,则会加载此配置类
    
    1.2@ConditionalOnProperty(prefix = “app”,name=“name”,matchIfMissing = true)
    假如没有配置这个name的配置,若matchIfMissing为true,则仍会加载此配置类
    假如配置了这个name的配置,若matchIfMissing为true,则会加载此配置类
    总结,若未配置havingValue的值,matchIfMissing为true则无论是否有配置都会加载配置类,matchIfMissing为false,有配置加载类,无配置不加载类。
    
    配置错误的havingValue的值
    2.1@ConditionalOnProperty(prefix = “app”,name=“name”,havingValue = “name1”,matchIfMissing = false)
    假如配置了错误的name值,若matchIfMissing为false,配置类不会加载
    假如配置了正确的name值,若matchIfMissing为false,配置类会加载
    
    2.2@ConditionalOnProperty(prefix = “app”,name=“name”,havingValue = “name1”,matchIfMissing = true)
    假如配置了错误的name值,若matchIfMissing为true,配置类不会加载
    假如配置了正确的name值,若matchIfMissing为true,配置类会加载
    总结,只要配置了正确的havingValue值,无论matchIfMissing怎么设置,都会加载,只要配置的havingValue值不正确,无论
    matchIfMissing怎么设置,都不会加载。
    ————————————————
    版权声明:本文为CSDN博主「岸河」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_42145871/article/details/118310230
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    添加配置

    配置类

    @Configuration

    @EnableConfigurationProperties

    @ConfigurationProperties

    @AutoConfigureBefore

    @AutoConfigureAfter

    指定配置文件

    @PropertySource

    指定配置文件
    作用域:类
    可以放在任何组件(@Component或其子注解标注的类)上,对应配置文件就会生效
    使用举例:@PropertySource("classpath:/blue.properties")
    与@Value配合,@PropertySource指定配置文件,@Value将配置文件中的配置赋值给属性
    
    • 1
    • 2
    • 3
    • 4
    • 5

    动态配置

    Profile:
        Spring为我们提供的可以根据当前环境,动态地激活和切换一系列组件的功能
    比如,我们可能有开发环境、测试环境、生产环境等,每个环境的数据源都不一样,或不同环境希望暴露不同的类等需求
    
    • 1
    • 2
    • 3

    @Profile

    指定组件在哪个环境的情况下才能被注册到容器中;不指定,任何环境下都能注册这个组件
    作用域:类,方法
    属性:value
    
    1、@Profile作用于方法(被@Bean标注的方法),只有这个环境被激活的时候组件才能被注册进容器中
        使用样例见下面“ @Profile作用于@Bean方法 ”
    2、@Profile作用于类(被@Configuration标注的配置类),指定环境被激活时整个配置里所有的配置都会生效
    3、没有标注@Profile的类和@Bean方法,任何情况都会加载(@Bean方法加载的前提是其所在的配置类会被加载)
    注意:默认是default环境:@Profile("default"),如果设置了环境参数default也就失效了
    注意:类上的@Profile与类中@Bean方法上的@Profile可以同时存在。若环境为test:若类上为@Profile("dev"),则整个类和类中@Bean方法都不会被加载进IOC容器;若类上为@Profile("test"),类中两个@Bean一个@Profile("test")另一个@Profile("dev"),则类和test的方法返回的对象会被加载进IOC,dev的@Bean方法不会执行
    
    激活@Profile:
        1、运行参数:项目运行时的java命令添加参数,-Dspring.profiles.active=test,就可以激活@Profile("test"),其标注的类、方法返回的对象就会被注册到容器中
        2、代码方式激活:见下面“ 代码激活@Profile ”
    注意:@Profile要放在可以被注册进IOC容器的类、方法上面(被标注@Component及其子注解、@Bean)才会生效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    @Profile作用于@Bean方法
    @PropertySource("classpath:/dbconfig.properties")
    @Configuration
    public class MainConfigOfProfile implements EmbeddedValueResolverAware {
    
        @Value("${db.user}")
        private String user;
    
        private String driverClass;
    
        private StringValueResolver resolver;
    
        @Override
        public void setEmbeddedValueResolver(StringValueResolver resolver) {
            this.resolver = resolver;
            driverClass = this.resolver.resolveStringValue("${db.driverClass}");
        }
    
        @Profile("default")
        @Bean("defaultDataSource")
        public DataSource dataSourceDefault(@Value("${db.password}") String psw) throws PropertyVetoException {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setUser(user);
            dataSource.setPassword(psw);
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/pig_job");
            dataSource.setDriverClass(driverClass);
            return dataSource;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    代码激活@Profile
    public class IOCTestProfile {
        @Test
        public void test02() {
            // 1、创建一个 applicationContext(无参构造)
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
            // 2、设置需要激活的环境
            applicationContext.getEnvironment().setActiveProfiles("test", "dev");
            // 3、注册主配置类
            applicationContext.register(MainConfigOfProfile.class);
            // 4、启动刷新容器
            applicationContext.refresh();
    
            String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
            for (String name : beanDefinitionNames) {
                System.out.println(name);
            }
            applicationContext.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    属性赋值

    @Value

    在注解中相当于标签的子标签的value属性,是给属性赋值的注解
    作用域:属性、方法、入参、注解
    Spring注解
    使用形式:
        1、基本数值,直接@Value("...")
        2、SpEL表达式,#{},@Value("#{key}")
        3、${}取出配置文件中的值(在运行环境里面的变量值),@Value("${key}")
    注意:在纯Spring框架下(非SpringBoot),当用${}取数据时,需要指定配置文件,两种方法,一是xml配置文件中,二是在配置类中(相当于配置文件)通过@PropertySource注解指定(在配置类中创建用配置中变量初始化属性的bean)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    自动装配

    自动装配:
       Spring利用依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值
    实现方式:
        1、@Autowired自动注入
        2、@Source和@Inject
        3、将@Autowired置于构造器、参数、方法、属性上
        4、Aware接口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    @Autowired

    默认优先按照 byType 自动注入,如果有多个,就将属性名作为id去容器中匹配并注入,如果还是有多个就会报错
    自动装配默认一定要将属性赋值好,没有在容器中找到目标对象就会报错,如果目标对象可以为空则要设置它的属性required=false
    作用域:属性、方法、构造器、参数
    注解属于Spring
    
    如果想要优先按照名称注入,就要配合 @Qualifier注解,如@Autowired () @Qualifier ( "baseDao" )
    如果不想每次都用@Qualifier对要注入的组件进行指定,可以用@Primary来指定首选的组件
    
    原理:
        AutowiredAnnotationBeanPostProcessor 后置处理器
    
    使用样例:
        1. 属性
            见下面“ @Autowired用于属性 ”
        2. 方法(set方法)
            见下面“ @Autowired用于方法 ”
        3. 构造器(有参构造器)
            见下面“ @Autowired用于构造器 ”
        4. 参数(@Bean标注的方法参数)
            见下面“ @Autowired用于参数 ”
            可用于@Bean标注的方法位置(被@Bean标注的方法的参数可以加@Autowired,也可以省略@Autowired,Spring会自动从IOC容器中找的)
    注意:虽然@Autowired可以标注在不同位置,但其本质都是将组件从IOC容器取出来并进行赋值
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    用于属性

    @Component
    public class Boss {
        @Autowired
        private Car car;
        public Car getCar() {return car;}
        public void setCar(Car car) {this.car = car;}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    用于方法

    // @Autowired标注在set方法上,Spring容器创建当前对象时就会调用它完成对属性的赋值,set方法的参数是从Spring的IOC容器中获取的
    @Component
    public class Boss {
        private Car car;
        public Car getCar() {return car;}
        @Autowired
        public void setCar(Car car) {this.car = car;}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    用于构造器

    // @Autowired标注在有参构造器上,或有参构造器的方法参数前面
    // 如果一个类的构造器只有一个有参的,则标在构造器方法上的@Autowired,或标在有参构造器参数前的@Autowired可以省略
    // 如果这个类有无参构造方法,则即使有参构造方法加了@Autowired注解Spring也不会调用,只会用无参构造方法
    @Component
    public class Boss {
        private Car car;
    //    @Autowired 唯一的有参构造方法上面或有参构造方法的入参前
        public Boss(@Autowired Car car) {
            this.car = car;
        }
        public Car getCar() {return car;}
        public void setCar(Car car) {this.car = car;}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    用于参数

    // @Bean标注的方法的参数中的对象,默认是从IOC容器中获取的,参数前的@Autowired加不加都可
    @Configuration
    public class MainConfigOfAutowired {
    	@Bean
    	public Color color(Car car) {
    		Color color = new Color();
    		color.setCar(car);
    		return color;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    @Qualifier

    配合@Autowired 就可以实现按照名称注入
    @Autowired + @Qualifier 可以达到与 @Resource 相同的效果
    注解属于Spring
    
    • 1
    • 2
    • 3

    @Primary

    作用域:类、方法
    指定组件,让Spring进行装配的时候,默认首选被注解修饰的bean
    注解属于Spring
    
    • 1
    • 2
    • 3

    @Resource

    • 默认按照 byName 的方式自动注入
    • 属性 name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型
    • @Resource装配顺序:
    默认按照 byName 的方式自动注入
    注解的参数name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型
    不支持@Primary,不支持像@Autowired一样的required=false的属性
    
    作用域:属性、set方法
    
    @Resource装配顺序:
        1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
      2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
      3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
      4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配就按照类型匹配
    
    属于J2EE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    @Inject

    用这个注解需要导入javax.Inject依赖才能用
    和@Autowired功能一样,但没有required属性
    属于J2EE
    
    • 1
    • 2
    • 3

    引入依赖

    
    <dependency>
        <groupId>javax.injectgroupId>
        <artifactId>javax.injectartifactId>
        <version>1version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Aware接口

    这是以Aware为父接口的一系列子接口,实现的机制都是后置处理器,前面讲的 BeanPostProcessor
    自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory 等等)
    自定义组件实现xxxAware接口,在创建对象的时候,会调用接口规定的方法注入相关组件,把Spring底层的一些组件注入到自定义的Bean中
    使用样例见下面代码块“ Aware接口样例 ”
    每个Aware都有对应的Processor,xxxAware的方法由xxxProcessor来运行,例如ApplicationContextAware的方法在ApplicationContextProcessor有相关逻辑负责运行
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Aware接口样例

    @Component
    public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
    
    	private ApplicationContext applicationContext;
    
    	// ApplicationContextAware 接口中的方法,用于拿到IOC容器对象
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    		System.out.println("ioc容器:" + applicationContext);
    		this.applicationContext = applicationContext;
    	}
    
    	// BeanNameAware 接口中的方法,用于拿到bean name(或 id)
    	@Override
    	public void setBeanName(String name) {
    		System.out.println("beanName = " + name);
    	}
    
    	// EmbeddedValueResolverAware接口中的方法,拿到解析器,
    	// 用于解析字符串,拿到字符串中的#{配置}、${表达式}等占位符的值
    	@Override
    	public void setEmbeddedValueResolver(StringValueResolver resolver) {
    		// os.name是配置文件中设置的值
    		String str = resolver.resolveStringValue("你好${os.name}, 我是#{18*20}");
    		System.out.println("解析后的内容:" + str);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    解决循环依赖

    @Lazy
    
    • 1

    Spring AOP

    AOP(Aspect Orient Programming):
        指在程序运行期间动态地将某段代码切入到指定方法指定位置进行运行的编程方式
        原理是动态代理(详细见下面代码块“ AOP动态代理原理 ”)
    
    AOP的代码实现:(实现样例见下面代码块“ AOP代码实现 ”)
    1、导入aop模块:Spring AOP,导入的包不是speing-aop而是spring-aspects
    2、定义一个业务逻辑类(MathCalculator),在业务逻辑运行的时候将打印日志(方法运行前、运行后、运行异常)
    3、定义一个日志切面类(LogAspects),切面类里的方法是要动态感知到业务逻辑运行到哪里然后执行一系列的切面操作                 
        切面类中有通知方法:
            前置通知 @Before(logStart):在目标方法(div)运行之前运行
            后置通知 @After(logEnd):在目标方法(div)运行结束后运行,无论方法正常还是异常结束都会调用
            返回通知 @AfterReturning(logReture):在目标方法(div)正常返回之后运行
            异常通知 @AfterThrowing(logException):在目标方法(div)运行出现异常后运行
            环绕通知 @Around:动态代理,我们手动目标方法运行(joinPoint.proceed())
    4、给切面类的目标方法标注通知注解
    5、将切面类和目标业务逻辑类(目标方法所在的类)都加入到容器中
    6、必须告诉Spring哪个类是切面类(给切面类上加一个注解@Aspect)
    7、开启基于注解的切面功能:
        xml配置:
        代码配置:在配置类上加注解@EnableAspectJAutoProxy(在Spring中有很多@EnableXXX都是用来开启某项功能某项配置的)
    
    三步:
        1、将业务逻辑组件和切面类都加入到容器中,告诉Spring哪个是切面类(@Aspect)
        2、在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
        3、开启Spring基于注解的AOP模式@EnableAspectJAutoProxy
    
    注意:切面方法如果想拿到目标方法的入参、返回值、异常等信息,就要在切面方法的入参加上JoinPoint
    注意:如果不需要JoinPoint中的信息可以从入参把它去掉,如果需要,要把JoinPoint写在参数第一位,否则Spring无法识别
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    AOP代码实现

    目标类(业务逻辑类、被加强的类)

    public class MathCalculator {
        public int div(int i, int j) {
            System.out.println("MathCalculator...div...");
            return i/j;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    切面类(要加强哪个方法、怎么加强)

    @Aspect
    public class LogAspects {
    
        // 抽取公共的切入点表达式 @Pointcut("execution(xxxx)")
        // 1、本类的切面注解引用此切入点表达式只需要在切面注解中放入方法名
        // 2、其他的切面类想引用此切入点表达式就要在切面注解中放入方法的全限定名
        // * 表示MathCalculator类中的所有方法,.. 表示任意多任意类型的参数都可
        @Pointcut("execution(public int cn.louzen.aop.MathCalculator.*(..))")
        public void pointCut() {}
    
        // @Before:在目标方法之前切入;切入点表达式:指定在哪个方法切入
        // 如果不需要JoinPoint中的信息可以从入参把它去掉,如果需要,要把JoinPoint写在参数第一位,否则Spring无法识别
        @Before("execution(public int cn.louzen.aop.MathCalculator.div(int, int))")
        // @Before("execution(public int cn.louzen.aop.MathCalculator.*(..))")
        public void logStart(JoinPoint joinPoint) {
            // 目标方法的名字
            String methodName = joinPoint.getSignature().getName();
            // 目标方法的入参
            Object[] args = joinPoint.getArgs();
            System.out.printf("%s运行。。。@Before参数列表是;{%s}\n", methodName, Arrays.asList(args));
        }
    
        // 直接用切入点表达式就不需要再写execution了
        @After("cn.louzen.aop.LogAspects.pointCut()")
        public void logEnd(JoinPoint joinPoint) {
            String methodName = joinPoint.getSignature().getName();
            System.out.printf("%s结束。。。@After", methodName);
        }
    
        // 如果不需要返回值就写成:@AfterReturning("pointCut()")
        // returning="result"是指定返回值参数名为result
        @AfterReturning(value = "pointCut()", returning = "result")
        public void logReture(JoinPoint joinPoint, Object result) {
            String methodName = joinPoint.getSignature().getName();
            System.out.printf("%s正常返回。。。@AfterReturning运行结果;{%s}\n", methodName, result.toString());
        }
    
        // 如果不需要返回值就写成:@AfterThrowing("pointCut()")
        // throwing = "exception"是指定异常的参数名为exception
        @AfterThrowing(value = "pointCut()", throwing = "exception")
        public void logException(JoinPoint joinPoint, Exception exception) {
            String methodName = joinPoint.getSignature().getName();
            System.out.printf("%s异常。。。@AfterThrowing异常信息;{%s}\n", methodName, exception);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    配置类(注解启动Spring的aop功能)

    @EnableAspectJAutoProxy
    @Configuration
    public class MainConfigOfAOP {
        // 业务逻辑类加入容器中
        @Bean
        public MathCalculator mathCalculator() {
            return new MathCalculator();
        }
        // 切面类加入容器中
        @Bean
        public LogAspects logAspects() {
            return new LogAspects();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    测试类

    public class IOCTestAOP {
        @Test
        public void test01() {
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
            // 1、不要自己new对象,要从容器中取才会有Spring的aop加强效果
            // MathCalculator mathCalculator = new MathCalculator();
            MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
            mathCalculator.div(1, 0);
            applicationContext.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    运行结果(正常)

    div运行。。。@Before参数列表是;{[1, 1]}
    MathCalculator...div...
    div正常返回。。。@AfterReturning运行结果;{1}
    div结束。。。@After
    
    • 1
    • 2
    • 3
    • 4

    运行结果(异常)

    div运行。。。@Before参数列表是;{[1, 0]}
    MathCalculator...div...
    div异常。。。@AfterThrowing异常信息;{java.lang.ArithmeticException: / by zero}
    div结束。。。@After
    
    • 1
    • 2
    • 3
    • 4

    AOP动态代理原理

    有两种情况:
        目标类实现了接口,使用JDK动态代理:
        目标类没有实现接口,使用CGLIB动态代理:
    
    AOP原理:【看给容器中注册了什么组件,这个组件什么时候工作,这个组件的功能是什么】
    1、@EnableAspectJAutoProxy注解
        @Import({AspectJAutoProxyRegistrar.class}),给容器中导入AspectJAutoProxyRegistrar
            AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,详见上面“ SpringIoC->组件注册->@Import ”的内容,将指定组件注册进容器中
                给容器中注册 AnnotationAwareAspectJAutoProxyCreator 对象(自动代理创建器)
    2、AnnotationAwareAspectJAutoProxyCreator,自动代理创建器
        类的继承关系:
        AnnotationAwareAspectJAutoProxyCreator 
            -> AspectJAwareAdvisorAutoProxyCreator
                -> AbstractAdvisorAutoProxyCreator
                    -> AbstractAutoProxyCreator 
                        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware 
                    关注这两个接口,后置处理器(在bean初始化完成前后做的事情)、自动装配BeanFactory
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    SpringMVC

    @Controller

    @RestController

    @RequestMapping

    • produces属性

    @Get/Post/Put/Delete+Mapping

    get 幂等
    post 非幂等
    put 幂等
    delete 幂等
    
    • 1
    • 2
    • 3
    • 4

    @RequestParam

    如果请求url后面跟着多个同名参数,形如:url?param=1¶m=2¶m=3
    我们Controller方法要用@RequestParam来接
    1. 如果我们用method(@RequestParam Integer param)来接,param会匹配第一个参数值,即 param=1
    2. 如果我们用method(@RequestParam Integer[] params 或 List params)来接,params=[1,2,3]
    
    • 1
    • 2
    • 3
    • 4

    @RequestBody

    @PathVariable

    Mybatis

    @MapperScan

    @Param

    @Select

    @Update

    @Insert

    Mybatis-Plus

    @TableName

    @TableId

    @TableField

    事务

    @Transactional

    遇到指定异常就回滚:@Transactional(rollbackFor = Exception.class)
    
    • 1

    异常

    @ExceptionHandler

    • 无法处理自定义异常(待求证)

    SpringBoot JUnit5

    SpringBoot自2.4开始内置JUnit5,之前版本用JUnit4
    
    • 1

    @SpringBootTest

    开启SpringBoot测试
    作用域:类,放在类名上
    SpringBoot自2.4版本开始用JUnit5,启用测试只需要这一个注解即可,不同于JUnit4
    
    这是复合注解:
    @BootstrapWith(SpringBootTestContextBootstrapper.class)
    @ExtendWith(SpringExtension.class)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    @Test

    JUnit5的@Test注解在jupiter包下,JUnit4的@Test不是,注意区分
    
    • 1

    @RepeatedTest

    可重复测试,比如 @RepeatedTest(5) 就是重复跑 5 次
    有了这个注解可以不用写@Test
    作用域:注解、方法
    
    • 1
    • 2
    • 3

    DisplayName

    用于展示测试结果的名称,在控制台更好辨认
    作用域:类、方法
    
    • 1
    • 2

    @BeforeEach

    替换JUnit4的@Before
    本类中每个@Test测试方法运行前都要先运行本注解修饰的方法
    注意:运行单个方法是点方法旁边的运行符号,或选中方法名右键运行
    
    • 1
    • 2
    • 3

    @AfterEach

    替换JUnit4的@After
    本类中每个@Test测试方法运行后都会运行本注解修饰的方法
    注意:与上类似
    
    • 1
    • 2
    • 3

    @BeforeAll

    替换JUnit4的@BeforeClass
    运行整个测试类前都要先运行本注解修饰的方法
    注意:运行整个测试类是点类名旁边的运行符号,或选中类名右键运行,被此注解修饰的方法必须都是静态的
    
    • 1
    • 2
    • 3

    @AfterAll

    替换JUnit4的@AfterClass
    运行整个测试类后都会运行本注解修饰的方法
    注意:与上类似
    
    • 1
    • 2
    • 3

    @Tag

    替换JUnit4的@Category
    表示单元测试类别
    
    • 1
    • 2

    @Disabled

    替换JUnit4的@Ignore
    表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
    在执行整个测试类时不会执行这个方法
    
    • 1
    • 2
    • 3

    @Timeout

    表示测试方法运行如果超过了指定时间将会返回错误
    
    • 1

    @ExtendWith

    为测试类或测试方法提供扩展类引用
    JUnit5的此注解替代了JUnit4中的@RunWith
    
    • 1
    • 2

    @Nested

    嵌套测试
    作用域:类
    外层的@Test不能驱动内层的@BeforeEach、@AfterEach、@BeforeAll、@AfterAll这些注解的方法运行;内层的@Test可以
    
    • 1
    • 2
    • 3

    @ParameterizedTest

    用于开启参数化测试
    被注释的方法的参数列表中可以定义入参
    作用域:方法、注解
    配合@ValueSource、@NullSource、@EnumSource、@CsvFileSource、@MethodSource将这些注解中的数据放进被@ParameterizedTest注释的方法参数中
    
    • 1
    • 2
    • 3
    • 4

    @ValueSource

    为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
    
    • 1

    @NullSource

    表示为参数化测试提供一个null的入参
    
    • 1

    @EnumSource

    表示为参数化测试提供一个枚举入参
    
    • 1

    @CsvFileSource

    表示读取指定CSV文件内容作为参数化测试入参
    
    • 1

    @MethodSource

    表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
    
    • 1

    Lombok

    @Slf4j

    @Data

    @Setter

    @Getter

    @AllArgsConstructor

    @NoArgsConstructor

  • 相关阅读:
    vmware入门之运行RHEL虚拟机
    【Linux 驱动基础】驱动程序基石
    Hive3.1.3 安装配置
    linux64/ubuntu20.04安装NVIDIA驱动详细过程
    golang学习笔记——编写 FizzBuzz 程序
    k8s 配置存储之 Configmap & secret
    看完这篇 教你玩转渗透测试靶机Vulnhub——DriftingBlues-7
    Python变量与常量
    stm32cubemx hal学习记录:TIMER输入捕获
    文本情感倾向查询易语言代码
  • 原文地址:https://blog.csdn.net/Louzen/article/details/126506810