SpringBoot-入门注解介绍

@SpringBootApplication

我们经常直接将@SpringBootApplication打在了主类上,其实更加清晰的写法应该是将主类和SpringBoot配置类分开,如下所示:

1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class SBConfiguration {
}

public class SBApplication {
public static void main(String args[]) throws Exception{
SpringApplication.run(SBConfiguration.class, args);
}
}

如此一来,就能比较清晰的看出主类SBApplication只是程序的入口,没有什么特殊的。调用了SpringApplication的静态方法run,并使用SpringBoot主配置类SBConfigration.class作为参数。 主配置类就是打上@SpringBootApplication注释的类,首先看一下注释SpringBootApplication的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Target({ElementType.TYPE}) //表示该注解只能用于类型
@Retention(RetentionPolicy.RUNTIME)//表示该注解的生命周期可以维持到运行时
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)})
public @interface SpringBootApplication {
}

这是一个复合注释,其中@SpringBootConfiguration代表了SpringBoot的配置类,除了测试时有些区别,大体上就是Spring标准@Configuration的替代品。

@EnableAutoConfiguration用于启动SpringBoot的自动配置机制,这是SpringBoot的核心特色之一,自动对各种机制进最大可能的进行配置。

@ComponentScan是Spring原来就有的注释,用于对指定的路径进行扫描,并将其中的@Configuration配置类加载。接下来分别对其一一介绍。

@SpringBootConfiguration

1
2
3
4
5
6
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}

从代码可见,其本质上就是一个@Configuration。唯一不同的地方是在测试时,如果打上了@SpringBootConfiguration注释,那么SpringBootTest中并不需要指定就可以自动加载该配置类;而当打上@Configuration时,需要通过@SpringBootTest(classes = SBConfiguration.class)来指定加载的SpringBoot配置类。

若不考虑测试时非要省略指定Configuration类的话,该注释可有可无。因为在作为参数传递给SpringApplication.run方法后,只要其中配置了@Bean方法,就会直接被认为是一个配置类进行加载处理,并不需要@Configuration来标识。

@EnableAutoConfiguration

EnableAutoConfiguration自动配置机制是SpringBoot的核心特色之一。可根据引入的jar包对可能需要的各种机制进进行默认配置。 该注释的定义如下:

1
2
3
4
5
6
7
8
9
10
@SuppressWarnings("deprecation")
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
..........
}

其中@AutoConfigurationPackage用来指示打了该注解的类的包(package)应该被注册到AutoConfigurationPackages中,以备后续扩展机制(例如JPA或Mybatis等)的实体扫描器使用。

@EnableAutoConfiguration真正核心的动作就是通过Import机制加载EnableAutoConfigurationImportSelector.selectImports函数返回的配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
、、org.springframework.boot.autoconfigure
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
try {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}

其中比较核心的动作为getCandidateConfigurations(annotationMetadata, attributes),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}

我们注意:

1
2
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());

这句,SpringFactoriesLoader是spring framework内部使用的通用的工厂加载机制,其可加载并实例化可能出现在classpath上的多个jar包中的META-INF/spring.factories文件中定义的指定类型的工厂,可视为一种类似于SPI的接口。

SpringBoot利用这种SPI接口实现了autoconfiguration机制:委托SpringFactoriesLoader来加载所有配置在META-INF/spring.factories中的org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值,spring-boot-autoconfiguration jar包中的META-INF/spring.factories中的EnableAutoConfiguration配置摘录如下:

1
2
3
4
5
6
7
8
9
10
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
..........................

其中我们可以看到相当多非常熟悉的自动配置类,例如AopAutoConfiguration、CacheAutoConfiguration等等。其中的每一个自动配置类都会在一定条件(@Condition)下启动生效,并对相关的机制进行默认自动的配置。这便是SpringBoot自动配置机制的核心功能所在。

@ComponentScan

这是spring-context原来就存在的注释,需要在@Configuration标注的类上标注,用来指示扫描某些包及其子包上的组件。可通过配置属性basePackageClasses、basePackages或value来指出需要扫描哪些包(包括其子包),如果没有指定任何一个属性值,则默认扫描当前包及其子包。

例如,在前面例子中,如果SBConfiguration所在的包是springbootext,那么由于SBConfiguration打了@ComponentScan注释,那么在springbootext、springbootext.service、springbootext.config等等地方定义的@Configuration、@Component、@Service、@Controller等等组件都可以直接被加载,无需额外配置。而在anotherpackage中定义的组件,无法被直接加载。可以通过设置扫描路径来解决:

1
2
3
4
@EnableAutoConfiguration
@ComponentScan(basePackages={"springbootext", "anotherpackage"})
public class SBConfiguration{
}

当然也可以通过借助3.2节中介绍的在spring.factories中定义扩展机制定义EnableAutoConfiguration来实现加载。 启动过程中@ComponentScan起作用的时机是在springcontext refresh主流程的invokeBeanFactoryPostProcessor阶段,也就是BeanFactory创建并准备完毕后通过BeanFactoryPostProcessors来进一步对beanFactory进行处理的阶段。

在该阶段,ConfigurationClassPostProcessor中对于Configuration类的处理里包括了识别其打的@ComponentScan注释,并委托ComponentScanAnnotationParser根据该注释的属性值进行组件扫描。将扫描生成的beanDefinitions注册到beanFactory中供下一个阶段创建beans。

@Conntroller

@Conntroller注解在类上,表名这个类是MVC里的Controller,并将其声明为Spring中的一个Bean,Dispatcher Servlet会自动扫描注解了@Conntroller的类,并将Web请求映射到注解了@ResquesrMapping的方法。

Ps: 在声明普通Bean时,使用@Component、@Service、@Repository和@Conntroller是等同的(@Service、@Repository和@Conntroller都组合了@Component元注解),但是在MVC声明控制器的时候,只能使用@Conntroller。

@RequestMapping

@RequestMapping注解用来映射Web请求(访问路径和参数)到处理类和方法的。可注解到方法上,也可以注解在类上(方法会继承类上的注解)。

@ResponseBody和@RequestBody

@ResponseBody该注解用于将Controller的方法返回的对象,注解到方法返回值前面或者方法前面,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。

解析:在使用@RequestMapping后,返回值通常解析为跳转路径,加上@Responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP 响应正文中。

@RequestBody注解用于读取http请求的内容(字符串),注解到想要获取的参数前面,通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。

@RequestMapping(value = “person/login”)

@ResponseBody

public Person login(@RequestBody Person person) {

// 将请求中的datas写入 Person 对象中

return person;

// 不会被解析为跳转路径,而是直接写入 HTTP 响应正文中

}

@RequestBody注解会根据content-type选择对应的MessageConverter对请求中的数据进行处理(与对象绑定或解绑)

ps:GetMapping 不支持@RequestBody

@PathVariable

@PathVariable用来接收路径参数,如/new/001,可接收001作为参数,此注解放置在参数前。

@RequestMapping(value = “person/profile/{id}/{name}/{status}”)

@ResponseBody

public Person porfile(@PathVariable int id, @PathVariable String name, @PathVariable boolean status) {

​ return new Person(id, name, status);

}

@RequestMapping(value = “/person/profile/{id}/{name}/{status}”) 中的 {id}/{name}/{status}与 @PathVariable int id、@PathVariable String name、@PathVariable boolean status一一对应,按名匹配。

@RequestParam

@ExceptionHandler和@ResponseStatus

@ExceptionHandler注解在方法上,捕获并处理controller中抛出的异常

当一个Controller中有多个HandleException注解出现时,那么异常被哪个方法捕捉呢?这就存在一个优先级的问题。

ExceptionHandler的优先级是:在异常的体系结构中,哪个异常与目标方法抛出的异常血缘关系越紧密,就会被哪个捕捉到。

@ResponseStatus可以注解到异常类上或者注解到具体的处理函数上。

@ControllerAdvice介绍

  • 是Spring3.2提供的新注解,注解在类上,通过@ControllerAdvice可以将对于控制器的全局配置放到同一个位置上。
  • @ControllerAdvice是一个@Component,使用context:component-scan扫描时也能扫描到。主要用于定义@ExceptionHandler,@InitBinder和@ModelAttribute方法,适用于所有使用@RequestMapping方法。
  • 据经验之谈,只有配合@ExceptionHandler最有用,其它两个不常用。