前言
SpringBoot的自动配置确实方便了操作,简化了很多配置。然而作为一个有追求的人,我们既要知其然,也要知其所以然。所以接下来我就以Spring AOP Starter为例来说明下AutoConfigure机制是如何生效的。
Spring Framework和SpringBoot如何使用AOP
首先来回忆下Spring是如何使用AOP的。
- 引入相关jar
- 引入
<aop:aspectj-autoproxy/>
或者使用@EnableAspectJAutoProxy
注解
SpringBoot是如何使用呢?引入如下依赖即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
源码解析
知识复习完毕,让我们看下源码吧
spring-boot-starter-aop源码
打开源码后发现这个starter只有一个pom文件,里面都是什么内容呢?
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-starter-aop</artifactId>
<name>Spring Boot AOP Starter</name>
<description>Starter for aspect-oriented programming with Spring AOP and AspectJ</description>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
</dependencies>
</project>
可以看到,其中有三个依赖。而spring-boot-starter这个是所有starter都会有的,其他的两个依赖则很明显是Spring AOP的两个依赖。
spring-boot-starter源码
接着打开spring-boot-starter源码看下,原来也只有一个pom.xml。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>${revision}</version>
</parent>
<artifactId>spring-boot-starter</artifactId>
<name>Spring Boot Starter</name>
<description>Core starter, including auto-configuration support, logging and YAML</description>
<properties>
<main.basedir>${basedir}/../../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
spring-boot
这个jar打开看下,可以看到SpringApplication
这个很熟悉的类,可以确定是Spring Boot的启动入口,此处不做展开。
spring-boot-autoconfigure
这个则是我们此次博文的主角。打开看下,可以看到诸如elasticsearch、redis、jdbc、web、mail等包。打开我们的aop看下,只有一个AopAutoConfiguration,代码如下:
@Configuration
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
AnnotatedElement.class })
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
在这里,我们看到了熟悉的@Configuration
和@EnableAspectJAutoProxy
。看样子SpringBoot就是在此处引入了AOP的支持。
生效条件
在这个类上,我们看到了两个条件:
- @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,AnnotatedElement.class })
这个比较直观,意思就是存在这些类的时候此配置才生效,其实际的作用在于检测是否存在对应的依赖 - @ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true)
这个则是spring.aop.auto为true才生效。注意matchIfMissing这个属性,从这个直白的命名上可以看出作用:如果此配置缺失则生效,等同于默认生效。
@ConditionalOnXX
打开org.springframework.boot.autoconfigure.condition
包看下,有大量的此类注解,命名也比较直白。作用如下:
注解 | 生效条件 |
---|---|
ConditionalOnBean | BeanFactory存在对应实例 |
ConditionalOnClass | classpath中存在对应类文件 |
ConditionalOnCloudPlatform | 匹配指定云平台 |
ConditionalOnExpression | 满足对应表达式 |
ConditionalOnJava | JVM版本检测 |
ConditionalOnJndi | jndi检测 |
ConditionalOnMissingBean | BeanFactory不存在对应实例 |
ConditionalOnMissingClass | classpath中不存在对应类文件 |
ConditionalOnNotWebApplication | 非web环境 |
ConditionalOnProperty | 匹配属性 |
ConditionalOnResource | 匹配文件 |
ConditionalOnSingleCandidate | 指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean |
ConditionalOnWebApplication | 匹配web容器 |
总结
最后总结下生效流程:
- 所有的starter都依赖于spring-boot-starter,而spring-boot-starter则依赖了spring-boot-autoconfigure。
- spring-boot-autoconfigure内置了众多的xxAutoConfiguration类。这些类会通过@ConditionalOnXX注解来检测是否满足生效条件,条件满足时配置就会生效。
- 当我们引入starter时就引入了其对应依赖,所以引入对应的starter就激活了对应的xxAutoConfiguration,进而进行了功能激活。
彩蛋
可能有人好奇,比如为什么引入db的支持一定要写成这样:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: root
password: xxxxxx
有这个疑问的同学可以打开DataSourceProperties这个类看一下,你的问题就会得到解答。