spring注解驱动第六节之@Profile根据环境注入Bean

6. @Profile根据环境注入Bean

@Profile注解,Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能,加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境,写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效,没有标注环境标识的bean在,任何环境下都是加载的;

演示场景:

加入有一个日志管理接口类,在这个接口下实现不同的具体日志实现方式,根据不同的环境来注入不同的日志实现类。如,当前有一个接口类Slf4jBean,有Log4jBeanLogbackBean两个实现,现决定如果使dev环境,则使用Lo4jBean来管理日志,如果prd环境,则使用LogbackBean;

  • 新建日志接口类Slf4jBean.java
package com.ddf.spring.annotation.bean;

/**
 * @author DDf on 2018/8/8
 * 测试根据不同的profile来动态切换注册不同的类,该类为接口,使用接口接收参数,实际注入值为接口实现类
 */
public interface Slf4jBean {
    void info(String str);
}
  • 新建日志实现类Log4jBean.java
package com.ddf.spring.annotation.bean;

/**
 * @author DDf on 2018/8/8
 */
public class Log4jBean implements Slf4jBean {

    @Override
    public void info(String str) {
        System.out.println(this.getClass().getName() + ": " + str);
    }
}
  • 新建另一个日志实现类LogbackBean.java
package com.ddf.spring.annotation.bean;

/**
 * @author DDf on 2018/8/8
 */
public class LogbackBean implements Slf4jBean {
    @Override
    public void info(String str) {
        System.out.println(this.getClass().getName() + ": " + str);
    }
}
  • 新建一个配置类ProfileConfiguration.java,使用@Profile注解在方法上根据不同环境注入不同的类
    其实这一块完全可以使用之前一直使用的配置类AnnotationConfiguration,只不过切换环境牵扯到多次的重启IOC容器,而之前的测试大多都是在容器启动时测试的,因此有大量的打印在控制台上,如果继续使用这个配置类,会在大量的日志里看到当前的测试,会非常不易观察,因此这一块单独出来用一个新的配置类
package com.ddf.spring.annotation.configuration;

import com.ddf.spring.annotation.bean.Log4jBean;
import com.ddf.spring.annotation.bean.LogbackBean;
import com.ddf.spring.annotation.bean.Slf4jBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

/**
 * @author DDf on 2018/8/8
 * 配置类2,因为一些特殊的测试需要重新启动IOC容器,在原来的测试类上有太多测试代码,重新启动后,有太多的打印影响观看,
 * 所以这里单独独立出来一个配置类,实际情况中一个配置类是完全可以的
 */
@Configuration
public class ProfileConfiguration {

    /**
     * 使用接口的形式根据环境注入接口的实现类
     * 如果当前环境是dev,Log4jBean
     * @return
     */
    @Bean
    @Profile("dev")
    public Slf4jBean log4jBean() {
        return new Log4jBean();
    }

    /**
     * 使用接口的形式根据环境注入接口的实现类
     * 如果当前环境是prd,则注入LogbackBean
     * @return
     */
    @Bean
    @Profile("prd")
    public Slf4jBean logbackBean() {
        return new LogbackBean();
    }
}
  • 修改主启动类Application.java,增加测试@profile的方法testProfile
package com.ddf.spring.annotation;

import com.ddf.spring.annotation.service.AutowiredService;
import com.ddf.spring.annotation.service.LazyBeanService;
import com.ddf.spring.annotation.service.PrototypeScopeService;
import com.ddf.spring.annotation.service.UserService;
import com.ddf.spring.annotation.bean.AutowiredBean;
import com.ddf.spring.annotation.bean.FactoryPrototypeBean;
import com.ddf.spring.annotation.bean.FactorySingletonBean;
import com.ddf.spring.annotation.bean.Slf4jBean;
import com.ddf.spring.annotation.configuration.AnnotationConfiguration;
import com.ddf.spring.annotation.configuration.ProfileConfiguration;
import com.ddf.spring.annotation.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author DDf on 2018/7/19
 */
public class Application {
    public static void main(String[] args) {
        System.out.println("-----------------------IOC容器初始化-------------------------");
        // 创建一个基于配置类启动的IOC容器,如果主配置类扫描包的路径下包含其他配置类,则其他配置类可以被自动识别
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationConfiguration.class);
        System.out.println("-----------------------IOC容器初始化完成-------------------------\n");
        // 获取当前IOC中所有bean的名称,即使是懒加载类型的bean也会获取到
        printBeans(applicationContext);
        // 测试@Scope bean的作用域
        testPrototypeScopeService(applicationContext);
        // 测试单实例bean的@Lazy懒加载
        testLazyBeanService(applicationContext);
        // 测试FactoryBean接口导入单实例与Prototype作用域的组件
        testFactoryBeanPrototypeBean(applicationContext);
        // 测试@PropertySource和@Value属性赋值
        testPropertySourceValue(applicationContext);
        // 测试@Autowired注入多个相同类型的Bean
        testAutowired(applicationContext);
        // 测试@Profile根据环境注入Bean
        testProfile(applicationContext);

        // 销毁容器
        applicationContext.close();
    }


    /**
     * 打印当前IOC容器中所有预定义的Bean
     * @param applicationContext
     */
    private static void printBeans(AnnotationConfigApplicationContext applicationContext) {
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        // 打印当前IOC中对应名称的bean和bean的类型
        for (String name : definitionNames) {
            // 这个会影响到测试懒加载的效果,如果需要测试懒加载,这行代码需要注释掉,因为getBean方法一旦调用则会初始化
            Object bean = applicationContext.getBean(name);
            System.out.println("bean name:" + name + ", type: " + bean.getClass());
        }
    }


    /**
     * 测试@Scope bean的作用域
     *
     * @param applicationContext
     */
    public static void testPrototypeScopeService(ApplicationContext applicationContext) {
        System.out.println("\n-----------------------测试@Scope开始-------------------------");
        UserService userService = (UserService) applicationContext.getBean("userService");
        UserService userService1 = applicationContext.getBean(UserService.class);
        System.out.println("默认单实例bean UserService是否相等 " + (userService == userService1));

        PrototypeScopeService prototypeScopeService = applicationContext.getBean(PrototypeScopeService.class);
        PrototypeScopeService prototypeScopeService1 = applicationContext.getBean(PrototypeScopeService.class);
        System.out.println("PrototypeScopeService prototype scope作用域是否相等: " + (prototypeScopeService == prototypeScopeService1));
        System.out.println("-----------------------测试@Scope结束-------------------------\n");
    }

    /**
     * 测试单实例bean的懒加载,只有等使用的时候再创建实例。
     * IOC容器启动后不会创建该bean的实例,如果是在该方法中才创建这个bean的实例,并且获得的两个bean是同一个的话,则测试通过。
     */
    public static void testLazyBeanService(ApplicationContext applicationContext) {
        System.out.println("\n---------------测试单实例bean的@Lazy懒加载开始----------------------");
        LazyBeanService lazyBeanService = applicationContext.getBean(LazyBeanService.class);
        LazyBeanService lazyBeanService1 = applicationContext.getBean(LazyBeanService.class);
        System.out.println("lazyBeanService==lazyBeanService1?: " + (lazyBeanService == lazyBeanService1));
        System.out.println("---------------测试单实例bean的@Lazy懒加载结束----------------------\n");
    }


    /**
     * 测试通过FactoryBean接口导入单实例与Prototype作用域的组件,根据打印可以看出FactoryBean创建的单实例Bean都是懒加载的
     * @param applicationContext
     */
    public static void testFactoryBeanPrototypeBean(ApplicationContext applicationContext) {
        System.out.println("\n----------测试通过FactoryBean注册单实例和Prototype作用域的组件开始----------");
        FactorySingletonBean factorySingletonBean = applicationContext.getBean(FactorySingletonBean.class);
        FactorySingletonBean factorySingletonBean1 = applicationContext.getBean(FactorySingletonBean.class);

        FactoryPrototypeBean factoryPrototypeBean = applicationContext.getBean(FactoryPrototypeBean.class);
        FactoryPrototypeBean factoryPrototypeBean1 = applicationContext.getBean(FactoryPrototypeBean.class);

        System.out.println("单实例factorySingletonBean==factorySingletonBean1?" + (factorySingletonBean==factorySingletonBean1));

        System.out.println("Prototype作用域factoryPrototypeBean==factoryPrototypeBean1?" + (factoryPrototypeBean==factoryPrototypeBean1));
        System.out.println("----------测试通过FactoryBean注册单实例和Prototype作用域的组件结束----------\n");
    }

    /**
     * 测试通过@PropertySource和@Value注解来对属性进行赋值
     * @param applicationContext
     */
    public static void testPropertySourceValue(ApplicationContext applicationContext) {
        System.out.println("\n---------------测试@PropertySource和@Value赋值开始----------------");
        User user = applicationContext.getBean(User.class);
        System.out.println("user属性为: " + user.toString());
        System.out.println("---------------测试@PropertySource和@Value赋值结束----------------\n");

    }

    /**
     * 测试在IOC容器中存在两个相同类型的Bean,但是Bean的名称不一致
     * 在这种情况下,使用@Autowired将该Bean注入到另外一个容器中
     * @Autowired 默认使用Bean的类型去匹配注入,如果找到多个相同类型的Bean,则使用默认名称去获取Bean,Bean的默认名称为类首字母缩写
     * 也可以配合@Autowired配合@Qualifier注解明确指定注入哪个Bean,还可以在注入Bean的地方使用@Primary,在不指定@Qualifier的情况下,
     * 默认注入哪个Bean {@link AutowiredService}
     * @param applicationContext
     */
    public static void testAutowired(ApplicationContext applicationContext) {
        System.out.println("\n--------------测试autowired注入多个相同类型的类开始-----------------");
        AutowiredBean autowiredBean = (AutowiredBean) applicationContext.getBean("autowiredBean");
        AutowiredBean autowiredBean2 = (AutowiredBean) applicationContext.getBean("autowiredBean2");
        System.out.println("autowiredBean: " + autowiredBean);
        System.out.println("autowiredBean2: " + autowiredBean2);
        System.out.println(autowiredBean == autowiredBean2);

        /**
         * 这里已做更改,修改了默认注入 {@link com.ddf.spring.annotation.configuration.AnnotationConfiguration.autowiredBean2}
         */
        AutowiredService autowiredService = applicationContext.getBean(AutowiredService.class);
        AutowiredBean autowiredServiceBean = autowiredService.getAutowiredBean();
        System.out.println("使用@Primay后AutowiredService默认注入bean: " + autowiredServiceBean);

        AutowiredBean autowiredServiceBean2 = autowiredService.getQualifierAutowiredBean();
        System.out.println("使用@Qualifier明确注入Bean: " + autowiredServiceBean2);

        // 使用@Resource注入
        AutowiredBean resourceAutowiredBean = autowiredService.getResourceAutowiredBean();
        System.out.println("使用@Resource注入autowiredBean: " + resourceAutowiredBean);
        AutowiredBean resourceAutowiredBean2 = autowiredService.getResourceAutowiredBean2();
        System.out.println("使用@Resource注入autowiredBean2: " + resourceAutowiredBean2);

        // 使用@Inject注入
        UserService userService = autowiredService.getUserService();
        System.out.println("使用@Inject注入UserService: " + userService);

        System.out.println("--------------测试autowired注入多个相同类型的类开始-----------------\n");
    }


    /**
     * 测试根据激活的Profile来根据环境注册不同的Bean
     * 切换profile有两种方式:
     * 1. 在java虚拟机启动参数加 -Dspring.profiles.active=test
     * 2. 如下演示,使用代码切换,可以将切换的变量放在配置文件,spring-boot配置文件即是这种方式
     * @param applicationContext
     */
    public static void testProfile(AnnotationConfigApplicationContext applicationContext) {
        System.out.println("------------------测试@Profile-------------------------");
        // 重新新建一个IOC容器
        applicationContext = new AnnotationConfigApplicationContext();
        // 注册配置类
        applicationContext.register(ProfileConfiguration.class);
        // 设置当前激活的环境profile
        applicationContext.getEnvironment().setActiveProfiles("dev");
        // 刷新容器
        applicationContext.refresh();

        // 使用接口获得实际接口实现类的注入Bean
        Slf4jBean devLogBean = applicationContext.getBean(Slf4jBean.class);
        devLogBean.info("测试环境");

        applicationContext = new AnnotationConfigApplicationContext();
        applicationContext.register(ProfileConfiguration.class);
        applicationContext.getEnvironment().setActiveProfiles("prd");
        applicationContext.refresh();

        Slf4jBean prdLogBean = applicationContext.getBean(Slf4jBean.class);
        prdLogBean.info("生产环境");

        System.out.println("------------------测试@Profile-------------------------");
    }
}
  • 打印日志如下,可以看到首先使用的dev环境,然后打印的是com.ddf.spring.annotation.bean.Log4jBean: 测试环境,然后切换为prd环境,打印的是com.ddf.spring.annotation.bean.LogbackBean: 生产环境
.......省略其它不相关日志............
------------------测试@Profile-------------------------
PostConstructAndPreDestoryBean容器销毁,使用@PreDestroy注解来指定调用销毁方法。。。。
InitAndDisposableBean容器销毁,实现DisposableBean接口调用销毁方法...........
User销毁后调用销毁方法....通过@Bean的destoryMethod指定销毁方法......
com.ddf.spring.annotation.bean.Log4jBean: 测试环境
com.ddf.spring.annotation.bean.LogbackBean: 生产环境
------------------测试@Profile-------------------------

你可能感兴趣的:(JAVA)