• 注解深入&动态代理




    1. 创建Module,引入依赖

     <dependencies>
            
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-contextartifactId>
                <version>5.1.2.RELEASEversion>
            dependency>
            
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-testartifactId>
                <version>5.1.2.RELEASEversion>
            dependency>
            
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.12version>
            dependency>
            
            <dependency>
                <groupId>org.yamlgroupId>
                <artifactId>snakeyamlartifactId>
                <version>1.25version>
            dependency>
    
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>5.1.47version>
            dependency>
            
            <dependency>
                <groupId>c3p0groupId>
                <artifactId>c3p0artifactId>
                <version>0.9.1.2version>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>1.18.8version>
            dependency>
        dependencies>
    
    • 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

    2. 创建核心配置类


    com.execise包里创建核心配置类AppConfig

    
    @Configuration
    @ComponentScan("com.execise")
    public class AppConfig {
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3.创建Service


    接口

    package com.execise.service;
    
    public interface UserService {
        void add();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    实现

    package com.execise.service.impl;
    
    import com.execise.service.UserService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
        public void add() {
            System.out.println("调用了UserServiceImpl的add方法~!~!");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.创建单元测试

    
    package com.execise.test;
    
    import com.execise.config.AppConfig;
    import com.execise.service.UserService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AppConfig.class)
    public class TestUserServiceImpl {
    
        //把工厂给注入进来
        @Autowired
        private ApplicationContext context;
    
    
        @Test
        public void testBeanNames(){
    
            String[] names = context.getBeanDefinitionNames();
            for (String name : names) {
                System.out.println("name = " + name);
            }
    
        }
    }
    
    
    • 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

    @ComponentScan


    这个注解的作用就是用来扫描指定包下的所有类。如果哪个类身上打了注解(@Controller | @Service | @Repository | @Component),就被spring给管理起来。默认情况下Spring管理这些对象的时候,**他们的id名字就是类的名字,但是第一个字母小写。**我们是否可用修改这种命名策略呢?


    BeanName生成策略


    说明

    默认的BeanName生成策略:

    • 如果注册bean时指定了id/name,以配置的id/name作为bean的名称

    • 如果没有指定id/name,则以类名首字母小写作为bean的名称


    在模块化开发中,多个模块共同组成一个工程。

    • 可能多个模块中,有同名称的类,按照默认的BeanName生成策略,会导致名称冲突。

    • 这个时候可以自定义beanname生成策略解决问题


    @ComponentScannameGenerator属性,可以配置自定义的BeanName生成策略,步骤:

    1. 创建Java类,实现BeanNameGenerator接口,定义BeanName生成策略

    2. 在注解@ComponentScan中,使用nameGenerator属性指定生成策略即可


    示例


    1. 创建Java类,实现BeanNameGenerator接口,定义BeanName生成策略
    package com.execise.demo1_componentscan;
    
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanNameGenerator;
    
    /*
        自定义的id生成策略
            1. id的生成靠的是generateBeanName的返回值
            2. 这个方法的返回值是什么,id就是什么。
     */
    public class MyBeanNameGenerator implements BeanNameGenerator {
    
        /**
         * 用来创建id值
         * @param beanDefinition 表示(包装)现在正在扫描(处理)的类
         * @param beanDefinitionRegistry
         * @return 对象的id值
         */
        public String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry beanDefinitionRegistry) {
    
            //1 得到现在正在扫描到的这个类的全路径地址:
            String className = beanDefinition.getBeanClassName();
    
            System.out.println("className = " + className);
    
            //2. 返回的是全路径。
            return className;
        }
    }
    
    
    • 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

    1. 在注解@ComponentScan中,使用nameGenerator指定生成策略
    @Configuration
    @ComponentScan(value = "com.execise" , nameGenerator = MyBeanNameGenerator.class) //扫描包 &  命名策略
    public class AppConfig {
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 执行上面的单元测试

    扫描规则过滤器


    说明


    @ComponentScan默认的扫描规则:

    • 扫描指定包里的@Component及衍生注解(@Controller,@Service,@Repository)配置的bean

    @ComponentScan注解也可以自定义扫描规则,来包含或排除指定的bean。步骤:

    1. 创建Java类,实现TypeFilter接口,重写match方法

      • 方法返回boolean。true表示匹配过滤规则;false表示不匹配过滤规则
    2. 使用@ComponentScan注解的属性,配置过滤规则:

      • includeFilter:用于包含指定TypeFilter过滤的类,符合过滤规则的类将被扫描

      • excludeFilter:用于排除指定TypeFilter过滤的类,符合过滤规则的类将被排除


    示例1-根据注解过滤


    哪个类身上有指定的注解,那么就忽略它 , 不扫描。这是按照注解的名字来忽略的。

    
    @Configuration // 这是核心配置类
    @ComponentScan(value = "com.execise" , nameGenerator = MyBeanNameGenerator.class,
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION , classes = Service.class)
    public class AppConfig {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例2-根据指定类过滤

    
    @Configuration // 这是核心配置类
    @ComponentScan(value = "com.execise" , nameGenerator = MyBeanNameGenerator.class,
        excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE , classes = UserServiceImpl.class) // 按照类不扫描
    public class AppConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例3-自定义过滤


    1. 编写过滤器,实现TypeFilter接口,重写match方法
    package com.execise.demo1_componentscan;
    
    import org.springframework.core.type.ClassMetadata;
    import org.springframework.core.type.classreading.MetadataReader;
    import org.springframework.core.type.classreading.MetadataReaderFactory;
    import org.springframework.core.type.filter.TypeFilter;
    
    import java.io.IOException;
    
    /*
        这是自定扫描(包含|排除)的规则
     */
    public class MyTypeFilter  implements TypeFilter {
    
        /**
         * 用于设置什么样的规则就排除|包含 类
         * @param metadataReader
         * @param metadataReaderFactory
         * @return true: 即表示匹配规则, false: 即表示不匹配规则。
         * @throws IOException
         */
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    
            //1. 得到现在正要被检查的类的元数据
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
    
            //2. 获取现在的类的全路径名字
            String className = classMetadata.getClassName();
    
            //如果哪个类的名字包含了User,那么就返回true
            return className.contains("User");
        }
    }
    
    
    • 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

    1. 使用注解@ComponentScan,配置过滤规则
    @ComponentScan(value = "com.execise" , nameGenerator = MyBeanNameGenerator.class,
        excludeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM , classes = MyTypeFilter.class) // 按照自定义的规则来忽略类不扫描!
    )
    public class AppConfig {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 执行单元测试,看看打印的结果。

    @PropertySource


    yml配置文件介绍

    以前学习过的常用配置文件有xmlproperties两种格式,但是这两种都有一些不足:

    • properties

      • 优点:键值对的格式,简单易读

      • 缺点:不方便表示复杂的层级

    • xml

      • 优点:层次结构清晰

      • 缺点:配置和解析语法复杂

    springboot采用了一种新的配置文件:yaml(或yml),它综合了xmlproperties的优点。

    • yaml are't markup language => yaml

    • 使用空格表示层次关系:相同空格的配置项属于同一级

    • 配置格式是key:空格value,键值对之间用:空格表示


    yaml文件示例:

    jdbc:
    	driver: com.mysql.jdbc.Driver # 注意:英文冒号后边必须有一个空格
    	url: jdbc:mysql:///spring
    	username: root
    	password: root
    	
    jedis:
    	host: localhost
    	port: 6379
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    使用@PropertySource加载yml


    说明

    @PropertySource可以使用factory属性,配置PropertySourceFactory,用于自定义配置文件的解析


    步骤:

    1. 创建yaml文件:application.yml

    2. 导入依赖snakeyaml,它提供了解析yml文件的功能

    3. 创建Java类,实现PropertySourceFactory接口,重写createPropertySource方法

    4. 使用@PropertySource注解,配置工厂类


    示例


    1. resources目录里创建yaml文件:application.yml
    jdbc:
    	driver: com.mysql.jdbc.Driver # 注意:英文冒号后边必须有一个空格
    	url: jdbc:mysql:///spring
    	username: root
    	password: root
    	
    jedis:
    	host: localhost
    	port: 6379
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1. pom.xml增加导入依赖snakeyaml,它提供了解析yml文件的功能
    <dependency>
        <groupId>org.yamlgroupId>
        <artifactId>snakeyamlartifactId>
        <version>1.25version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 创建Java类,实现PropertySourceFactory接口,重写createPropertySource方法
    public class YamlSourceFactory implements PropertySourceFactory {
        /**
         * 解析yaml配置文件
         * @param name 名称
         * @param resource 配置文件EncodedResource对象 
         * @return PropertySource
         */
        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            //1. 创建yaml解析的工厂
            YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
            //2. 设置要解析的资源内容
            factoryBean.setResources(resource.getResource());
            //3. 把资源文件解析成Properties对象
            Properties properties = factoryBean.getObject();
            //4. 把properties封装成PropertySource对象并返回
            return new PropertiesPropertySource("application", properties);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1. 使用@PropertySource注解,配置工厂类
    @Configuration("appConfig")
    @PropertySource(value = "application.yml" , factory = YamlSourceFactory.class) //导入yml文件
    public class AppConfig {
    
     @Value("${jdbc.driver}")
        private String driver;
    
        @Value("${jdbc.url}")
        private String url;
    
        @Value("${jdbc.username}")
        private String username;
    
        @Value("${jdbc.password}")
        private String password;
    
        public void show(){
            System.out.println("driver = " + driver);
            System.out.println("url = " + url);
            System.out.println("username = " + username);
            System.out.println("password = " + password);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1. 测试
    package com.execise.test;
    
    import com.execise.config.AppConfig;
    import com.execise.service.UserService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AppConfig.class)
    public class TestAppConfig {
    
        @Autowired
        private AppConfig config;
    
        @Test
        public void testShow(){
            config.show();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    @Import


    注册bean的方式


    如果要注解方式配置一个bean,可以如下方式:

    • 在类上使用@Component,@Controller, @Service, @Repository:只能用于自己编写的类上,jar包里的类不能使用(比如ComboPooledDataSource)
    • 在方法上使用@Bean:把方法返回值配置注册成bean到IoC容器,通常用于注册第三方jar里的bean
    • 在核心配置类上使用@Import
      • @Import(类名.class),注册的bean的id是全限定类名
      • @Import(自定义ImportSelector.class):把自定义ImportSelector返回的类名数组,全部注册bean
      • @Import(自定义ImportBeanDefinitionRegister.class):在自定义ImportBeanDefinitionRegister里手动注册bean

    ImportSelector导入器


    示例1-直接导入注册bean

    使用@import来到一个类

    
    @Configuration
    @ComponentScan("com.execise"@Import(Teacher.class) 
    public class AppConfig {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    示例2-使用ImportSelector注册bean


    导入器

    package com.execise.demo3_import;
    
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;
    
    //自定义导入选择器
    public class MyImportSelector implements ImportSelector {
    
        /**
         * 用于控制到底导入那个类到spring的容器|工厂里面
         * @param annotationMetadata
         * @return 数组,数组里面包含类的全路径地址。
         */
        @Override
        public String[] selectImports(AnnotationMetadata annotationMetadata) {
            return new String[]{Teacher.class.getName() , Student.class.getName()};
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    示例

    @Configuration
    @ComponentScan("com.execise"@Import(MyImportSelector.class)
    public class AppConfig {
        ...
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    示例3-ImportSelector的高级使用


    说明

    pringboot框架里有大量的@EnableXXX注解,底层就是使用了ImportSelector解决了模块化开发中,如何启动某一模块功能的问题


    例如:

    我们开发了一个工程,要引入其它的模块,并启动这个模块的功能:把这个模块的bean进行扫描装载


    在这里插入图片描述


    步骤:

    创建一个Module:module_a

    1. 在包com.a里创建几个Bean

    2. 创建核心配置文件AConfig,扫描com.a

    3. 定义一个ImportSelector:AImportSelector,导入AConfig

    4. 定义一个注解@EnableModuleA

    在我们的Module的核心配置文件AppConfig上增加注解:@EnableModuleA

    1. 引入module_a的坐标

    2. 测试能否获取到Module a里的bean


    第一步:创建新Module:module_a

    
    
    <groupId>com.execisegroupId>
        <artifactId>spring_module_aartifactId>
        <version>1.0-SNAPSHOTversion>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述


    1. 在包com.a.beans里创建类 DemoBeanA
       @Component
       public class DemoBeanA {
       }
    
    • 1
    • 2
    • 3

    1. 创建核心配置类AConfig
    @Configuration
    @ComponentScan("com.a")
    public class AConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    1. 创建导入器:创建Java类,实现ImportSelector接口,重写selectImports方法
    public class AImportSelector implements ImportSelector {
        /**
         * @param importingClassMetadata。被@Import注解标注的类上所有注解的信息
         */
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{AConfig.class.getName()};
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    1. 定义注解@EnableModuleA
    @Import(AImportSelector.class)
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EnableModuleA {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    第二步:引入module_a,启动模块a


    1. 在我们自己的工程pom.xml里增加依赖
    <dependency>
        <groupId>com.execisegroupId>
        <artifactId>spring_module_aartifactId>
        <version>1.0-SNAPSHOTversion>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 在我们自己的工程核心配置类上,使用注解@EnableModuleA启动模块a
    @Configuration
    @EnableModuleA
    public class AppConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 在我们自己的工程里测试
       @Test
        public void testBean(){
            //在我们自己的工程里,可以获取到module_a里的bean
            DemoBeanA demoBeanA = app.getBean(DemoBeanA.class);
            System.out.println(demoBeanA);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ImportBeanDefinitionRegister注册器


    说明

    ImportBeanDefinitionRegister提供了更灵活的注册bean的方式

    AOP里的@EnableAspectJAutoProxy就使用了这种注册器,用于注册不同类型的代理对象


    步骤:

    1. 创建注册器:

      创建Java类,实现ImportBeanDefinitionRegister接口,重写registerBeanDefinitions方法

    2. 在核心配置类上,使用@Import配置注册器


    示例


    1. 创建类com.other.Other
    public class Other {
        public void show(){
            System.out.println("Other.show....");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 创建注册器
    public class CustomImportBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
        /**
         * @param importingClassMetadata 当前类的注解信息
         * @param registry 用于注册bean的注册器
         */
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //获取bean定义信息
            BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition("com.other.Other").getBeanDefinition();
            //注册bean,方法参数:bean的id,bean的定义信息
            registry.registerBeanDefinition("other", beanDefinition);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1. 在核心配置类上,使用@Import配置注册器
    @Configuration
    @Import({CustomImportBeanDefinitionRegister.class})
    public class AppConfig {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 测试
    @Test
    public void testImportRegister(){
        //获取扫描范围外,使用ImportBeanDefinitionRegister注册的bean
        Other other = app.getBean("other",Other.class);
        other.showOther();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    @Conditional (条件)


    说明

    @Conditional 一般是搭配@Bean注解使用,用于表示设定一个条件,如果条件满足,spring就会管理方法的返回值!

    • 符合Condition条件的,Spring会生成bean对象 存储容器中

    • 不符合Condition条件的,不会生成bean对象

    示例:

    • 有一个类Person(姓名name,年龄age)

    • 如果当前操作系统是Linux:就创建Person(linus, 62)对象,并注册bean

    • 如果当前操作系统是Windows:就创建Person(BillGates, 67)对象,并注册bean

    步骤

    1. 创建Person类

    2. 创建两个Java类,都实现Condition接口:

      • WindowsCondition:如果当前操作系统是Windows,就返回true

      • LinuxCondition:如果当前操作系统是Linux,就返回true

    3. 在核心配置类里创建两个bean

      • 一个bean名称为bill,加上@Conditional(WindowsCondition.class)

      • 一个bean名称为linus,加上@Conditional(LinuxCondition.class)


    示例


    1. 创建Person类
    package com.execise.demo4_conditional;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
        private String name;
        private int age;
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 创建两个Java类,实现Condition接口
    package com.execise.demo4_conditional;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    /*
        判断当前的操作系统是不是windows
     */
    public class WindowsCondition implements Condition {
    
        /*
            返回true:  即表明是windows操作系统
            返回false :  即表示不是windows系统
         */
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    
            //1. 得到当前的环境对象
            Environment environment = conditionContext.getEnvironment();
    
            //2. 得到当前系统的名字
            String osName = environment.getProperty("os.name");
    
            return osName.contains("Windows");
        }
    }
    
    
    • 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
    package com.execise.demo4_conditional;
    
    import org.springframework.context.annotation.Condition;
    import org.springframework.context.annotation.ConditionContext;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotatedTypeMetadata;
    
    /*
        判断当前的操作系统是不是Linux
     */
    public class LinuxCondition implements Condition {
    
        /*
            返回true:  即表明是Linux操作系统
            返回false :  即表示不是Linux系统
         */
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
    
            //1. 得到当前的环境对象
            Environment environment = conditionContext.getEnvironment();
    
            //2. 得到当前系统的名字
            String osName = environment.getProperty("os.name");
    
            return osName.contains("Linux");
        }
    }
    
    
    • 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

    1. 核心配置类
    @Configuration
    @ComponentScan("com.execise")
    public class AppConfig {
        
        ...
    
      
        //=========================以下代码使用@Conditional来判断创建对象============================
    
        @Bean
        @Conditional(WindowsCondition.class)
        public Person windows(){
            System.out.println("创建了windows对象!~");
            return new Person("比尔", 75);
        }
    
        @Bean
        @Conditional(LinuxCondition.class)
        public Person linux(){
            System.out.println("创建了linux对象!~");
            return new Person("林纳斯", 62);
        }
    
    
         ...
    
    }
    
    • 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
    1. 测试
    package com.execise.test;
    
    import com.execise.config.AppConfig;
    import com.execise.service.UserService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AppConfig.class)
    public class TestUserServiceImpl {
    
        //把工厂给注入进来
        @Autowired
        private ApplicationContext context;
    
    
        @Test
        public void testBeanNames(){
    
            String[] names = context.getBeanDefinitionNames();
            for (String name : names) {
                System.out.println("name = " + name);
            }
    
        }
    }
    
    
    
    • 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

    执行一次单元测试方法之后,按照以下方式,可以通过JVM参数的方式,设置os.name的参数值。

    设置之后,再次执行单元测试方法


    在这里插入图片描述


    在这里插入图片描述


    Conditional的扩展注解


    @Conditional在springboot里应用非常多,以下列出了一些@Conditional的扩展注解:

    • @ConditionalOnBean:当容器中有指定Bean的条件下进行实例化。

    • @ConditionalOnMissingBean:当容器里没有指定Bean的条件下进行实例化。

    • @ConditionalOnClass:当classpath类路径下有指定类的条件下进行实例化。

    • @ConditionalOnMissingClass:当类路径下没有指定类的条件下进行实例化。

    • @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。

    • @ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。

    • @ConditionalOnProperty:当指定的属性有指定的值时进行实例化。

    • @ConditionalOnExpression:基于SpEL表达式的条件判断。

    • @ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。

    • @ConditionalOnResource:当类路径下有指定的资源时触发实例化。

    • @ConditionalOnJndi:在JNDI存在的条件下触发实例化。

    • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。


    @Profile


    说明


    在开发中,我们编写的工程通常要部署不同的环境,比如:开发环境、测试环境、生产环境。不同环境的配置信息是不同的,比如:数据库配置信息;如果每次切换环境,都重新修改配置的话,会非常麻烦,且容易出错

    针对这种情况,Spring提供了@Profile注解:可以根据不同环境配置不同的bean,激活不同的配置

    • @Profile注解的底层就是@Conditional

    例如:

    • 定义三个数据源:

      • 开发环境一个DataSource,使用@Profile配置环境名称为dev

      • 测试环境一个DataSource,使用@Profile配置环境名称为test

      • 生产环境一个DataSource,使用@Profile配置环境名称release

    • 在测试类上,使用@ActiveProfiles激活哪个环境,哪个环境的数据源会生效

      • 实际开发中有多种方式可以进行激活,这里演示一个单元测试类里是怎样激活的

    示例

    1. pom.xml中增加导入依赖mysql驱动, c3p0
    <dependencies>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.47version>
        dependency>
        <dependency>
            <groupId>c3p0groupId>
            <artifactId>c3p0artifactId>
            <version>0.9.1.2version>
        dependency>
        
        
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.2.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-testartifactId>
            <version>5.2.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>
    
    • 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
    1. 在配置类里创建三个数据源,并配置@Profile
    @Configuration
    public class AppConfig {
    
         //生成三个数据源
        @Bean
        @Profile("dev")
        public DataSource devDataSource() throws PropertyVetoException {
            System.out.println("dev  开发环境的");
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql:///devdb");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            dataSource.setMaxPoolSize(20);
            return dataSource;
        }
    
        @Bean
        @Profile("test")
        public DataSource testDataSource() throws PropertyVetoException {
            System.out.println("test  测试环境的");
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql:///testdb");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            dataSource.setMaxPoolSize(20);
            return dataSource;
        }
    
    
        @Bean
        @Profile("release")
        public DataSource releaseDataSource() throws PropertyVetoException {
            System.out.println("release  生产环境的");
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql:///releasedb");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            dataSource.setMaxPoolSize(20);
            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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    1. 在测试类上,使用@ActiveProfiles激活哪个环境,哪个环境的数据源会生效

    或者使用JVM参数-Dspring.profiles.active=dev

    
    package com.execise.test;
    
    import com.execise.config.AppConfig;
    import com.execise.service.UserService;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    @ActiveProfiles("dev")
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = AppConfig.class)
    public class TestUserServiceImpl {
    
        //把工厂给注入进来
        @Autowired
        private ApplicationContext context;
    
    
        @Test
        public void testBeanNames(){
    
            String[] names = context.getBeanDefinitionNames();
            for (String name : names) {
                System.out.println("name = " + name);
            }
    
        }
    }
    
    • 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
  • 相关阅读:
    达梦数据库报错:Invalid column name [PASSWORD]
    Tesseract .Net SDK C# OCR 2022.1
    大型系统技术架构之服务架构(进阶版)
    QT 集成MQTT过程
    Fruit-Dataset水果数据集+水果分类识别训练代码(支持googlenet, resnet, inception_v3, mobilenet_v2)
    git的基本使用
    Google Colab 快速上手
    分库分表MySQL
    代码随想录算法训练营第23期day24|回溯算法理论基础、77. 组合
    力扣 -- 322. 零钱兑换(完全背包问题)
  • 原文地址:https://blog.csdn.net/m0_67559541/article/details/126692902