@Profile
根据环境注入Bean
@Profile
注解,Spring为我们提供的可以根据当前环境,动态的激活和切换一系列组件的功能,加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境,写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效,没有标注环境标识的bean在,任何环境下都是加载的;
演示场景:
加入有一个日志管理接口类,在这个接口下实现不同的具体日志实现方式,根据不同的环境来注入不同的日志实现类。如,当前有一个接口类
Slf4jBean
,有Log4jBean
和LogbackBean
两个实现,现决定如果使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-------------------------