Spring Boot 注解 ---- @Configuration

@Configuration的由来和作用

在以前,没有Spring Boot的时候,都是用Spring开发Java后端。Spring虽然给我们带来了很多便捷性,但是有一点比较诟病的地方就是如果需要对程序做一些配置,需要写很多的xml配置文件。自从有了Spring Boot后配置文件的数量减少了许多,甚至只有一个配置文件,如果需要进行一些自定义配置怎么办,Spring 官方提供了多种方式:

  • xml配置
  • Java编程式配置
  • 配置文件中配置
    @Configuration就是用在Java编程式配置,该注解可以将某个类作为配置类,在Spring Boot启动的时候注入到IOC容器中。作为配置,随时调用。

当某个类上标注了@Configuration注解的时候,就是告诉Spring Boot这是一个配置类,等同于配置文件

使用方法

简单用法

// 告诉Spring Boot 这个类是个配置类,等同于配置文件
@Configuration
public class AppConfig {
	/**@Bean 作用:给容器中添加组件,以方法名为组件ID,返回类型就是组件类型
	 * 返回的值就是在Spring Boot中启动时创建的实例
	 *@Bean 详细解说,详见Spring Boot 注解 ---- @Bean
	 */
	@Bean
	public MyBean myBean() {
	// instantiate, configure and return bean ...
	}
}

配置组件扫描

@Configuration类不仅可以使用组件扫描进行引导,还可以使用@ComponentScan注释自己配置组件扫描

@Configuration
@ComponentScan("com.acme.app.services")
public class AppConfig {
	// various @Bean definitions ...
}

通过元件扫描

@Configuration内部实现中使用@Component元注释,因此@Configuration类是组件扫描的候选者,因此也可以像任何其他@Component一样利用@Autowired、@Inject、@Recource注入实例。特别是,如果存在单个构造函数,自动装配语义将透明地应用于该构造函数:

@Configuration
public class AppConfig {
	private final SomeBean someBean;

	public AppConfig(SomeBean someBean) {
    	this.someBean = someBean;
	}
	// @Bean definition using "SomeBean"
}

使用外化值

使用 Environment API

可以通过将Spring org.springframework.core.env.Environment注入@Configuration类来查找外化值。
例如,使用@Autowired注释:

@Configuration
public class AppConfig {
	private Environment env;

	@Autowired
	public Appconfig (Environment env) {
	this.env = env;
	}

	@Bean
	public MyBean myBean() {
	MyBean myBean = new MyBean();
	myBean.setName(env.getProperty("bean.name"));
	return myBean;
	}
}

通过Environment解析的属性驻留在一个或多个“属性源”对象中,@Configuration类可以使用@PropertySource注释将属性源给Environment对象:

@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
	// 也是注入方式的一种,同@Autowired
	@Inject
	Environment env;
	@Bean
	public MyBean myBean() {
		return new MyBean(env.getProperty("bean.name"));
	}
}

使用@Value注释

可以使用@Value注释将其他文件中的值注入到@Configuration类中:

@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
	@Value("${bean.name}")
	String beanName;

	@Bean
	public MyBean myBean() {
	return new MyBean(beanName);
	}
}

这种方法通常与 Spring 的PropertySourcesPlaceholderConfigurer结合使用,它可以通过<context:property-placeholder/>在 XML 配置中自动启用,或者通过专用的静态@Bean方法在@Configuration类中显式@Bean。但需要注意的是,通常仅当需要自定义配置(例如占位符语法等)时,才需要通过静态@Bean方法显式注册PropertySourcesPlaceholderConfigurer。具体来说,如果没有 bean 后处理器(例如PropertySourcesPlaceholderConfigurer)已注册ApplicationContext的嵌入值解析器,Spring 将注册一个默认的嵌入值解析器,它根据Environment注册的属性源解析占位符。

@Configuration和@Import同时使用

@Configuration类可以使用@Import注释组成,类似于<import>在 Spring XML 中的工作方式。 因为@Configuration对象在容器内作为 Spring bean 进行管理,所以可以注入需要导入的配置。@Import 详情请参考 (超链接预留 Spring Boot 注解 ---- @Import)
例如,通过构造函数注入:

@Configuration
public class DatabaseConfig {
	@Bean
	public DataSource dataSource() {
		// instantiate, configure and return DataSource
	}
}

@Configuration
@Import(DatabaseConfig.class)
public class AppConfig {
	private final DatabaseConfig dataConfig;

	public AppConfig(DatabaseConfig dataConfig) {
		this.dataConfig = dataConfig;
	}

	@Bean
	public MyBean myBean() {
		// reference the dataSource() bean method
		return new MyBean(dataConfig.dataSource());
	}
}

现在,AppConfig和导入的DatabaseConfig都可以通过仅针对 Spring 上下文注册AppConfig来注入:

new AnnotationConfigApplicationContext(AppConfig.class);

使用@Profile注释

@Configuration类可以用@Profile注释标记,以表明只有在给定的一个或多个配置文件处于活动状态时才应该处理它们。

简单来说,可以通过@Profile注解根据当前程序所在情况(生产环境还是开发环境等等)选择性启用

@Profile("development")
@Configuration
public class EmbeddedDatabaseConfig {
    @Bean
    public DataSource dataSource() {
        // instantiate, configure and return embedded DataSource
    }
}

@Profile("production")
@Configuration
public class EmbeddedDatabaseConfig {
    @Bean
    public DataSource dataSource() {
        // instantiate, configure and return embedded DataSource
    }
}

或者,您也可以在@Bean方法级别声明配置文件条件。
例如,对于同一配置类中的替代 bean 变体:

@Configuration
public class ProfileDatabaseConfig {

    @Bean("dataSource")
    @Profile("development")
    public DataSource embeddedDatabase() {
        ...
    }

    @Bean("dataSource")
    @Profile("production")
    public DataSource productionDatabase() {
        ...
    }
}

使用@ImportResource注释引入Spring XML

@Configuration类可以在 Spring XML 文件中声明为常规 Spring <bean>定义。 还可以使用@ImportResource注释将 Spring XML 配置文件导入到@Configuration类中。 可以注入从 XML 导入的 Bean 定义。
例如,使用@Inject注释:

@Configuration
@ImportResource("classpath:/com/acme/database-config.xml")
public class AppConfig {

    // from XML
    @Inject
    DataSource dataSource;

    @Bean
    public MyBean myBean() {
        // inject the XML-defined dataSource bean
        return new MyBean(this.dataSource);
    }
}

@Configuration嵌套

@Configuration类可以相互嵌套,如下所示:

@Configuration
public class AppConfig {
    @Inject
    DataSource dataSource;

    @Bean
    public MyBean myBean() {
        return new MyBean(dataSource);
    }

    @Configuration
    static class DatabaseConfig {
        @Bean
        DataSource dataSource() {
            return new EmbeddedDatabaseBuilder().build();
        }
    }
}

在这种情况下时,只需要针对应用程序上下文注册AppConfig 。由于@Configuration类是嵌套的,DatabaseConfig将自动注册。当AppConfigDatabaseConfig之间的关系已经隐式明确时,这避免了使用@Import注释的使用。
需要注意的是,嵌套的@Configuration类可以与@Profile注释一起使用,以向封闭的@Configuration类提供相同 bean 的两个选项。
配置延迟初始化。默认情况下,@Bean方法将在容器引导时急切地实例化。 为了避免这种情况,@Configuration可以与@Lazy注释结合使用,以指示类中声明的所有@Bean方法默认是延迟初始化的。

源码解析

// 所在的包
package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

	/**
	 * 显式指定与@Configuration类关联的 Spring bean 定义的名称。 
	 * 如果未指定(常见情况),将自动生成一个 bean 名称。
	 * 自定义名称仅适用于通过组件扫描获取@Configuration类或直接提供给AnnotationConfigApplicationContext。 
	 * 如果@Configuration类注册为传统的 XML bean 定义,则 bean 元素的名称/id 将优先
	 * @return 显式组件名称,如果有(否则为空字符串)
	 * @see AnnotationBeanNameGenerator
	 */
	@AliasFor(annotation = Component.class)
	String value() default "";

	/**
	 * 指定是否应该代理@Bean方法以强制执行 bean 生命周期行为。
	 * 例如,即使在用户代码中直接调用@Bean方法的情况下也返回共享的单例 bean 实例。 
	 * 此功能需要方法拦截,通过运行时生成的 CGLIB 子类实现,该子类具有诸如配置类及其方法不允许声明final。
	 * 默认值为true,
	 * 允许通过配置类中的直接方法调用进行“bean 间引用”,
	 * 以及对此配置的@Bean方法的外部调用,例如来自另一个配置类。
	 * 如果该类被标注@Bean,请将此标志切换为false以避免 CGLIB 子类处理。
	 * 因为每个特定配置的@Bean方法都是自包含的,并且设计为容器使用的普通工厂方法。
	 * 关闭 bean 方法拦截可以有效地单独处理@Bean方法,就像在非@Configuration类上声明时一样,
	 * 也就是“@Bean Lite 模式”
	 * 因此,它在行为上等同于删除@Configuration
	 */
	boolean proxyBeanMethods() default true;
}

@Configuration内部实现的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
  • 第一个注解说明,该注解用于描述类,接口,注解,或者枚举的声明。详细内容参考@Target
  • 第二个注解说明,该注解的生命周期,在运行时使用,详细内容参考@Retention
  • 第三个注解可以不用考虑,只用记得所有自定义注解都需要添加
  • 第四个注解是Spring的注解,意味着被@Configuartion标注的类将会被注册为Bean。

@Configuration支持的属性

  1. value:显式指定与@Configuration类关联的 Spring bean 定义的名称。 如果未指定(常见情况),将自动生成一个 bean 名称。自定义名称仅适用于通过组件扫描获取@Configuration类或直接提供给AnnotationConfigApplicationContext 。 如果@Configuration类注册为传统的 XML bean 定义,则优先使用bean 元素的名称或id
  2. proxyBeanMethods:指定是代理@Bean方法以强制执行 bean 生命周期行为,例如,即使在用户代码中直接调用@Bean方法的情况下也返回共享的单例 bean 实例。 此功能需要方法拦截,通过运行时生成的 CGLIB 子类实现,该子类具有诸如配置类及其方法不允许声明final 。默认值为true ,允许通过配置类中的直接方法调用进行“bean 间引用”,以及对此配置的@Bean方法的外部调用,例如来自另一个配置类。 如果@Bean ,因为每个特定配置的@Bean方法都是自包含的,并且设计为容器使用的普通工厂方法,请将此标志切换为false以避免 CGLIB 子类处理。关闭 bean 方法拦截可以有效地单独处理@Bean方法,就像在@Configuration类上声明时一样,也就是“@Bean Lite 模式”。 因此,它在行为上等同于删除@Configuration。简单来说:就是将该注解中的@Bean以代理的形式生成,每次获取的时候,都会去IOC容器中查找。如果设置为false,每次调用的时候都会生成一个新的实例。
    • 当组件之间没有依赖关系用Lite模式(轻量模式),也就是proxyBeanMethods = false,会Spring Boot启动速度会提升,减少判断,每次获取的时候都会创建一个新的实例。
    • 当组件之间存在依赖关系、方法被调用之前得到单实例组件,用Full模式(全量模式),也就是proxyBeanMethods = true
posted @ 2022-04-07 17:45  夏醉浅梦  阅读(1091)  评论(0)    收藏  举报