<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
配置
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=5000
单元测试
@SpringBootTest(classes = {App.class})
@RunWith(SpringRunner.class)
public class SpringRedisTest {
@Resource
private RedisTemplate<String,String> redisTemplate;
@Test
public void testRedis() throws Exception {
ValueOperations<String, String> ops = redisTemplate.opsForValue();
ops.set("name", "enjoy");
String value = ops.get("name");
System.out.println(value);
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
配置
## rabbitmq config
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=rabbit
spring.rabbitmq.password=123456
RabbitConfig
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
@Bean
public Queue firstQueue() {
// 创建一个队列,名称为:enjoy
return new Queue("enjoy");
}
}
创建消息的生产者
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class Sender {
@Resource
private AmqpTemplate rabbitTemplate;
public void send() {
rabbitTemplate.convertAndSend("enjoy", "this a messages !!!");
}
}
创建消息的消费者
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
//定义该类需要监听的队列
@RabbitListener(queues = "enjoy")
public class Receiver {
@RabbitHandler //指定对消息的处理
public void process(String msg) {
System.out.println("receive msg : " + msg);
}
}
新增单元测试
import cn.enjoy.App;
import cn.enjoy.mq.Sender;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = App.class)
public class RabbitmqTest {
@Resource
private Sender sender;
@Test
public void testRabbitmq() throws Exception {
sender.send();
}
}
Actuator是spring boot的一个附加功能,可帮助你在应用程序生产环境时监视和管理应用程序。可以使用HTTP的各种请求来监管,审计,收集应用的运行情况.特别对于微服务管理十分有意义
缺点:没有可视化界面(Spring cloud 还会用到这功能,就可以看到界面了)
pom
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
修改application.properties文件,启动监控端点
# 加载所有的端点/默认只加载了 info / health
management.endpoints.web.exposure.include=*
# 描述信息
info.blog-url=http://hgy.len
info.author=hgy
[email protected]@
重新启动,在地址栏输入
http://localhost:8080/actuator/info
Actuator访问路径
通过actuator/+端点名就可以获取相应的信息。
路径 | 作用 |
---|---|
/actuator/beans | 显示应用程序中所有Spring bean的完整列表。 |
/actuator/configprops | 显示所有配置信息。 |
/actuator/env | 陈列所有的环境变量。 |
/actuator/mappings | 显示所有@RequestMapping的url整理列表。 |
/actuator/health | 显示应用程序运行状况信息 up表示成功 down失败 |
/actuator/info | 查看自定义应用信息 |
不管是集成redis还是RabbitMQ,甚至是前面集成mybatis已经学习了很多starter,这些starter都是springboot为我们提供的一些封装,这些starter能非常方便快捷的增加功能,并不需要很多配置,即使需要配置也就在application.properties稍微配置下就可以了。
怎么创建属于自己的starter
前面已经使用过spring-boot-starter-data-redis,这个starter是用来集成redis的,那么接下来完成一个starter,这个starter也就集成下redis
新建一个项目,这个项目不需要web功能
pom
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.0.1version>
dependency>
dependencies>
创建一个RedisProperties用于加载Redis需要的配置,这里为简单起见,并没有设置密码
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
private String host;
private int port;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
}
创建一个配置类,这个配置类用于加载配置,并实例化Jedis客户端
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
@Configuration //开启配置
@ConditionalOnClass(Jedis.class)
@EnableConfigurationProperties(RedisProperties.class) // 开启使用映射实体对象
@ConditionalOnProperty//存在对应配置信息时初始化该配置类
(
prefix = "redis",//存在配置前缀redis
value = "enabled",//开启
matchIfMissing = true//缺失检查
)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean // bean缺失才创建
public Jedis jedis(RedisProperties redisProperties){
return new Jedis(redisProperties.getHost(), redisProperties.getPort());
}
}
自动化配置代码中有很多我们之前没有用到的注解配置,我们从上开始讲解
@Configuration:这个配置就不用多做解释了,我们一直在使用
@EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。(例如: @EnableConfigurationProperties(RedisProperties.class)
)
SpringBoot内置条件注解
有关**@ConditionalOnXxx**相关的注解这里要系统的说下,因为这个是我们配置的关键,根据名称我们可以理解为具有Xxx条件,当然它实际的意义也是如此,条件注解是一个系列,下面我们详细做出解释
@ConditionalOnBean:当SpringIoc容器内存在指定Bean的条件
@ConditionalOnClass:当SpringIoc容器内存在指定Class的条件
@ConditionalOnExpression:基于SpEL表达式作为判断条件
@ConditionalOnJava:基于JVM版本作为判断条件
@ConditionalOnMissingBean:当SpringIoc容器内不存在指定Bean的条件
@ConditionalOnMissingClass:当SpringIoc容器内不存在指定Class的条件
@ConditionalOnNotWebApplication:当前项目不是Web项目的条件
@ConditionalOnProperty:指定的属性是否有指定的值
@ConditionalOnResource:类路径是否有指定的值
@ConditionalOnSingleCandidate:当指定Bean在SpringIoc容器内只有一个,或者虽然有多个但是指定首选的Bean
@ConditionalOnWebApplication:当前项目是Web项目的条件
以上注解都是元注解**@Conditional**演变而来的,根据不用的条件对应创建以上的具体条件注解。
到目前为止我们还没有完成自动化配置starter,我们需要了解SpringBoot运作原理后才可以完成后续编码。
Starter自动化运作原理
在注解@SpringBootApplication上存在一个开启自动化配置的注解**@EnableAutoConfiguration**来完成自动化配置,注解源码如下所示:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
EnableAutoConfigurationImportSelector
是一个DeferredImportSelector
,由spring boot autoconfigure
从版本1.3开始,提供用来处理EnableAutoConfiguration
自动配置。EnableAutoConfigurationImportSelector继承自AutoConfigurationImportSelector,从 spring boot 1.5 以后,EnableAutoConfigurationImportSelector已经不再被建议使用,而是推荐使用 AutoConfigurationImportSelector。
在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能,而AutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包。我们可以先来看下spring-boot-autoconfigure包内的spring.factories文件内容,如下所示:
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;
}
// SpringFactoriesLoader#loadSpringFactories
public static finaString FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != nul?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : // here
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URur= urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
可以看到配置的结构形式是Key=>Value形式,多个Value时使用,隔开,那我们在自定义starter内也可以使用这种形式来完成,我们的目的是为了完成自动化配置,所以我们这里Key则是需要使用org.springframework.boot.autoconfigure.EnableAutoConfiguration
自定义 spring.factories
我们在src/main/resource目录下创建META-INF目录,并在目录内添加文件spring.factories,具体内容如下所示:
#配置自定义Starter的自动化配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=len.hgy.redis.RedisAutoConfiguration
目前为止自定义的starter已经开发完毕
pom
<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.0modelVersion>
<groupId>len.hgygroupId>
<artifactId>testRedisStarterartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>len.hgygroupId>
<artifactId>redis-starterartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
project>
新建一个springboot启动类
package len.hgy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
新建application.properties在里面配置redis连接相关信息
redis.port=6379
redis.host=127.0.0.1
测试类
package len.hgy.test;
import javax.annotation.Resource;
import len.hgy.App;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import redis.clients.jedis.Jedis;
@SpringBootTest(classes = App.class)
@RunWith(SpringRunner.class)
public class RedisTest {
@Resource
private Jedis jedis;
@Test
public void test() {
jedis.set("hgy", "hgy");
String enjoy = jedis.get("hgy");
System.out.println(enjoy);
}
}
整个bean被拉取到spring容器的过程梳理
Spring Boot CLI是一个命令行工具,如果想使用Spring进行快速开发可以使用它。它允许你运行Groovy脚本,这意味着你可以使用熟悉的类Java语法,并且没有那么多的模板代码。你可以通过Spring Boot CLI启动新项目,或为它编写命令。
Groovy是个基于JVM(Java虚拟机)的敏捷开发语言,既然是基于jvm,那么在groovy里面使用任何java的组件他都是可以支持识别的,在大概5,6年前Groovy比较火,尤其微信公众刚开放的那段时间,很多微信的后端程序都是基于grails开发的。
解压安装SpringBoot CLI
https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.1.2.RELEASE/spring-boot-cli-2.1.2.RELEASE-bin.zip
# 查看版本信息
spring.bat --version
# 使用命令可以启动
spring.bat run .\hello.groovy
# 构建项目
spring init --build=maven --java-version=1.8 --dependencies=web --packaging=jar --boot-version=1.5.3.RELEASE --groupId=enjoy --artifactId=demo
在默认情况下,我们会使用@SpringBootApplication注解来自动获取应用的配置信息,但这样也会带来一些副作用。使用这个注解后,会触发自动配置(auto-configuration)和组件扫描(component scanning),这跟使用@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解的作用是一样的。这样做给开发带来方便的同时,会有以下的一些影响:
会导致项目启动时间变长(原因:加载了我们不需要使用的组件,浪费了cpu资源和内存资源)。当启动一个大的应用程序,或将做大量的集成测试启动应用程序时,影响会特别明显。
会加载一些不需要的多余的实例(beans)。
会增加CPU消耗和内存的占用。
不使用@SpringBootApplication,并且使用带参数的@ComponentScan注解(此注解会自动扫描我们注解了@Controller,@Service的注解的类,加载到Spring IOC容器中),然后我们使用@Configuration和@EnableAutoConfiguration进行配置启动类
//@SpringBootApplication
@EnableAutoConfiguration
@Configuration
@ComponentScan("len.hgy.controller")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
@EnableAutoConfiguration注解会导入META-INF/spring.factories里面配置的很多Configuration,这些Configuration他都会去扫描
在启动VM参数里面加入 -Ddebug
重新启动,发现在控制台
控制台输入的信息,大概分成四大类
我们只需要在启动的时候,显式地引入这些组件需要的组件=Positive matches+Unconditional classes
可以不使用@EnableAutoConfiguration,转而显示的使用@Import来导入需要的配置类
另外也可以删除一些虽然匹配到了,但是在项目中目前并没有使用到的配置,比如
任务调度:TaskExecutionAutoConfiguration,TaskSchedulingAutoConfiguration
WebSocket: WebSocketServletAutoConfiguration
附件上传:MultipartAutoConfiguration
JMX:JmxAutoConfiguration
等等……
启动App,使用jdk里面jvisualvm.exe
这个时候内存分配了2个G,可以根据需要,判断是否需要这么大,一般来说1个G足够,尤其是微服务
另外还发现最大值和最小值两个设置的并不一样,来看下会有什么问题。
设置JVM参数
-XX:+PrintGCDetails -Xmx32M -Xms6M
gc了66次
full gc 了4次
频繁的GC对性能影响是很大的。
频繁调用http://localhost:8080/hi,发现垃圾回收特别频繁
-XX:+PrintGCDetails -Xmx32M -Xms32M
只full gc了一次
gc了50次
频繁调用http://localhost:8080/hi,发现垃圾回收依然特别频繁,这不断的申请内存,释放内存对性能是有不小的影响
置JVM参数,把最大的内存数设置成1024
-XX:+PrintGCDetails -Xmx1024M -Xms1M
-XX:+PrintGCDetails -Xmx1024M -Xms1024M full gc 1次 gc 2次(含full gc)
自己去测试
默认情况下,Spring Boot 使用 Tomcat 来作为内嵌的 Servlet 容器可以将 Web 服务器切换到 Undertow 来提高应用性能。
Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制。Undertow 是红帽公司的开源产品
jmeter配置
重复三次,结果为:1796,1761,1720, 可见tomcat在1秒内处理请求数不到2000
修改pom
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-tomcatartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-undertowartifactId>
dependency>
启动报错:
2022-01-27 23:52:31,422 restartedMain ERROR An exception occurred processing Appender File java.nio.file.InvalidPathException: Illegal char <:> at index 5: ${sys:LOG_PATH}\2022-01
tomcat写的日志格式和undertow格式不兼容,换一个日志文件名称或删除即可
启动成功
重新压测三次
2454,2345 ,2654
性能提升比较明显
SpringBoot默认是集成事务的,只要在方法上加上@Transactional既可, 但某些项目用到了多个数据库,也就代表有多个数据源。
有两个数据库
db1: hgy_user
CREATE TABLE `hgy_user` (
`id` int NOT NULL AUTO_INCREMENT ,
`passwd` varchar(255) NULL ,
`username` varchar(255) NULL ,
PRIMARY KEY (`id`)
);
db2: hgy_order
CREATE TABLE `hgy_order` (
`id` INT ( 10 ) NOT NULL AUTO_INCREMENT,
`name` VARCHAR ( 255 ),
`user_id` INT ( 11 ) NOT NULL,
`account` INT ( 255 ) NULL DEFAULT NULL,
PRIMARY KEY ( `id` )
)
pom文件
<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.0modelVersion>
<groupId>hgygroupId>
<artifactId>springbootatomikosartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.2.RELEASEversion>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jta-atomikosartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.22version>
dependency>
dependencies>
project>
新增model(Orders,Users)
略
新增mapper接口
略
Mapper的XML配置
为了管理方便,xml的配置也根据数据库的不同放置到不同的文件夹中。
application.properties
spring.datasource.spring.driverClassName=com.mysql.jdbc.Driver
spring.datasource.spring.jdbcUrl=jdbc:mysql://127.0.0.1:3306/my_db?serverTimezone=GMT%2B8
spring.datasource.spring.username=root
spring.datasource.spring.password=root
spring.datasource.spring2.driverClassName=com.mysql.jdbc.Driver
spring.datasource.spring2.jdbcUrl=jdbc:mysql://192.168.71.10:3306/len?serverTimezone=GMT%2B8
spring.datasource.spring2.username=root
spring.datasource.spring2.password=root
数据源配置类
db1
@Configuration
@MapperScan(basePackages = "len.hgy.dao.users", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSource1Config {
// @Bean(name = "test1DataSource")
// @ConfigurationProperties(prefix = "spring.datasource.spring")
// @Primary
// public DataSource testDataSource() {
// return DataSourceBuilder.create().build();
// }
@Bean(name = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/users/*.xml"));
return bean.getObject();
}
@Bean(name = "test1DataSource")
@Primary
public DataSource testDataSource(DBConfig1 config1) {
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setUrl(config1.getJdbcUrl());
mysqlXADataSource.setPassword(config1.getPassword());
mysqlXADataSource.setUser(config1.getUsername());
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
atomikosDataSourceBean.setUniqueResourceName("test1Datasource");
return atomikosDataSourceBean;
}
// @Bean(name = "test1TransactionManager")
// @Primary
// public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
@Bean(name = "test1SqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
db2
@Configuration
@MapperScan(basePackages = "len.hgy.dao.orders", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSource2Config {
// @Bean(name = "test2DataSource")
// @ConfigurationProperties(prefix = "spring.datasource.spring2")
// public DataSource testDataSource() {
// return DataSourceBuilder.create().build();
// }
@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/orders/*.xml"));
return bean.getObject();
}
@Bean(name = "test2DataSource")
public DataSource testDataSource(DBConfig2 config2) {
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setUrl(config2.getJdbcUrl());
mysqlXADataSource.setPassword(config2.getPassword());
mysqlXADataSource.setUser(config2.getUsername());
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
atomikosDataSourceBean.setUniqueResourceName("test2Datasource");
return atomikosDataSourceBean;
}
// @Bean(name = "test2TransactionManager")
// public DataSourceTransactionManager testTransactionManager(@Qualifier("test2DataSource") DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
配置了数据源,连接工厂,Mapper扫描的包, mapper xml配置的位置等
@SpringBootTest(classes = {App.class})
@RunWith(SpringRunner.class)
public class UserTest {
@Resource
private IOrderService iOrderService;
@org.junit.Test
public void test1() {
Users users = new Users();
users.setUsername("hgy");
users.setPasswd("123");
users.setId(1);
Orders orders = new Orders();
orders.setAccount(12);
orders.setName("娃娃");
orders.setUserId(1);
iOrderService.addOrder(orders, users);
}
}
这个时候以及集成了2套数据源,并且已经测试发现可以同时入库。
修改pom文件
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jta-atomikosartifactId>
dependency>
新增配置类(DBConfig1,DBConfig2)
@ConfigurationProperties(prefix = "spring.datasource.spring")
@Component
@Data
public class DBConfig1 {
private String driverClassName;
private String jdbcUrl;
private String username;
private String password;
}
@ConfigurationProperties(prefix = "spring.datasource.spring2")
@Component
@Data
public class DBConfig2 {
private String driverClassName;
private String jdbcUrl;
private String username;
private String password;
}
修改数据源配置类
@Configuration
@MapperScan(basePackages = "len.hgy.dao.users", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSource1Config {
// @Bean(name = "test1DataSource")
// @ConfigurationProperties(prefix = "spring.datasource.spring")
// @Primary
// public DataSource testDataSource() {
// return DataSourceBuilder.create().build();
// }
@Bean(name = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/users/*.xml"));
return bean.getObject();
}
@Bean(name = "test1DataSource")
@Primary
public DataSource testDataSource(DBConfig1 config1) {
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setUrl(config1.getJdbcUrl());
mysqlXADataSource.setPassword(config1.getPassword());
mysqlXADataSource.setUser(config1.getUsername());
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
atomikosDataSourceBean.setUniqueResourceName("test1Datasource");
return atomikosDataSourceBean;
}
// @Bean(name = "test1TransactionManager")
// @Primary
// public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
@Bean(name = "test1SqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "len.hgy.dao.orders", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSource2Config {
// @Bean(name = "test2DataSource")
// @ConfigurationProperties(prefix = "spring.datasource.spring2")
// public DataSource testDataSource() {
// return DataSourceBuilder.create().build();
// }
@Bean(name = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/orders/*.xml"));
return bean.getObject();
}
@Bean(name = "test2DataSource")
public DataSource testDataSource(DBConfig2 config2) {
MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
mysqlXADataSource.setUrl(config2.getJdbcUrl());
mysqlXADataSource.setPassword(config2.getPassword());
mysqlXADataSource.setUser(config2.getUsername());
AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
atomikosDataSourceBean.setXaDataSource(mysqlXADataSource);
atomikosDataSourceBean.setUniqueResourceName("test2Datasource");
return atomikosDataSourceBean;
}
// @Bean(name = "test2TransactionManager")
// public DataSourceTransactionManager testTransactionManager(@Qualifier("test2DataSource") DataSource dataSource) {
// return new DataSourceTransactionManager(dataSource);
// }
@Bean(name = "test2SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
修改Service方法
@Override
@Transactional
public void addOrder(Orders orders, Users users) {
usersMapper.insertSelective(users);
int i=10/0; // 使得抛出异常
ordersMapper.insertSelective(orders);
}
单元测试两个数据都不能入库成功