SpringBoot注入配置(2)
接下来就是讲一下SpringBoot注入配置的一些详细的东西,也是通过结合网上和其他视频学习总结出来的内容,全手敲实践虽然会有取自其他地方的内容。这里提前多谢大家的阅读并且有地方的问题批评指正即可。
文章目录
1、引言
在以前学习spring的时候,我们通常会创建一个xml文件。比如在配置连接池时,我们往往会以下进行创建:
<!--配置druid连接池>
<bean id = "dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value= "${jdbc.url}" />
<property name = "username" value="${jdbc.username}" />
<property name = "password" value="${jdbc.password}" />
当然,这种方法还要创建一个配置文件来编写连接池对应的password
、url
和username
。
那么学习springboot中,我们现在又该怎么面对这个问题(配置连接池)呢?
这就要了解spring框架的历史了。
2、spring的历史
以下内容借鉴参考自:Spring框架每个版本的特性及历史介绍
spring1.X的项目路径如下:
当时spring还处于完全xml格式的配置,对于程序员配置环境而言确实非常头疼。
spring2.X的源码项目结构路径如下:
在spring2.0中,增添了注解的支持。虽然目前我们添加的spring包依赖应该使用的版本差不多是5.0+,但是前期很多地方的学习还只是到2.X的内容范围。(不过了解两者的配置对日后对spring3.0及以上的了解应该会更加详细。)
到了spring3.X,我们开始增添Java类的配置,其结构已经跟我们目前maven依赖的5.X相类似了。
到了spring3.0以后,Spring的注解已经非常完善了,因此在这个版本内spring推荐使用完全的java配置来代替以前的xml。
3、注入
3.1 spring注解注入
spring注解配置主要靠java类和一些注解,当然常见的注解如下:
@Configuration
:声明一个类作为配置类,代替xml文件@Bean
:声明在方法上,将方法的返回值加入Bean容器,代替标签@value
:属性注入@PropertySource
:指定外部属性文件
1、引入druid连接池依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
2、创建一个jdbc.properties,编写jdbc属性
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/xaf
jdbc.username=root
jdbc.password=root
3、尝试编写代码
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.url}")
String url;
@Value("${jdbc.driverClassName}")
String driverClassName;
@Value("${jdbc.username}")
String username;
@Value("${jdbc.password}")
String password;
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
@Configuration`是声明我们这个类是一个配置类。
`@PropertySource("classpath:jdbc.properties")`指定属性文件路径是:`classpath:jdbc.properties`
通过@value属性注入值
通过@Bean将`dataSource()`方法声明成一个注册Bean的方法,Spring会自动调用该方法,将方法的返回值加入Spring容器中。默认的对象名id=方法名。
4、注入@Autowired
比如说我在下面创建一个测试:
@RestController
public class HelloController {
@Autowired
private DataSource dataSource;
@GetMapping("hello")
public String hello() {
return "hello, spring boot!" + dataSource;
}
}
此时就将第3步的内容注入到DataSource中来。之后测试运行时可以通过debug来查看是否注入成功。
3.2 springBoot属性注入
在前面的内容我们看着使用之前没用过的注解的方式来完成属性注入,而这里我们来通过使用springBoot来提供新的属性注入的方式,它支持各种java基本数据类型和复杂类型的注入。
1、新建一个类,用来进行属性注入:
@ConfigurationProperties(prefix = "jdbc")
public class JdbcProperties {
private String url;
private String driverClassName;
private String username;
private String password;
// ... 略
// getters 和 setters
}
首先使用@ConfigurationProperties(prefix = "jdbc")
注解声明当前类为属性读取类。其中的prefix读取文件时,读取的数据要有jdbc为前缀的值。
在类上定义各个属性,名称必须与属性文件中 jdbc.
后面部分一致。注意有个点。
当然我们如果没有指定属性文件的地址,所以我们需要把jdbc.properties
名称改为 application.properties
,这是SpringBoot默认读取的属性文件名:
2、创建另一个类使用属性
@Configuration
@EnableConfigurationProperties(JdbcProperties.class)
public class JdbcConfig {
@Bean
public DataSource dataSource(JdbcProperties jdbc) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(jdbc.getUrl());
dataSource.setDriverClassName(jdbc.getDriverClassName());
dataSource.setUsername(jdbc.getUsername());
dataSource.setPassword(jdbc.getPassword());
return dataSource;
}
}
通过 @EnableConfigurationProperties(JdbcProperties.class)
来声明要使用 JdbcProperties 这个类的对象
然后你可以通过以下方式注入JdbcProperties:
- @Autowired注入
@Autowired
private JdbcProperties prop;
- 构造函数注入
private JdbcProperties prop;
public JdbcConfig(Jdbcproperties prop){
this.prop = prop;
}
- 声明有@Bean的方法注入参数
@Bean
public Datasource dataSource(JdbcProperties prop){
// ...
}
3.3 springBoot属性注入—@ConfigurationProperties的优点
- Relaxed binding:即松散绑定。它不严格要求属性文件中的属性名与成员变量名一致。支持驼峰,中划线,下划线等等转换,甚至支持对象引导。
比如:user.friend.name:代表的是user对象中的friend属性中的name属性,显然friend也是对象。@value注解就难以完成这样的注入方式。
- meta-data support:元数据支持,帮助IDE生成属性提示(写开源框架会用到)。
3.4 优化
基于如上的注入方式,springboot还能更加优化的注入。
比如说对于一段属性只有一个bean使用,就可以直接在需要的地方声明即可:
@Configuration
public class JdbcConfig {
@Bean
// 声明要注入的属性前缀,SpringBoot会自动把相关属性通过set方法注入到DataSource中
@ConfigurationProperties(prefix = "jdbc")
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
return dataSource;
}
}
我们直接把 @ConfigurationProperties(prefix = "jdbc")
声明在需要使用的 @Bean
的方法上,然后SpringBoot就会自动调用这个Bean方法中(此处是DataSource)的set方法,然后完成注入。使用的前提是:该类必须有对应属性的set方法!
4、自动配置原理
首先我们就从我们上面介绍的和Application启动类的注解进行简单的介绍。
4.1 @SpringBootApplication
该注解一般放置于一个项目的启动器上,即Application类中。这个注解是用来将启动类注入到容器中,定义容器扫描的范围,加载classpath环境的一些bean。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
...
}
最开始所标注的粗体字,分别就是由如上代码的第一行到第三行的注解实现完成。
4.1.1 @SpringBootConfiguration
该注解说明这是一个springboot的配置类,再次进入:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
我们在第四行看到了全注解配置@Configuration
,说明这个启动器是一个配置类。再次点击进入:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
第四行中的@Component
就是说明将一个bean注入到容器中。
补充:在我们一般学习spring中主要学习的四个注解中有提及:
@controller: controller控制器层(注入服务)
@service : service服务层(注入dao)
@repository : dao持久层(实现dao访问)
@component: 标注一个类为Spring容器的Bean,(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)
这就是能够实现将启动类注入到容器的原因,并且详细化将其声明成springboot配置类。
4.1.2 @EnableAutoConfiguration
点击进去可能并不一定看的明白上面注解的含义,这里截取官方的说明:
/**
* Enable auto-configuration of the Spring Application Context, attempting to guess and
* configure beans that you are likely to need. Auto-configuration classes are usually
* applied based on your classpath and what beans you have defined. For example, if you
* have {@code tomcat-embedded.jar} on your classpath you are likely to want a
* {@link TomcatServletWebServerFactory} (unless you have defined your own
* {@link ServletWebServerFactory} bean).
- 简单来说,就是这个注解的使用,springBoot会猜测你的使用,即猜测你想要怎么配置spring。(Auto-configuration classes are usually applied based on your classpath and what beans you have defined)
- 假设你添加了tomcat依赖在你的路径下,那么这个启动器会帮你添加tomcat的依赖。此时自动配置就知道你是要开发一个web应用(大致是这个意思)。
这就是Application能够定义容器扫描的范围的原因。因为它能够自行判断你要使用什么依赖。
4.1.3 @ComponentScan
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
- 配置组件扫描的指令。提供了类似与
<context:component-scan>
标签的作用。通过basePackageClasses或者basePackages属性来指定要扫描的包。如果没有指定这些属性,那么将从声 明这个注解的类所在的包开始,扫描包及子包。 - 因为我们的
@SpringBootApplication
注解声明的类就是main函数所在的启动类,因此扫描的包是该类所在包及其子包。所以,一般启动类会放在一个比较前的包目录中,这就是为什么将Application放到前面的缘故。
这也能间接实现加载classpath环境的一些bean的功能,我们通常会@Bean注解来声明一个类等等,使得spring能够自动调用一次,随后这个Spring将会将这个Bean对象放在自己的IOC容器中。而SpringIOC容器管理一个或者多个bean,这些bean都需要在@Configuration
注解下进行创建,在一个方法上使用@Bean注解就表明这个方法需要交给Spring进行管理。
4.2 默认配置原理
这里另外讲一下为什么sspringboot能够完成根据你引入的依赖来生效对应的默认配置呢?
首先进入spring-boot-autoconfiguration依赖下:
看的出来它能够完成的引入依赖远不止是web那么简单。
接下来进入WebMvcAutoConfiguration:
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
根据里面内容的注解选取一部分看,这里的的注解如上所示:
- @Configuration :声明这个类是一个配置类
- @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
这里的条件是OnClass
,也就是满足以下类存在:Servlet、DispatcherServlet、WebMvcConfigurer,其中 Servlet只要引入了tomcat依赖自然会有,后两个需要引入SpringMVC才会有。这里就是判断你是否引入了相 关依赖,引入依赖后该条件成立,当前类的配置才会生效!
- @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
这个条件与上面不同,是说环境中没有指定的Bean这个才生效。意思同上。
总结
以上就是对SpringBoot注入配置的介绍,后续就会根据跟其他主流框架的整合来进行简单的介绍。谢谢大家的阅读。