写在前面
本文参考自 Spring-Boot 官方文档,介绍了 Auto-configuration 常用特性,关于具体的说明可参考官方文档。
Spring Boot 自动配置尝试根据添加的jar依赖项自动配置 Spring 应用程序。例如,如果 HSQLDB 在您的类路径中,并且您没有手动配置任何数据库连接bean,那么Spring Boot将自动配置内存中的数据库。
自动配置需要通过 @EnableAutoConfiguration
注解来启用,仅仅只需要将该注解添加到一个 @Configuration
所注解的类上。使用 @SpringBootApplication
注解来启用也是一样的(因为@SpringBootApplication
注解本身添加了 @EnableAutoConfiguration
)。
应该只添加一个
@SpringBootApplication
或@EnableAutoConfiguration
注解。我们通常建议只向主@Configuration
类添加一个或另一个。
实现自定义自动配置
用例:实现一个自定义的 Derby 内嵌数据库(derby 的使用请参考这篇博文)。
-
导入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 驱动 --> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbyclient</artifactId> <version>10.11.1.1</version> </dependency>
spring-boot-starter
包是需要的,我曾在未导入该包的情况下运行程序,会出现无法加载配置文件(application.yml)。spring-boot-configuration-processor
是在我们使用@ConfigurationProperties
注解时,就会提醒我们导入的。 -
配置属性类
derby 的一些配置属性,以便在配置文件中使用:
/** * derby 数据库基本配置 * @author duofei * @date 2019/11/25 */ @ConfigurationProperties(prefix = "spring.derby") public class DerbyProperties { private String drivers = "org.apache.derby.jdbc.ClientDriver"; private String url = "jdbc:derby://localhost:1527/COREJAVA;create=true"; private String userName = ""; private String password = ""; public String getDrivers() { return drivers; } public void setDrivers(String drivers) { this.drivers = drivers; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
自动配置类
该类启用配置属性,一般在 Spring 的使用中,会结合
@Conditional
的一系列注解来使用,以便达到在不同条件下实现不同配置的目的。@Configuration @EnableConfigurationProperties(DerbyProperties.class) public class DerbyAutoConfiguration { @Bean public Connection derbyConnection(DerbyProperties derbyProperties) throws Exception{ System.setProperty("jdbc.drivers", derbyProperties.getDrivers()); return DriverManager.getConnection(derbyProperties.getUrl(), derbyProperties.getUserName(), derbyProperties.getPassword()); } }
-
启动类
该类作为启动类,但同时又负担了测试的功能,有关 derby 的使用疑惑,可以参考上面推荐的博文。
@SpringBootApplication public class Application implements ApplicationContextAware { private static ApplicationContext applicationContext; public static void main(String[] args) { SpringApplication.run(Application.class, args); final Connection derbyConnection = applicationContext.getBean("derbyConnection", Connection.class); try { run(derbyConnection); } catch (SQLException e) { e.printStackTrace(); } } public static void run(Connection conn) throws SQLException { try { Statement stat = conn.createStatement(); stat.executeUpdate("CREATE TABLE Greetings (Message CHAR(40))"); // Using ' not " stat.executeUpdate("INSERT INTO Greetings VALUES('hello')"); stat.executeUpdate("INSERT INTO Greetings VALUES('你好,世界')"); try(ResultSet result = stat.executeQuery("SELECT * FROM Greetings")){ //将光标移动到下一行,初始在第一行之前 while(result.next()){ System.out.println(result.getString("Message")); } } stat.executeUpdate("DROP TABLE Greetings"); }finally { conn.close(); } } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Application.applicationContext = applicationContext; } }
-
配置文件
spring: derby: user-name: duofei password: duofei001
启动测试类,我们可以在控制台取得
hello 你好,世界
输出。那如果通过 jar 包引入该类,仍然可用嘛?新建项目,引入该 jar 包。在新项目中,仍然使用以上的启动类和配置文件。结果与直接在 jar 包中执行并无区别。但是当你在新项目中,修改启动类的包路径(与 jar 包的包路径不同),你会发现,控制台抛出了异常,我们并不能找到这个 bean 了。
发生以上现象的原因是 @SpringBootApplication
注解的扫包路径只能在其所注解的类所在的路径及其子路径上,我们可以验证这个猜想,手动指定扫包路径如下:
@SpringBootApplication(scanBasePackages = "com.duofei")
,运行结果又正常了。难道我们一定需要这样做嘛?spring 自身实现的自动配置可没有要求我们声明包路径(那样一定糟糕透了)。
参考 spring 的自动配置,我们可以对以上用例进行修改。
修改原来的 jar 包。在 resource
目录下,新建目录 META-INF
,在该目录下创建 spring.factories
文件,文件内容如下:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.duofei.derby.DerbyAutoConfiguration
现在,即使新建项目并未指定扫包路径,程序依然可以正确执行了。
取代自动配置
自动配置非侵入性。在任何时候,您都可以开始定义自己的配置来替换自动配置的特定部分。例如,如果您添加自己的 DataSource
bean,则默认的嵌入式数据库支持将后退。
继续以上用例: 重新定义 Connection
bean,不使用配置文件的用户名和密码,仅仅使用固定的 duofei
和 007
。
启动类新增如下 bean 的配置:
@Bean
public Connection derbyConnection(DerbyProperties derbyProperties) throws Exception{
System.setProperty("jdbc.drivers", derbyProperties.getDrivers());
return DriverManager.getConnection(derbyProperties.getUrl(),
"duofei", "007");
}
重新启动程序,报错,无法定义相同名称的 bean ,提示可以修改 spring.main.allow-bean-definition-overriding = true
。在配置文件中按照提示修改,重新启动程序。程序正常运行,但通过断点,你会发现,后配置的 bean 根本就没有执行。所以,这不会是我们想要的。那么问题既然出在重名,那么我们可以重新定义之前的 jar 包中的 bean,为其添加以下注解:
@ConditionalOnMissingBean(name = {"derbyConnection"})
该注解表示只有不存在名为 "derbyConnection"
的 bean 时,才会注册该 bean,即实现了一个默认的作用。重新运行程序,启动类中新配置的 Connection
生效。
禁用自动配置
-
如果你发现你不想要的特定的自动配置类被应用,你可以使用
@EnableAutoConfiguration
的exclude
属性来禁用它们。 -
如果类不在类路径中,则可以使用注释的
exclude
属性并指定完全限定名。最后,您还可以使用spring.autoconfigure.exclude
属性来控制要排除的自动配置类列表。 -
@SpringBootApplication
注解的exclude
属性,也能达到同样的功能。
也就是说,我们能够在注解或者使用属性两个层面禁用自动配置功能。
继续以上用例: 禁用自动配置的 Connection
,使用重新实现的 Connection
。
需要先取消@ConditionalOnMissingBean(name = {"derbyConnection"})
注解。修改启动类上的 @SpringBootApplication
注解:@SpringBootApplication(exclude = DerbyAutoConfiguration.class)
。重新启动程序,我们会得到 DerbyProperties
的 bean 无法找到,因为该 bean 是在配置类上启用的,现在禁用了该配置类,相应的,这个 bean 也找不到了。禁用自动配置的目的达到了,不过发生了这种现象,也是我们在使用时,需要注意的一方面。