43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】
【视频&交流平台】
http://study.163.com/course/introduction.htm?courseId=1004329008&utm_campaign=commission&utm_source=400000000155061&utm_medium=share
http://study.163.com/course/introduction.htm?courseId=1004638001&utm_campaign=commission&utm_source=400000000155061&utm_medium=share
https://gitee.com/happyangellxq520/spring-boot
http://412887952-qq-com.iteye.com/blog/2321532
在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库。那么这一节我们将要介绍怎么进行多数据源动态切换。添加本文实现的代码之后,只需要配置要数据源就可以直接通过注解使用,在实际使用的时候特别的简单。那么本章主要分以下几个步骤进行实战。
(2)在pom.xml添加依赖包;
(3)编写启动类App.java
(4)编写配置文件application.properties;
(5)动态数据源路由类;
(6)注册多数据源;
(7)测试类测试;
接下来我们看看每一步具体的实现吧:
(1)新建maven java project;
新建一个maven project,取名为:spring-boot-multi-ds
(2)在pom.xml添加依赖包;
在pom.xml文件中加入依赖的库文件,主要是spring boot基本的,数据库驱动,spring-jpa支持即可,具体pom.xml文件如下:
<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>
<groupId>com.kfit</groupId>
<artifactId>spring-boot-multids</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-multids</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- jdk版本号,这里需要你本地进行的jdk进行修改,这里angel使用的是1.8的版本. -->
<java.version>1.8</java.version>
</properties>
<!--
spring boot 父节点依赖,
引入这个之后相关的引入就不需要添加version配置,
spring boot会自动选择最合适的版本进行添加。
在这里使用的1.3.3版本,可能目前官方有最新的版本了,大家可以
使用最新的版本。
-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
<!-- 单元测试包,在这里没有使用到. -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- spring boot web支持:mvc,aop...
这个是最基本的,基本每一个基本的demo都是需要引入的。
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql驱动.
我们的demo是多数据源,在这里使用Mysql数据库.
-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- spring jpa
spring jpa中带有自带的tomcat数据连接池;
在代码中我们也需要用到.
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
</project>
在上面的配置文件中都有相应的解释,大家可以自己解读下。
(3)编写启动类App.java
编写spring boot的启动类:
com.kfit.App:
package com.kfit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
*
* @author Angel(QQ:412887952)
* @version v.0.1
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
(4)编写配置文件application.properties;
在这里主要是多数据源和jpa的配置:
src/main/resources/application.properties:
########################################################
###配置文件包括1个主数据源和多个数据源,
###其中主数据源在Spring中的beanName默认为dataSource,
###另外几个数据源的beanName分包为:ds1、ds2、ds3
###其中datasource的type属性可以具体指定到我们需要的数据源上面,
###不指定情况下默认为:org.apache.tomcat.jdbc.pool.DataSource
###当然你也可以把这些数据源配置到主dataSource数据库中,然后读取数据库生成多数据源。当然这样做的必要性并不大,难不成数据源还会经常变吗。
########################################################
# 主数据源,默认的
#spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
# 更多数据源
custom.datasource.names=ds1,ds2,ds3
#custom.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
custom.datasource.ds1.driverClassName =com.mysql.jdbc.Driver
custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1
custom.datasource.ds1.username=root
custom.datasource.ds1.password=root
#custom.datasource.ds2.type=com.zaxxer.hikari.HikariDataSource
custom.datasource.ds2.driverClassName =com.mysql.jdbc.Driver
custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test
custom.datasource.ds2.username=root
custom.datasource.ds2.password=root
#custom.datasource.ds3.type=com.zaxxer.hikari.HikariDataSource
custom.datasource.ds3.driverClassName =com.mysql.jdbc.Driver
custom.datasource.ds3.url=jdbc:mysql://localhost:3306/test
custom.datasource.ds3.username=root
custom.datasource.ds3.password=root
# 下面为连接池的补充设置,应用到上面所有数据源中
spring.datasource.maximum-pool-size=100
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
spring.datasource.validation-query=SELECT 1
spring.datasource.test-on-borrow=false
spring.datasource.test-while-idle=true
spring.datasource.time-between-eviction-runs-millis=18800
########################################################
### Java Persistence Api
########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
#[org.hibernate.cfg.ImprovedNamingStrategy #org.hibernate.cfg.DefaultNamingStrategy]
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.DefaultNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
(5)动态数据源路由类;
动态数据源能进行自动切换的核心就是spring底层提供了AbstractRoutingDataSource类进行数据源的路由的,我们主要继承这个类,实现里面的方法即可实现我们想要的,这里主要是实现方法:determineCurrentLookupKey(),而此方法只需要返回一个数据库的名称即可,所以我们核心的是有一个类来管理数据源的线程池,这个类才是动态数据源的核心处理类。还有另外就是我们使用aop技术在执行事务方法前进行数据源的切换。所以这里有几个需要编码的类,具体如下:
动态数据源上下文>com.kfit.config.datasource.DynamicDataSourceContextHolder:
package com.kfit.config.datasource.dynamic;
import java.util.ArrayList;
import java.util.List;
/**
* 动态数据源上下文.
*
* @author Angel(QQ:412887952)
* @version v.0.1
*/
public class DynamicDataSourceContextHolder {
/*
* 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
/*
* 管理所有的数据源id;
* 主要是为了判断数据源是否存在;
*/
public static List<String> dataSourceIds = new ArrayList<String>();
/**
* 使用setDataSourceType设置当前的
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
/**
* 判断指定DataSrouce当前是否存在
*
* @param dataSourceId
* @return
* @author SHANHY
* @create 2016年1月24日
*/
public static boolean containsDataSource(String dataSourceId){
return dataSourceIds.contains(dataSourceId);
}
}
数据源路由类>com.kfit.config.datasource.dynamic.DynamicDataSource:
package com.kfit.config.datasource.dynamic;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源.
* @author Angel(QQ:412887952)
* @version v.0.1
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/*
* 代码中的determineCurrentLookupKey方法取得一个字符串,
* 该字符串将与配置文件中的相应字符串进行匹配以定位数据源,配置文件,即applicationContext.xml文件中需要要如下代码:(non-Javadoc)
* @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
*/
@Override
protected Object determineCurrentLookupKey() {
/*
* DynamicDataSourceContextHolder代码中使用setDataSourceType
* 设置当前的数据源,在路由类中使用getDataSourceType进行获取,
* 交给AbstractRoutingDataSource进行注入使用。
*/
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
指定数据源注解类>com.kfit.config.datasource.dynamic.TargetDataSource:
package com.kfit.config.datasource.dynamic;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 在方法上使用,用于指定使用哪个数据源
* @author Angel(QQ:412887952)
* @version v.0.1
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
切换数据源Advice>com.kfit.config.datasource.dynamic.DynamicDataSourceAspect:
package com.kfit.config.datasource.dynamic;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 切换数据源Advice
* @author Angel(QQ:412887952)
* @version v.0.1
*/
@Aspect
@Order(-10)//保证该AOP在@Transactional之前执行
@Component
public class DynamicDataSourceAspect {
/*
* @Before("@annotation(ds)")
* 的意思是:
*
* @Before:在方法执行之前进行执行:
* @annotation(targetDataSource):
* 会拦截注解targetDataSource的方法,否则不拦截;
*/
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSourcetargetDataSource) throws Throwable {
//获取当前的指定的数据源;
String dsId = targetDataSource.value();
//如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
System.err.println("数据源[{}]不存在,使用默认数据源 > {}"+targetDataSource.value()+point.getSignature());
} else {
System.out.println("Use DataSource : {} > {}"+targetDataSource.value()+point.getSignature());
//找到的话,那么设置到动态数据源上下文中。
DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
}
}
@After("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSourcetargetDataSource) {
System.out.println("Revert DataSource : {} > {}"+targetDataSource.value()+point.getSignature());
//方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
(6)注册多数据源;
以上都是动态数据源在注入的时候使用的代码,其实很重要的一部分代码就是注册我们在application.properties配置的多数据源,这才是重点,这里我们使用
ImportBeanDefinitionRegistrar进行注册,具体的代码如下:
package com.kfit.config.datasource.dynamic;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
/**
* 动态数据源注册;
* @author Angel(QQ:412887952)
* @version v.0.1
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
//如配置文件中未指定数据源类型,使用该默认值
private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
private ConversionService conversionService = new DefaultConversionService();
private PropertyValues dataSourcePropertyValues;
// 默认数据源
private DataSource defaultDataSource;
private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>();
/**
* 加载多数据源配置
*/
@Override
public void setEnvironment(Environment environment) {
System.out.println("DynamicDataSourceRegister.setEnvironment()");
initDefaultDataSource(environment);
initCustomDataSources(environment);
}
/**
* 加载主数据源配置.
* @param env
*/
private void initDefaultDataSource(Environment env){
// 读取主数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
Map<String, Object> dsMap = new HashMap<String, Object>();
dsMap.put("type", propertyResolver.getProperty("type"));
dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName"));
dsMap.put("url", propertyResolver.getProperty("url"));
dsMap.put("username", propertyResolver.getProperty("username"));
dsMap.put("password", propertyResolver.getProperty("password"));
//创建数据源;
defaultDataSource = buildDataSource(dsMap);
dataBinder(defaultDataSource, env);
}
/**
* 初始化更多数据源
*
* @author SHANHY
* @create 2016年1月24日
*/
private void initCustomDataSources(Environment env) {
// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.");
String dsPrefixs = propertyResolver.getProperty("names");
for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源
Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");
DataSource ds = buildDataSource(dsMap);
customDataSources.put(dsPrefix, ds);
dataBinder(ds, env);
}
}
/**
* 创建datasource.
* @param dsMap
* @return
*/
@SuppressWarnings("unchecked")
public DataSource buildDataSource(Map<String, Object> dsMap) {
Object type = dsMap.get("type");
if (type == null){
type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource
}
Class<? extends DataSource> dataSourceType;
try {
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
String driverClassName = dsMap.get("driverClassName").toString();
String url = dsMap.get("url").toString();
String username = dsMap.get("username").toString();
String password = dsMap.get("password").toString();
DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dataSourceType);
returnfactory.build();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
returnnull;
}
/**
* 为DataSource绑定更多数据
* @param dataSource
* @param env
*/
private void dataBinder(DataSource dataSource, Environment env){
RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);
dataBinder.setConversionService(conversionService);
dataBinder.setIgnoreNestedProperties(false);//false
dataBinder.setIgnoreInvalidFields(false);//false
dataBinder.setIgnoreUnknownFields(true);//true
if(dataSourcePropertyValues == null){
Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties(".");
Map<String, Object> values = new HashMap<>(rpr);
// 排除已经设置的属性
values.remove("type");
values.remove("driverClassName");
values.remove("url");
values.remove("username");
values.remove("password");
dataSourcePropertyValues = new MutablePropertyValues(values);
}
dataBinder.bind(dataSourcePropertyValues);
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()");
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
// 将主数据源添加到更多数据源中
targetDataSources.put("dataSource", defaultDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
// 添加更多数据源
targetDataSources.putAll(customDataSources);
for (String key : customDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
}
// 创建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
//添加属性:AbstractRoutingDataSource.defaultTargetDataSource
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
}
}
这里还有一个步骤很重要,由于我们是使用的ImportBeanDefinitionRegistrar的方式进行注册的,所以我们需要在App.java类中使用@Import进行注册,具体改造之后的App.java代码如下:
package com.kfit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
import com.kfit.config.datasource.dynamic.DynamicDataSourceRegister;
/**
*
* @author Angel(QQ:412887952)
* @version v.0.1
*/
@SpringBootApplication
//注册动态多数据源
@Import({DynamicDataSourceRegister.class})
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
(7)测试类测试;
核心的代码都编写完毕了,当然我们肯定是需要编写代码进行测试下,我们才放心了。
com.kfit.demo.bean.Demo:
package com.kfit.demo.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* 测试demo类.
* @author Angel(QQ:412887952)
* @version v.0.1
*/
@Entity
public class Demo {
@Id @GeneratedValue
private longid;
private String name;
public long getId() {
return id;
}
public void setId(longid) {
this.id = id;
}
public String getName() {
return name;
}
publicvoid setName(String name) {
this.name = name;
}
@Override
public String toString() {
return"Demo [id=" + id + ", name=" + name + "]";
}
}
com.kfit.demo.dao.TestDao:
package com.kfit.demo.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import com.kfit.demo.bean.Demo;
@Service
public class TestDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 不指定数据源使用默认数据源
* @return
*/
public List<Demo> getList(){
String sql = "select *from Demo";
return (List<Demo>) jdbcTemplate.query(sql, new RowMapper<Demo>(){
@Override
public Demo mapRow(ResultSet rs, introwNum) throws SQLException {
Demo demo = new Demo();
demo.setId(rs.getLong("id"));
demo.setName(rs.getString("name"));;
returndemo;
}
});
}
/**
* 指定数据源
* 在对应的service进行指定;
* @return
* @author SHANHY
* @create 2016年1月24日
*/
public List<Demo> getListByDs1(){
/*
* 这张表示复制的主库到ds1的,在ds中并没有此表.
* 需要自己自己进行复制,不然会报错:Table 'test1.demo1' doesn't exist
*/
String sql = "select *from Demo1";
return (List<Demo>) jdbcTemplate.query(sql, new RowMapper<Demo>(){
@Override
public Demo mapRow(ResultSet rs, introwNum) throws SQLException {
Demo demo = new Demo();
demo.setId(rs.getLong("id"));
demo.setName(rs.getString("name"));;
returndemo;
}
});
}
}
com.kfit.demo.service.TestService :
package com.kfit.demo.service;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
import com.kfit.config.datasource.dynamic.TargetDataSource;
import com.kfit.demo.bean.Demo;
import com.kfit.demo.dao.TestDao;
@Service
public class TestService {
@Resource
private TestDao testDao;
/**
* 不指定数据源使用默认数据源
* @return
*/
public List<Demo> getList(){
returntestDao.getList();
}
/**
* 指定数据源
* @return
*/
@TargetDataSource("ds1")
public List<Demo> getListByDs1(){
returntestDao.getListByDs1();
}
}
com.kfit.demo.controller.TestController:
package com.kfit.demo.controller;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.kfit.demo.bean.Demo;
import com.kfit.demo.service.TestService;
@RestController
public class TestController {
@Resource
private TestService testService;
@RequestMapping("/test1")
public String test(){
// for(Demo d:testService.getList()){
// System.out.println(d);
// }
for(Demo d:testService.getListByDs1()){
System.out.println(d);
}
return"ok";
}
}
好了,测试代码就这么多了,运行App.java进行测试把,访问:
http://127.0.0.1:8080/test1 查看控制台的打印。
这里需要提醒下,这种方式spring-jpa的方式好像不能自动路由,博主打算在之后的一篇文章介绍spring-jpa多数据源的问题。
【Spring Boot 系列博客】
视频&交流平台:
http://study.163.com/course/introduction.htm?courseId=1004329008
http://412887952-qq-com.iteye.com/blog/2321532
网易云课堂视频最新更新:
第十一章 Spring Boot 日志
1、spring boot日志—理论
2、Spring Boot日志-logback
3、Spring Boot日志-log4j2
第十二章 Spring Boot 知识点2
1、spring boot 服务配置和部署
2、Spring Boot 定制URL匹配规则
历史章节:
第一章 快速开始
1、Spring Boot之Hello World
2、Spring Boot之Hello World访问404
第二章 Spring Boot之JSON
1、spring boot返回json数据
2、Spring Boot完美使用FastJson解析JSON数据
第三章 Spring Boot热部署
1、Spring Boot热部署(springloader)
2、springboot + devtools(热部署)
第四章 Spring Boot数据库
1、Spring Boot JPA/Hibernate/Spring Data概念
2、Spring Boot JPA-Hibernate
3、Spring Boot Spring Data JPA介绍
4、Spring Boot JdbcTemplate
5、Spring Boot集成MyBatis
第五章 web开发
1、全局异常捕捉
2、配置server信息
3、spring boot使用thymeleaf
4、Spring Boot 使用freemarker
5、Spring Boot添加JSP支持
第六章 定时任务
1、Spring Boot定时任务
2、Spring Boot 定时任务升级篇(动态修改cron参数)
3、Spring Boot 定时任务升级篇(动态添加修改删除定时任务)
4、Spring Boot 定时任务升级篇(集群/分布式下的定时任务说明)
5、Spring Boot Quartz介绍
6、Spring Boot Quartz在Java Project中使用
7、Spring Boot 集成Quartz普通使用
8、Spring Boot 集成Quartz升级版
9、Spring Boot 集成Quartz二次升级版
10、Spring Boot 集成Quartz-Job如何自动注入Spring容器托管的对象
第七章 Spring Boot MyBatis升级篇
1、Spring Boot MyBatis升级篇-注解
2、Spring Boot MyBatis升级篇-注解-自增ID
3、Spring Boot MyBatis升级篇-注解-增删改查
4、Spring Boot MyBatis升级篇-注解-分页查询
5、Spring Boot MyBatis升级篇-注解-分页PageHelper不生效
6、Spring Boot MyBatis升级篇-注解- mybatic insert异常:BindingException: Parameter 'name' not found
7、Spring Boot MyBatis升级篇-注解- #和$符号特别篇
8、Spring Boot MyBatis升级篇-注解-@Result
9、Spring Boot MyBatis升级篇-注解-动态SQL(if test)-方案一:<script>
10、Spring Boot MyBatis升级篇-注解-动态SQL(if test)-方案二:@Provider
11、Spring Boot MyBatis升级篇-注解-动态SQL-参数问题
12、Spring Boot MyBatis升级篇-注解-特别篇:@MapperScan和@Mapper
13、Spring Boot MyBatis升级篇-XML
14、Spring Boot MyBatis升级篇-XML-自增ID
15、Spring Boot MyBatis升级篇-XML-增删改查
16、Spring Boot MyBatis升级篇-XML-分页查询
17、Spring Boot MyBatis升级篇-XML-分页PageHelper不生效
18、Spring Boot MyBatis升级篇-XML-动态SQL(if test)
19、Spring Boot MyBatis升级篇-XML-注解-初尝试
20、Spring Boot MyBatis升级篇- pagehelper替换为pagehelper-spring-boot-starter
第八章 Spring Boot 知识点1
1、Spring Boot 拦截器HandlerInterceptor
2、Spring Boot启动加载数据CommandLineRunner
3、Spring Boot环境变量读取和属性对象的绑定
4、Spring Boot使用自定义的properties
5、Spring Boot使用自定义的properties
6、Spring Boot使用@SpringBootApplication
7、Spring Boot 监控和管理生产环境
第十章 Spring Boot 打包部署
1、Spring Boot打包部署((提供Linux的sh文件))
第十一章 Spring Boot 日志
1、spring boot日志—理论
2、Spring Boot日志-logback
3、Spring Boot日志-log4j2
转自 http://412887952-qq-com.iteye.com/blog/2303075
43. Spring Boot动态数据源(多数据源自动切换)【从零开始学Spring Boot】的更多相关文章
- (37)Spring Boot集成EHCache实现缓存机制【从零开始学Spring Boot】
[本文章是否对你有用以及是否有好的建议,请留言] 写后感:博主写这么一系列文章也不容易啊,请评论支持下. 如果看过我之前(35)的文章这一篇的文章就会很简单,没有什么挑战性了. 那么我们先说说这一篇文 ...
- (15)Spring Boot使用Druid和监控配置【从零开始学Spring Boot】
Spring Boot 系列博客] 更多查看博客:http://412887952-qq-com.iteye.com/blog Spring Boot默认的数据源是:org.apache.tomcat ...
- (4)Spring Boot使用别的json解析框架【从零开始学Spring Boot】
此文章已经废弃,请看新版的博客的完美解决方案: 78. Spring Boot完美使用FastJson解析JSON数据[从零开始学Spring Boot] http://412887952-qq-co ...
- (35)Spring Boot集成Redis实现缓存机制【从零开始学Spring Boot】
[本文章是否对你有用以及是否有好的建议,请留言] 本文章牵涉到的技术点比较多:Spring Data JPA.Redis.Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对 ...
- (33)Spring Boot 监控和管理生产环境【从零开始学Spring Boot】
[本文章是否对你有用以及是否有好的建议,请留言] spring-boot-actuator模块提供了一个监控和管理生产环境的模块,可以使用http.jmx.ssh.telnet等拉管理和监控应用.审计 ...
- (38)Spring Boot分布式Session状态保存Redis【从零开始学Spring Boot】
[本文章是否对你有用以及是否有好的建议,请留言] 在使用spring boot做负载均衡的时候,多个app之间的session要保持一致,这样负载到不同的app时候,在一个app登录之后,而访问到另外 ...
- (23)Spring Boot启动加载数据CommandLineRunner【从零开始学Spring Boot】
[Spring Boot 系列博客] )前言[从零开始学Spring Boot] : http://412887952-qq-com.iteye.com/blog/2291496 )spring bo ...
- 15、Spring Boot使用Druid和监控配置【从零开始学Spring Boot】
转载:http://blog.csdn.net/linxingliang/article/details/52001740目录(?)[-] 1添加Maven依赖 或jar包 2配置数据源相关信息 3 ...
- (34)Spring Boot的启动器Starter详解【从零开始学Spring Boot】
Spring Boot应用启动器基本的一共有N(现知道的是44)种:具体如下: 1)spring-boot-starter 这是Spring Boot的核心启动器,包含了自动配置.日志和YAML. 2 ...
- Spring Boot集成Redis实现缓存机制【从零开始学Spring Boot】
转自:https://blog.csdn.net/linxingliang/article/details/52263763 spring boot 自学笔记(三) Redis集成—RedisTemp ...
随机推荐
- [POI2001]Peaceful Commission
题目大意: 有n个国家要派代表开会,每个国家有两个代表可供选择. 有m对代表有仇,不能同时开会. 若每个国家只能派一个代表开会,问是否存在一种方案,使得每个国家都能正常参会? 如果有,输出字典序最小的 ...
- 生成随机位数的UUID
1,生成UUID package com.jeeplus.common.utils; import java.util.UUID; /** * 生成唯一的UUID * * @author songya ...
- Spinner控件:Spinner绑定的监听是SetOnItemSelectedListener
(一) 1.效果图:ArrayAdapter可以不用设置 arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_it ...
- JavaScript中的模块化之AMD和CMD
前言: 为什么我们需要模块化开发,模块化开发的好处有哪些? 首先我们先说一下非模块化的开发方式带来的弊端. 非模块化开发中会导致一些问题的出现,变量和函数命名可能相同,会造成变量污染和冲突,并且出错时 ...
- Error (10663): Verilog HDL Port Connection error at led_demo.v(6): output or inout port "led" must be connected to a structural net expression
错误现象:
- [Git] 根据commiter过滤该用户的所有提交
git log --pretty=oneline --author="xxxx" -(n) 仅显示最近的 n 条提交 --since,--after 仅显示指定时间之后的提交 -- ...
- unity GPU bound or CPU bound
unity判断GPU CPUbound android 用unity profiler 里面的cpu时间 xcode有直接的显示
- ZOJ 3526 Weekend Party
Weekend Party Time Limit: 2 Seconds Memory Limit: 65536 KB As the only Oni (a kind of fabulous ...
- 利用forever在Linux上实现Node.js项目自启动
在一台计算机上手动跑Node项目简单,node xx.js就搞定了,想让Node项目后台执行,尽管不能直接用node命令搞定,可是在安装了forever这个包以后.还是非常轻松的.只是要是在远程ser ...
- 火车票抢票API 根据乘客的车次与座席要求快速订票出票
火车票抢票API 根据乘客的车次与座席要求快速订票出票:https://www.juhe.cn/docs/api/id/257 1.站站查询 接口地址:http://v.juhe.cn/grabTic ...