Spring框架核心注解用例

image

示例的源码地址: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成功注册。

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值