示例的源码地址:https://gitee.com/cq-laozhou/spring-stack-source-code-analysis
@Configuration 和 @Bean
回忆下,在古老的Spring框架版本中(Spring3之前),spring的所有配置都是通过xml文件来配置,比如:
在 applicationContext.xml文件中定义一个bean:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.zyy.sc.analysis.framework.testxml.Person"></bean>
</beans>
此时,使用ClassPathXmlApplicationContext 来初始化spring容器,并可从容器中获取在xml文件定义的bean。
public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("xml/applicationContext.xml");
System.out.println(context.getBean("person"));
}
}
在Spring framework 3后,引入了通过java风格的配置方法,替代原有的冗长的xml配置方式。
@Configuration 类比于一个xml文件,使用该注解的java类称为配置类。
@Bean 类比xml文件中的一个bean定义。
比如有一个配置类:
@Configuration
public class MainConfig {
@Bean
public Person person(){
return new Person();
}
}
然后,使用 AnnotationConfigApplicationContext 来初始化spring容器,并可从容器中获取定义的bean。
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println(context.getBean("person"));
}
}
@CompentScan
在配置类中,使用@CompentScan注解来指定扫描的包路径,从而实现bean的自动注册到spring容器中,默认自动注册的注解有@Controller @Service @Repository @Compent
比如在包“com.zyy.sc.analysis.framework.testConpnentScan”有如下的类,他们都被上面几种注解了:
@Controller
public class PersonController {
}
@Service
public class PersonService {
}
@Repository
public class PersonDao {
}
在配置类中,配置上@CompentScan:
@Configuration
@ComponentScan(basePackages = "com.zyy.sc.analysis.framework.testConpnentScan")
public class MainConfig {
}
然后输出spring 容器中注册的bean
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
printCustomSpringBeanName(context);
}
private static void printCustomSpringBeanName(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
if(name.contains("spring")){
continue;
}
System.out.println("beanName: " + name);
}
}
}
输出为:
beanName: mainConfig
beanName: person
beanName: personDao
beanName: personService
默认@ComponentScan会扫描包下所有带有@Controller @Service @Repository @Compent注解的类。
我们可以使用useDefaultFilters=false来关闭对@Controller @Service @Repository @Compent 4种注解的自动过滤,使用includeFilters来包含哪些类会注册为bean,excludeFilters来排除哪些类,让他们不注册为bean,从而实现一些自定义的包扫描策略。
比如如下配置指明包含 @Service @Repository注解的类会注册为bean,Person类会注册为bean,而@Controller注解的类不会注册为bean,同时关闭默认的4种常规注解扫描:
@Configuration
@ComponentScan(basePackages = "com.zyy.sc.analysis.framework.testConpnentScan",
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Service.class, Repository.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class)},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)},
useDefaultFilters = false
)
public class MainConfig {
}
在这个配置下的运行输出为:
beanName: mainConfig
beanName: person
beanName: personDao
beanName: personService
在配置过滤器时,FilterType支持5种类型:通过注解过滤,通过具体类型过滤,自定义过滤器过滤,ASPECTJ过滤和正则过滤,前两种在上面的示例中已经见过,后两种几乎用不上,接下来我们来看下自定义过滤的用法。
首先实现一个自定义过滤器,它要实现TypeFilter接口:
public class EndWithDaoTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
if(className.endsWith("Dao")){
return true;
}
return false;
}
}
接下来在配置类中,使用自定义过滤器:
@Configuration
@ComponentScan(basePackages = "com.zyy.sc.analysis.framework.testConpnentScan",
excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = EndWithDaoTypeFilter.class)}
)
public class MainConfig {
}
以上配置的输出为:
beanName: mainConfig
beanName: personController
beanName: personService
可见,已“Dao”结尾的类名会被排除,不注册为bean。
@Scope
spring中,bean的Scope有4种,singleton,prototype,request,session。不过常用的为singlton和prototype。
默认bean的scope为singleton,且是随着容器的初始化就初始化好了。
比如,有Person类:
public class Person {
public Person() {
System.out.println("--person 构造方法--");
}
}
配置类中将它申明为bean;
@Configuration
public class MainConfig {
@Bean
public Person person(){
return new Person();
}
}
此时,启动spring容器,就会初始化person的bean,且是单例
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
Person person1 = context.getBean("person",Person.class);
Person person2 = context.getBean("person",Person.class);
System.out.println(person1 == person2);
}
}
输出为:
--person 构造方法--
true
如果想让person的bean延迟初始化,即getBean时才真正初始化,可以使用@Lazy注解.
当我们配置bean的Scope为prototype后,bean就是延迟初始化的。
比如,在配置类中将person bean配置为prototype的scope:
@Configuration
public class MainConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person person(){
return new Person();
}
}
使用同样的代码输出为:
--person 构造方法--
--person 构造方法--
false
可见person初始化了两次,且从容器中每次获取都是新的person对象。
@Conditional
@Conditional注解用于配置类中,当满足特定条件后,才会注册bean,其中条件是否满足,由指定的Condition来判断。
假设有两个组件componentA和componentB, 当然spring容器中存在componentA组件时,才注册componentB组件:
public class ComponentA {
}
public class ComponentB {
}
在配置类中,使用@Conditional来配置componentB,其条件有ComponentAExistsCondition类来实现
@Configuration
public class MainConfig {
@Bean
public ComponentA componentA(){
return new ComponentA();
}
@Bean
@Conditional(value = ComponentAExistsCondition.class)
public ComponentB componentB(){
return new ComponentB();
}
}
而 ComponentAExistsCondition 实现了 Condition接口:
public class ComponentAExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if(context.getBeanFactory().containsBean("componentA")){
return true;
}
return false;
}
}
最后打印容器的中的bean名称,输出:
beanName: mainConfig
beanName: componentA
beanName: componentB
当我们将componentA的配置修改下,去掉componentA的bean定义:
//@Bean
public ComponentA componentA(){
return new ComponentA();
}
打印容器的中的bean名称,输出:
beanName: mainConfig
容器中的componentB也不会注册了。
@Import
@Import 注解是除了 @Bean 和 @ComponentScan 之外,另外一种声明bean的方式,它可以直接指定类型来声明bean,也可以指定importSelector,或者ImportBeanDefinitionRegistrar来声明bean。
比如,在配置类中使用@Import指定类型:
@Configuration
@Import(value = Person.class)
public class MainConfig {
}
打印容器的中的bean名称,输出:
beanName: mainConfig
beanName: com.zyy.sc.analysis.framework.testimport.Person
可以看到,这种方式注册的beanName为类的全限定名。
现在,配置文件中使用importSelector方式来指定bean:
@Configuration
@Import(value = PersonImportSelector.class)
public class MainConfig {
}
且 PersonImportSelector 实现了 ImportSelector 接口,返回的String[]为要注册bean的全限定名:
public class PersonImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.zyy.sc.analysis.framework.testimport.Person"};
}
}
打印容器的中的bean名称,输出:
beanName: mainConfig
beanName: com.zyy.sc.analysis.framework.testimport.Person
可以看到,这种方式注册的beanName也为类的全限定名。
最后再在配置类中使用ImportBeanDefinitionRegistrar方式:
@Configuration
@Import(value = PersonImportBeanDefinitionRegistrar.class)
public class MainConfig {
}
且 PersonImportBeanDefinitionRegistrar 实现了 ImportBeanDefinitionRegistrar 接口:
public class PersonImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(Person.class);
registry.registerBeanDefinition("person", beanDefinition);
}
}
打印容器的中的bean名称,输出:
beanName: mainConfig
beanName: person
可以看到,这种方式注册的beanName可自定义。
FactoryBean
FactoryBean是一种特殊的bean,当从容器中获取这种类型的bean时,实际上调用FactoryBean.getObject()方法,返回该方法创建的对象,可以在beanName前加上“&”来获取真实的FactoryBean的bean。
假如有一个初始化非常复杂的类,如果将初始化代码放到配置类中,会降低开发者的使用体验,可使用FactoryBean将复杂的初始化过程封装起来,开发者在配置类中使用简单的FactoryBean会非常的愉快。
比如初始化复杂的Person类:
@Data
public class Person {
private int age;
private String name;
}
使用PersonFactoryBean 将构建person的复杂代码封装起来:
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
return buildPerson();
}
private Person buildPerson() {
Person person = new Person();
person.setName("zyy");
person.setAge(30);
return person;
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
}
这样在配置类中,就可以简单配置PersonFactoryBean即可。
@Configuration
public class MainConfig {
@Bean
public PersonFactoryBean personFactoryBean(){
return new PersonFactoryBean();
}
}
我们输出下容器中的beanName,以及获取person bean 和 personFactoryBean bean的方式:
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
printCustomSpringBeanName(context);
System.out.println(context.getBean("personFactoryBean"));
System.out.println(context.getBean("&personFactoryBean"));
}
private static void printCustomSpringBeanName(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
if(name.contains("spring")){
continue;
}
System.out.println("beanName: " + name);
}
}
}
输出:
beanName: mainConfig
beanName: personFactoryBean
Person(age=30, name=zyy)
com.zyy.sc.analysis.framework.testFactoryBean.PersonFactoryBean@4c9f8c13
bean的生命周期管理
bean的生命周期是指bean在spring容器中的创建、初始化、销毁的整个生命周期,spring提供了几种方式,让我们可以让在生命周期事件中执行自定义的一些代码。
@Bean 的 initMethod 和 destoryMethod 配置,指定在bean的初始化和销毁时回调方法。
比如有Person类
public class Person {
public void init(){
System.out.println("----Person.init()----");
}
public void destory(){
System.out.println("------Person.destory------");
}
}
在配置类中,将Person配置为bean,并指定initMethod 和 destoryMethod:
@Configuration
public class MainConfig {
@Bean(initMethod = "init",destroyMethod = "destory")
public Person person(){
return new Person();
}
}
Main方法中注意要调用close()方法关闭spring容器,这样才能看到bean destoryMethod方法效果:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
printAllSpringBeanName(context);
context.close();
}
private static void printAllSpringBeanName(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
System.out.println("============================");
for (String name : names) {
System.out.println("beanName: " + name);
}
}
}
输出:
----Person.init()----
============================
beanName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalRequiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalCommonAnnotationProcessor
beanName: org.springframework.context.event.internalEventListenerProcessor
beanName: org.springframework.context.event.internalEventListenerFactory
beanName: mainConfig
beanName: person
------Person.destory------
需要注意的是,如果bean的Scope为prototype的话,在getBean的时候,即spring容器在创建bean时,会调用initMethod方法,而容器在销毁的时候不会触发destoryMethod。这也好理解,prototype的bean创建完成后,如果不用了会被gc回收,容器在关闭的时候已经不存在了,也就不会触发destoryMethod方法了。
在主配置文件中,修改person的scope为prototype:
@Configuration
public class MainConfig {
@Bean(initMethod = "init",destroyMethod = "destory")
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person person(){
return new Person();
}
}
在Main方法中加入一行获取person bean的代码 context.getBean("person");
, 运行Main方法,输出:
============================
beanName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalRequiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalCommonAnnotationProcessor
beanName: org.springframework.context.event.internalEventListenerProcessor
beanName: org.springframework.context.event.internalEventListenerFactory
beanName: mainConfig
beanName: person
----Person.init()----
可以看到scope为prototype时,只会触发initMethod方法,不会触发destoryMethod方法,即使还没有gc。
也可以让java类实现 InitializingBean 和 DisposableBean 接口,这样接口类型的bean,spring也会初始化时回调InitializingBean.afterPropertiesSet()方法,spring在关闭容器的时候调用 DisposableBean.destroy()方法。
比如有Person2的java类,实现了InitializingBean 和 DisposableBean接口:
public class Person2 implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("------Person2.destroy------");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("----Person2.init()----");
}
}
配置类中,将Person2声明为spring bean:
@Configuration
public class MainConfig {
@Bean
public Person2 person2(){
return new Person2();
}
}
打印容器中的bean名称:
----Person2.init()----
============================
beanName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalRequiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalCommonAnnotationProcessor
beanName: org.springframework.context.event.internalEventListenerProcessor
beanName: org.springframework.context.event.internalEventListenerFactory
beanName: mainConfig
beanName: person2
------Person2.destroy------
效果与initMethod和destoryMethod一致。当然bean声明为prototype时,也是只有在getBean时触发afterPropertiesSet()方法,不会触发destroy方法。
使用 @PostConstruct 和 @PreDestory 注解java类中的方法,也可以让spring在初始化时调用 @PostConstruct 注解的方法,spring在关闭时调用 @PreDestory 注解的方法。
比如有Person3类,它使用@PostConstruct 和 @PreDestory 注解:
public class Person3 {
@PostConstruct
public void init(){
System.out.println("----Person3.init()----");
}
@PreDestroy
public void destory(){
System.out.println("------Person3.destory------");
}
}
在配置类中将Person3声明为bean:
@Configuration
public class MainConfig {
@Bean
public Person3 person3(){
return new Person3();
}
}
打印容器中的bean名称:
----Person3.init()----
============================
beanName: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanName: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalRequiredAnnotationProcessor
beanName: org.springframework.context.annotation.internalCommonAnnotationProcessor
beanName: org.springframework.context.event.internalEventListenerProcessor
beanName: org.springframework.context.event.internalEventListenerFactory
beanName: mainConfig
beanName: person3
------Person3.destroy------
效果与initMethod和destoryMethod,以及实现InitializingBean 和 DisposableBean 一样。 。当然bean声明为prototype时,也是只有在getBean时触发 @PostConstruct 注解的方法,不会触发@PreDestroy 注解的方法。
BeanPostProcessor
在spring中,还有一个非常重要的组价叫BeanPostProcessor,它会拦截所有的bean的创建过程,并在bean的初始haul之前执行 BeanPostProcessor 的 postProcessBeforeInitialization 方法,在bean的初始化之后执行 BeanPostProcessor 的 postProcessAfterInitialization 方法。
比如,有个自定义简单的 PrintBeanNameBeanPostProcessor:
public class PrintBeanNameBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(isCustomBean(beanName)) {
System.out.println("----PrintBeanNameBeanPostProcessor.postProcessBeforeInitialization(),beanName is [" + beanName + "]-----");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(isCustomBean(beanName)) {
System.out.println("----PrintBeanNameBeanPostProcessor.postProcessAfterInitialization(),beanName is [" + beanName + "]-----");
}
return bean;
}
private boolean isCustomBean(String beanName) {
return StringUtils.isEmpty(beanName) ? false : !beanName.contains("spring");
}
}
主配置文件中需要将BeanPostProcessor也声明为spring的bean:
@Configuration
public class MainConfig {
@Bean(initMethod = "init",destroyMethod = "destory")
public Person person(){
return new Person();
}
@Bean
public PrintBeanNameBeanPostProcessor printBeanNameBeanPostProcessor(){
return new PrintBeanNameBeanPostProcessor();
}
}
在Main方法中,输出spring容器bean:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
printAllSpringBeanName(context);
context.close();
}
private static void printAllSpringBeanName(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
System.out.println("============================");
for (String name : names) {
if (name.contains("spring")) continue;
System.out.println("beanName: " + name);
}
}
}
输出为:
----PrintBeanNameBeanPostProcessor.postProcessBeforeInitialization(),beanName is [person]-----
----Person.init()----
----PrintBeanNameBeanPostProcessor.postProcessAfterInitialization(),beanName is [person]-----
============================
beanName: mainConfig
beanName: person
beanName: printBeanNameBeanPostProcessor
------Person.destory------
可见,在Person初始化前后,spring分别调用了PrintBeanNameBeanPostProcessor的 postProcessBeforeInitialization方法和 postProcessAfterInitialization方法。
spring中创建bean时,可使用 @Vaule 和 @PropertySource 注解配合来给bean属性赋值,支持直接赋值,SeEL表达式,以及外部配置文件方式。
比如有Person类,使用@Value注解了其属性:
@Data
public class Person {
@Value("Z")
private String firstName;
@Value("#{18 + 10}")
private Integer age;
@Value("${person.lastName}")
private String lastName;
}
在配置类中,使用@PropertySource指定外部配置文件路径:
@Configuration
@PropertySource(value = {"classpath:prop/person.properties"})
public class MainConfig {
@Bean
public Person person(){
return new Person();
}
}
在resources中创建prop/person.properties的配置:
person.lastName=YY
最后Main方法中获取person的bean,输出其属性:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println(context.getBean(Person.class));
context.close();
}
}
输出为:
Person(firstName=Z, age=28, lastName=YY)
可以看到@Value的几种属性注入值的方式都生效了。
自动装配
在java类的属性,setter方法,构造方法中 使用 @AutoWired 注解, 那么将该java类声明为spring bean后,spring在创建bean实例时,会自动找到对应的bean,自动注入进来。注入的规则是,如果要注入类型的bean在spirng中只有一个,spring就会按照类型将这唯一的bean注入(byType);如果要注入类型的bean有多个,则默认通过bean的名称和属性名来注入(byName),如果名称也不匹配,最后可通过@Qualifier 和 @Primary 限定到具体的bean注入。
比如有要自动注入的PersonDao类:
public class PersonDao {
}
有需要自动注入的PersonService类:
@Service
@Data
public class PersonService {
@Autowired
private PersonDao personDao;
}
配置类中,配置了两个PersonDao的bean:
@Configuration
@ComponentScan(basePackageClasses = MainConfig.class)
public class MainConfig {
@Bean
public PersonDao personDao(){
return new PersonDao();
}
@Bean
public PersonDao personDao2(){
return new PersonDao();
}
}
按照前面所述,因为有多个备选的bean,PersonService的bean中自动注入会使用ByName的方式,将personDao注入进去。
因此Main方法:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
System.out.println(context.getBean(PersonService.class).getPersonDao() == context.getBean("personDao"));
context.close();
}
}
的输出为true。
如果想要让personDao2注入到PersonService中,可以使用@Qualifier或者@Primary 注解指定要注入的为personDao2:
比如:
@Service
@Data
public class PersonService {
@Autowired
@Qualifier(value = "personDao2")
private PersonDao personDao;
}
假如spring容器中没有PersonDao类型的bean,那么@Autowired不能自动注入成功,spring容器默认在启动时会抛出org.springframework.beans.factory.NoSuchBeanDefinitionException
异常。
如果想要自动注入失败时,忽略异常,让spring容器正常启动,可以在指定Autowired注解的required属性为false:
@Autowired(required = false)
@Qualifier(value = "personDao2")
private PersonDao personDao;
这样,虽然会注入失败,当不影响spring容器启动。
XXXAware接口
spring给我提供了一些列的以Aware结尾的接口,当我们的java类实现这种接口,并且声明为spring bean后,spring会自动回调对应的setXXX方法,将一些对象设置进来。
比如有java类TestBeanAndContextAware,实现了BeanNameAware, ApplicationContextAware接口:
@Component
public class TestBeanAndContextAware implements BeanNameAware, ApplicationContextAware {
@Override
public void setBeanName(String name) {
System.out.println("--beanName is ["+ name +"]---");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
printAllSpringBeanName(applicationContext);
}
private void printAllSpringBeanName(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
System.out.println("============================");
for (String name : names) {
if (name.contains("spring")) continue;
System.out.println("beanName: " + name);
}
}
}
在配置类中,使用@ComponentScan将TestBeanAndContextAware扫描进去,注册为bean:
@Configuration
@ComponentScan
public class MainConfig {
}
Main方法中启动spring容器:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
context.close();
}
}
启动spring中会输出:
--beanName is [testBeanAndContextAware]---
============================
beanName: mainConfig
beanName: testBeanAndContextAware
这就是spring在创建testBeanAndContextAwarebean时,会调用setBeanName方法和setApplicationContext方法。其他的Aware结尾的接口用法类似。
@Profile
spring的 @Profile 注解,可以实现根据不同的环境来激活不同的bean配置, @Profile 即可作用在整个配置类上,也可作用在单个bean上,当然没有用 @Profile注解的配置,是不管什么环境都会激活的。
比如有配置类,使用@Profile注解:
@Configuration
public class MainConfig {
@Profile("dev")
@Bean
public Person personForDev(){
return new Person();
}
@Profile("test")
@Bean
public Person personForTest(){
return new Person();
}
@Profile("prod")
@Bean
public Person personForProd(){
return new Person();
}
@Bean
public Person personAllExists(){
return new Person();
}
}
接下来,我们可以通过代码的方式,或者JVM参数的方式(-Dspring.profiles.active=dev),来激活环境:
public class Main {
public static void main(String[] args) {
System.setProperty("spring.profiles.active","prod");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
printAllSpringBeanName(context);
context.close();
}
private static void printAllSpringBeanName(ApplicationContext context) {
String[] names = context.getBeanDefinitionNames();
System.out.println("============================");
for (String name : names) {
if (name.contains("spring")) continue;
System.out.println("beanName: " + name);
}
}
}
输出的spring bean名称:
============================
beanName: mainConfig
beanName: personForProd
beanName: personAllExists
可以看到,其他环境bean没有注册,只有指定环境的bean成功注册。