怎么新建springboot项目我在这里就不介绍了,首先我们先来看一下需要引入哪些主要的jar
1:pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
com.example
dome
0.0.1-SNAPSHOT
dome
Demo project for Spring Boot
UTF-8
UTF-8
1.8
1.1.2
8.0.7-dmr
2.1.8
1.0.5
2.9.0
1.2.45
1.18.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-aop
mysql
mysql-connector-java
${mysql-connector.version}
runtime
com.alibaba
druid
${druid.version}
com.baomidou
mybatis-plus
${mybatis-plus.version}
com.baomidou
mybatisplus-spring-boot-starter
${mybatisplus-spring-boot-starter.version}
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.5
com.baomidou
mybatis-plus-generator
3.0.7.1
org.apache.velocity
velocity-engine-core
2.0
org.freemarker
freemarker
2.3.23
com.ibeetl
beetl
2.2.5
org.springframework.boot
spring-boot-starter-test
test
org.hamcrest
hamcrest-all
1.3
test
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
${lombok.version}
provided
org.springframework.boot
spring-boot-starter-data-redis
redis.clients
jedis
${redis-clien.version}
com.alibaba
fastjson
${fast-json.version}
compile
org.apache.maven.plugins
maven-compiler-plugin
3.6.1
1.8
org.apache.maven.plugins
maven-surefire-plugin
2.20
true
org.springframework.boot
spring-boot-maven-plugin
src/main/resources
src/main/java
**/*.xml
2:application.yml
#自定义配置
example:
muti-datasource-open: true #是否开启多数据源(true/false)
redis-multi-open: false
#spring配置
spring:
profiles:
active: dev
#端口
server:
port: 8987
---
#主数据源
spring:
profiles: dev
datasource:
url: jdbc:mysql://127.0.0.1:3306/expo0917?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
username: root
password: root
#数据源 biz
biz:
datasource:
url: jdbc:mysql://127.0.0.1:3306/xm001dev?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
username: root
password: root
logging:
level:
ROOT: INFO
com:
example: DEBUG
file: D:/logs/app.log
---
#mybatis-plus配置
mybatis-plus:
mapper-locations: classpath*:com/example/dome/common/dao/repository/mapping/*.xml
typeAliasesPackage: >
com.example.dome.common.dao.entity
global-config:
id-type: 0 # 0:数据库ID自增 1:用户输入id 2:全局唯一id(IdWorker) 3:全局唯一ID(uuid)
db-column-underline: false
refresh-mapper: true
configuration:
map-underscore-to-camel-case: true
cache-enabled: true #配置的缓存的全局开关
lazyLoadingEnabled: true #延时加载的开关
multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性
logging:
level:
org.springframework.web.servlet: ERROR
---
#Redis
spring:
redis:
host: 192.168.11.200
porl: 6379
database: 0
password:
timeout: 6000ms
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
cache:
type: none
关于yml里面的自定义配置内容提示可以参考:https://blog.csdn.net/qq_34805041/article/details/100103980
3:Druid数据连接池配置
package com.example.dome.config.properties;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.JdbcConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
/**
* @Time 2019/8/28 18:54
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain 数据库数据源配置
* 说明:这个类中包含了许多默认配置,若这些配置符合您的情况,您可以不用管,若不符合,建议不要修改本类,建议直接在"application.yml"中配置即可
*/
@Component
@ConfigurationProperties(prefix = "spring.datasource")
public class DruidProperties {
private String url;
private String username;
private String password;
private String driverClassName = "com.mysql.cj.jdbc.Driver";
private Integer initialSize = 10;
private Integer minIdle = 3;
private Integer maxActive = 60;
private Integer maxWait = 60000;
private Boolean removeAbandoned = true;
private Integer removeAbandonedTimeout = 180;
private Integer timeBetweenEvictionRunsMillis = 60000;
private Integer minEvictableIdleTimeMillis = 300000;
private String validationQuery = "SELECT 'x'";
private Boolean testWhileIdle = true;
private Boolean testOnBorrow = false;
private Boolean testOnReturn = false;
private Boolean poolPreparedStatements = true;
private Integer maxPoolPreparedStatementPerConnectionSize = 50;
private String filters = "stat";
public void config(DruidDataSource dataSource) {
dataSource.setDbType(JdbcConstants.MYSQL);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClassName);
// 定义初始连接数
dataSource.setInitialSize(initialSize);
// 最小空闲
dataSource.setMinIdle(minIdle);
// 定义最大连接数
dataSource.setMaxActive(maxActive);
// 获取连接等待超时的时间
dataSource.setMaxWait(maxWait);
// 超过时间限制是否回收
dataSource.setRemoveAbandoned(removeAbandoned);
// 超过时间限制多长
dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout);
// 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
// 配置一个连接在池中最小生存的时间,单位是毫秒
dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
// 用来检测连接是否有效的sql,要求是一个查询语句
dataSource.setValidationQuery(validationQuery);
// 申请连接的时候检测
dataSource.setTestWhileIdle(testWhileIdle);
// 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能
dataSource.setTestOnBorrow(testOnBorrow);
// 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能
dataSource.setTestOnReturn(testOnReturn);
// 打开PSCache,并且指定每个连接上PSCache的大小
dataSource.setPoolPreparedStatements(poolPreparedStatements);
dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
// 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
// 监控统计用的filter:stat
// 日志用的filter:log4j
// 防御SQL注入的filter:wall
try {
dataSource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
}
}
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;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public Integer getInitialSize() {
return initialSize;
}
public void setInitialSize(Integer initialSize) {
this.initialSize = initialSize;
}
public Integer getMinIdle() {
return minIdle;
}
public void setMinIdle(Integer minIdle) {
this.minIdle = minIdle;
}
public Integer getMaxActive() {
return maxActive;
}
public void setMaxActive(Integer maxActive) {
this.maxActive = maxActive;
}
public Integer getMaxWait() {
return maxWait;
}
public void setMaxWait(Integer maxWait) {
this.maxWait = maxWait;
}
public Integer getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public Integer getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public Boolean getTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(Boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public Boolean getTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(Boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public Boolean getTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(Boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public Boolean getPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(Boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public Integer getMaxPoolPreparedStatementPerConnectionSize() {
return maxPoolPreparedStatementPerConnectionSize;
}
public void setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize) {
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
public Boolean getRemoveAbandoned() {
return removeAbandoned;
}
public void setRemoveAbandoned(Boolean removeAbandoned) {
this.removeAbandoned = removeAbandoned;
}
public Integer getRemoveAbandonedTimeout() {
return removeAbandonedTimeout;
}
public void setRemoveAbandonedTimeout(Integer removeAbandonedTimeout) {
this.removeAbandonedTimeout = removeAbandonedTimeout;
}
}
4:自定义注解类,用来使用在service方法上指定改方法要使用的数据源
package com.example.dome.common.annotion;
import java.lang.annotation.*;
/**
* @Time 2019/8/27 18:40
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain 自定义注解类
*
* java 四大元注解作用
* 1.@Target :用于描述注解的使用范围,也就是说使用了@Target去定义一个注解,
* 那么可以决定定义好的注解能用在什么地方
* 1.CONSTRUCTOR:用于描述构造器
* 2.FIELD:用于描述域
* 3.LOCAL_VARIABLE:用于描述局部变量
* 4.METHOD:用于描述方法
* 5.PACKAGE:用于描述包
* 6.PARAMETER:用于描述参数
* 7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
* 2.@Retention:用于描述注解的生命周期,也就是说这个注解在什么范围内有效,
* 注解的生命周期和三个阶段有关:源代码阶段、CLASS文件中有效、运行时有效,
* 故其取值也就三个值,分别代表着三个阶段
* 1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
* 2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
* 3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
* 3.@Documented:表示该注解是否可以生成到 API文档中。在该注解使用后,如果导出API文档,
* 会将该注解相关的信息可以被例如javadoc此类的工具文档化。
* 注意:Documented是一个标记注解,没有成员。
* 4.@Inherited:使用@Inherited定义的注解具备继承性
* 假设一个注解在定义时,使用了@Inherited,然后该注解在一个类上使用,如果这个类有子类,
* 那么通过反射我们可以从类的子类上获取到同样的注解、
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String name() default "";
}
5:定义多数据源枚举常量类
package com.example.dome.common.mutidatesource;
/**
* @Time 2019/8/27 18:40
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain 多数据源的常量类
*/
public interface DSEnum {
/**
* 核心数据库
*/
String DATA_SOURCE_CORE="dataSourceCore";
/**
* 其他业务数据库
*/
String DATA_SOURCE_BIZ="dataSourceBiz";
}
6:配置DataSource上下文,用来存储当前线程的数据源
package com.example.dome.common.mutidatesource;
/**
* @Time 2019/8/27 18:45
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain dataSource的上下文 用来存储当前线程的数据源类型
*/
public class DataSourceContextHolder {
private static final ThreadLocal contextHolder=new ThreadLocal();
/**
* @param dataSourceType 数据库类型
* @Description: 设置数据源类型
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
/**
* @Description: 获取数据源类型
*/
public static String getDataSourceType() {
return contextHolder.get();
}
/**
* @Description: 清除数据源类型
*/
public static void clearDataSourceType() {
contextHolder.remove();
}
}
7:定义动态数据源
package com.example.dome.common.mutidatesource;
/**
* @Time 2019/8/27 18:48
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain 动态数据源
*/
public class AbstractRoutingDataSource extends org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
8:最核心最重要的AOP类
package com.example.dome.common.aop;
import com.example.dome.common.annotion.DataSource;
import com.example.dome.common.mutidatesource.DSEnum;
import com.example.dome.common.mutidatesource.DataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @Time 2019/8/28 11:43
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain AOP切面类
*
*/
@Aspect
@Component
@ConditionalOnProperty(prefix = "example",name = "muti-datasource-open",havingValue = "true")
public class MultiSourceExAop implements Ordered {
Logger logger= LoggerFactory.getLogger(this.getClass());
/**
* 定义一个Annotation 类型的切点,当spring 扫描到自定义注解@DataSource,进入该切点
*/
@Pointcut(value = "@annotation(com.example.dome.common.annotion.DataSource)")
private void cut() {
}
/**
* 可以同时在所拦截cut()方法的前后执行一段逻辑
* @param point
* @return
* @throws Throwable
*/
@Around("cut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
Signature signature = point.getSignature();
MethodSignature methodSignature = null;
if (!(signature instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
methodSignature = (MethodSignature) signature;
Object target = point.getTarget();
Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
DataSource datasource = currentMethod.getAnnotation(DataSource.class);
if (datasource != null) {
DataSourceContextHolder.setDataSourceType(datasource.name());
logger.debug("设置数据源为:" + datasource.name());
} else {
DataSourceContextHolder.setDataSourceType(DSEnum.DATA_SOURCE_CORE);
logger.debug("设置数据源为:dataSourceCore");
}
try {
return point.proceed();
} finally {
logger.debug("清空数据源信息!");
DataSourceContextHolder.clearDataSourceType();
}
}
/**
* AOP事务顺序 AOP的顺序要早于spring的事务
* @return
*/
@Override
public int getOrder() {
return 1;
}
}
配置信息类
package com.example.dome.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.example.dome.common.mutidatesource.AbstractRoutingDataSource;
import com.example.dome.common.mutidatesource.DSEnum;
import com.example.dome.config.properties.DruidProperties;
import com.example.dome.config.properties.MutiDataSourceProperties;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.sql.SQLException;
import java.util.HashMap;
/**
* @Time 2019/8/28 15:33
* @Author GuiPing.Xu
* @Version 1.0.0
* @Explain
*/
@Configuration
@EnableTransactionManagement(order = 2)
@MapperScan(basePackages = {"com.example.dome.common.mapper"})
public class MybatisPlusConfig {
@Autowired
DruidProperties druidProperties;
@Autowired
MutiDataSourceProperties mutiDataSourceProperties;
/**
* 核心数据源
*/
private DruidDataSource coreDataSource() {
DruidDataSource dataSource = new DruidDataSource();
druidProperties.config(dataSource);
return dataSource;
}
/**
* 另一个数据源
*/
private DruidDataSource bizDataSource() {
DruidDataSource dataSource = new DruidDataSource();
druidProperties.config(dataSource);
mutiDataSourceProperties.config(dataSource);
return dataSource;
}
/**
* 单数据源连接池配置 通过@ConditionalOnProperty来控制Configuration是否生效
*/
@Bean
@ConditionalOnProperty(prefix = "example", name = "muti-datasource-open", havingValue = "false")
public DruidDataSource singleDatasource() {
return coreDataSource();
}
/**
* 多数据源连接池配置
*/
@Bean
@ConditionalOnProperty(prefix = "example", name = "muti-datasource-open", havingValue = "true")
public AbstractRoutingDataSource mutiDataSource() {
DruidDataSource coreDataSource = coreDataSource();
DruidDataSource bizDataSource = bizDataSource();
try {
coreDataSource.init();
bizDataSource.init();
} catch (SQLException sql) {
sql.printStackTrace();
}
AbstractRoutingDataSource dynamicDataSource = new AbstractRoutingDataSource();
HashMap
GitHub源代码地址:https://github.com/xuguipinga/springBootStudy/tree/master/multipleDataSources
本人也是第一次写,其中有一些代码也是借鉴一些大牛的,如有什么不合理的地方,希望可以指出来,多多交流,学习