spring boot自动配置和自定义配置
本文介绍spring boot的自动配置原理,redis自动配置示例,如何配置多数据源。
一、Spring Boot 自动配置分析
spring Boot很大的改变就是xml
配置改成配置类。并且为很多常用的中间件引入了自动配置,用户只需要依赖对应jar包并在properties种配置即可使用。
我们为spring boot项目添加一个启动类,使用@SpringBootApplication
注解,即可右键启动项目。先看看这个注解的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class))
public @interface SpringBootApplication {
...
}
继续找到@EnableAutoConfiguration
源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
对于这段代码,有几个需要关注的地方:
@Import
:功能等效于xml配置文件中的,xml中import引入的xml文件也将被解析,@import主要用来引入@Configuration注解的类。注意,如果要引入的就是xml配置问题,请使用@ImportResource。@AutoConfigurationPackage
:存储自动配置类的包通过@Import(EnableAutoConfigurationImportSelector.class)查看EnableAutoConfigurationImportSelector类的部分源码:
/**
*返回自动配置类的名字
*/
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;
}
自动配置类位于 META-INF/spring.factories
文件中,内容如下(只列出部分):
# Initializers
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration
以redis 为例子,在文件搜redis,发现配置类为:org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
,根据它找到源码:
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
.....
}
解读:
- @Configuration:表示声明一个或者多个方法,生成spring容器的bean。
- @ConditionalOnClass :注解参数对应的类(JedisConnection,RedisOperations,Jedis)必须存在,否则不解析该注解修饰的配置类。也就是说,不引入redis客户端相关包,自动配置不会生效。
- @EnableConfigurationProperties:让RedisAutoConfiguration以RedisProperties所定义的配置标准读取配置。
先看下RedisProperties
部分代码:
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
//application.properties中 spring.redis.port 的值会赋给port属性。
private int database = 0;
private String host = "localhost";
private String password;
private int port = 6379;
@Configuration
protected static class RedisConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
解读:
- 获取前缀为
spring.redis
的配置,如果没有配置,host 默认localhost,端口默认6379。 @ConditionalOnMissingBean(StringRedisTemplate.class)
: 如果StringRedisTemplate这个bean不存在配置才生效,意思就是说如果我们自己配置了StringRedisTemplate,自动配置就不会生效。
二、spring boot redis 自动配置使用示例
2.1 maven 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.2 添加配置
这儿使用redis伪集群,在properties
中添加如下配置:
spring.redis.cluster.nodes=192.168.99.100:7001,192.168.99.100:7002,192.168.99.100:7003,192.168.99.100:7004,192.168.99.100:7005,192.168.99.100:7006
spring.redis.pool.max-active=8
spring.redis.pool.max-idle=8
spring.redis.pool.max-wait=-1
spring.redis.pool.min-idle=0
spring.redis.timeout=0
2.3 使用
@SpringBootApplication
public class UserApplication implements CommandLineRunner {
@Autowired
private StringRedisTemplate redisTemplate;
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
String key = "name";
opsForValue.set(key, "erdaoya");
System.out.println("redis key " + key + ", value=" + opsForValue.get(key));
}
}
说明:
StringRedisTemplate
就相当于RedisTemplate<String,String>
,如果只是简单的key-value 字符串操作,推荐用前者。后者提供更丰富的功能,如对java对象序列化存储。
三、Spring Boot + Jdbc + Mybatis 多数据源自定义配置
3.1 添加依赖
<!--mysql-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter.version}</version>
</dependency>
3.2 编写配置类
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.user.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@Qualifier("secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.user.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "primarySqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/mybatis-config.xml"));
return bean.getObject();
}
@Bean(name = "primaryTransactionManager")
@Primary
public DataSourceTransactionManager transactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "primarySqlSessionTemplate")
@Primary
public SqlSessionTemplate sqlSessionTemplate(
@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:mybatis/mybatis-config.xml"));
return bean.getObject();
}
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "secondarySqlSessionTemplate")
public SqlSessionTemplate secondarySqlSessionTemplate(
@Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
可以看出,其实配置类跟xml相差无几。
3.3 添加配置
spring.datasource.user.primary.url=jdbc:mysql://192.168.99.100:3306/boot?useUnicode=true&characterEncoding=UTF-8
spring.datasource.user.primary.username=root
spring.datasource.user.primary.password=1234
spring.datasource.user.primary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.user.primary.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.user.secondary.url=jdbc:mysql://192.168.99.100:3306/boot?useUnicode=true&characterEncoding=UTF-8
spring.datasource.user.secondary.username=root
spring.datasource.user.secondary.password=1234
spring.datasource.user.secondary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.user.secondary.type=com.alibaba.druid.pool.DruidDataSource
3.4 使用
@Repository
public class UserDao {
@Autowired
@Qualifier("primarySqlSessionTemplate")
private SqlSessionTemplate primarySqlSessionTemplate;
@Autowired
@Qualifier("secondarySqlSessionTemplate")
private SqlSessionTemplate secondarySqlSessionTemplate;
public User selectUserFromDB1ById(Long id) {
return primarySqlSessionTemplate.selectOne("selectUserById", id);
}
public User selectUserFromDB2ById(Long id) {
return secondarySqlSessionTemplate.selectOne("selectUserById", id);
}
}
访问/health
监控端口,可以看到2个配置都UP
。