SpringBoot 中的注解学习

本贴最后更新于 1285 天前,其中的信息可能已经天翻地覆

背景

SpringBoot 依赖注解来简化了参数配置,通过使用默认值实现了工程可以开箱即用。

本文来从注解的角度,学习一下 SpringBoot。

罗列的内容较大,使用时只需要有个大概印象即可。

SpringBoot 中基本注解

新建一个空应用时,遇到的注解有:

SpringBootApplication

SpringBootApplication 是一个组合注解,包含下列 3 个注解:

同时声明了 4 个接口

用来灵活的配置不进行 auto-configuration 的类,和扫描注解的类。

SpringBootConfiguration

表明 SpringBoot 的配置可以从 Configuration 中自动得到。

被标注的类等于在spring的XML配置文件中(applicationContext.xml),装配所有bean事务,提供了一个spring的上下文环境

应用应该只包含一个 SpringBootConfiguration,基本所有的 SpringBoot 应用都会继承这个类。

Configuration

Configuration 表明类中定义了一些 Bean 方法,可以通过 Spring 来管理生成 Bean 的定义和运行时的请求。

@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        // instantiate, configure and return bean ...
    }
}

Bean 的发现

Spring 怎么发现 Bean 呢?

AnnotationConfigApplicationContext

第一种方法是通过 AnnotationConfigApplicationContext,示例如下:

   AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
   ctx.register(AppConfig.class);
   ctx.refresh();
   MyBean myBean = ctx.getBean(MyBean.class);
   // use myBean ...

spring beans xml

第二种方式是通过 spring 的 <beans> xml,如下:

   <beans>
     <context:annotation-config/>
     <bean class="com.acme.AppConfig"/>
  </beans>

SomeBean

第三种方式是通过组件扫描,如下:

 @Configuration
 public class AppConfig {
     private final SomeBean someBean;

     public AppConfig(SomeBean someBean) {
         this.someBean = someBean;
     }

     // @Bean definition using "SomeBean"
 }

也可以通过 ComponentScan 注解来扫描

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

Bean 的调用

怎么样在外面使用呢?

通过 Environment API

方法一:通过 Environment API,如下:

 @Configuration
 public class AppConfig {

     @Autowired Environment env;

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

也可以同时通过 PropertySource 来指定 Configuration 中的配置,示例如下:

 @Configuration
 @PropertySource("classpath:/com/acme/app.properties")
 public class AppConfig {

     @Inject Environment env;

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

通过 Value 注释

方法二:通过 @Value 注解,示例如下:

 @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 后,这样使用就很方便了。

编写 Configuration 类

怎么样编写一个 Configuration 类呢?

import 注解

方法一:通过 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,可以同时使用 AppConfig 和引入的 DatabaseConfig

new AnnotationConfigApplicationContext(AppConfig.class);

通过 Profile 注解

方法二,通过 @Profile 注解

通过 @Profile 来表明只有 profile(s)活跃时,才来处理。

@Profile("development")
 @Configuration
 public class EmbeddedDatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return embedded DataSource
     }
 }

 @Profile("production")
 @Configuration
 public class ProductionDatabaseConfig {

     @Bean
     public DataSource dataSource() {
         // instantiate, configure and return production DataSource
     }
 }

也可以通过 @Bean 注释,来实现 profile 的条件执行。

@Configuration
 public class ProfileDatabaseConfig {

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

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

通过 Spring xml 和 ImportResource 注解

方法三,通过 Spring xml 和 ImportResource 注解,如:

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

     @Inject DataSource dataSource; // from XML

     @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 即可。

lazy 启动

默认情况下,@Bean 方法会在容器启动时初始化。如果不想这样做的话,可以通过增加 @Lazy 注解,来实现 lazy 初始化。

在测试中使用

示例

@RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration(classes={AppConfig.class, DatabaseConfig.class})
 public class MyTests {

     @Autowired MyBean myBean;

     @Autowired DataSource dataSource;

     @Test
     public void test() {
         // assertions against myBean ...
     }
 }

启用内置 Spring 特征

通过 @Enable 注解来启动 Spring 特征,比如:

Component

表明这个类,在从包路径搜索类的注解时,可以被搜索到。

EnableAutoConfiguration

允许 SpringBoot 根据应用中声明的依赖,对 Spring 框架上下文的进行自动配置。

使用 SpringBootApplication 时,会自动启用 EnableAutoConfiguration。不过在类中多写一个 EnableAutoConfiguration 也不会有副作用。

其中提供了 exclude()excludeName() 方法,

AutoConfigurationPackage

表明本注解类会使用 AutoConfigurationPackages 进行注册。

Import

表明需要引入的 Configuration 配置类

ComponentScan

表明 Configuration 类中使用中组件扫描指令。

组件扫描,可自动发现和装配Bean,默认扫描SpringApplication的run方法里的Booter.class所在的包路径下文件,所以最好将该启动类放到根包路径下

详情略

AliasFor

AliasFor 设置注解的别名。

使用场景

字段别名

示例如下,@ContextConfiguration 中,locations 和 value 互为别名。

public @interface ContextConfiguration {

    @AliasFor("locations")
    String[] value() default {};

    @AliasFor("value")
    String[] locations() default {};

    // ...
 }

字段属性别名

示例如下,

 @ContextConfiguration
 public @interface XmlTestConfig {

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xmlFiles();
 }

@XmlTestConfig 中,xmlFiles@ContextConfigurationlocations 的别名,即 xmlFiles 参数覆盖了 @ContextConfiguration 中的 locations 属性。

字段属性字段别名

就是上面两种情况的综合体

@ContextConfiguration
 public @interface MyTestConfig {

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] value() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] groovyScripts() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xmlFiles() default {};
 }

其中 value()groovyScripts()xmlFiles() 互为别名,同是也都是 ContextConfigurationlocations 的别名。

注解中隐式别名

@MyTestConfig
 public @interface GroovyOrXmlTestConfig {

    @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
    String[] groovy() default {};

    @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
    String[] xml() default {};
 }

@GroovyOrXmlTestConfig 中,groovy 覆盖了 @MyTestConfig 中的 groovyScripts 属性,xml 覆盖了 @ContextConfiguration 中的 locations 属性,其中 groovyxml 又互为别名,均覆盖了 @ContextConfiguration 中的 locations 属性。

参考

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3012 引用 • 8158 回帖 • 548 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...