<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.30.156
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=12345678
# 连接超时时间(毫秒)
spring.redis.timeout=5000
/**
* 测试连接redis(连接地址为虚拟机redis服务,需要手动开启)
*/
@SpringBootTest(classes = {App.class})
@RunWith(SpringRunner.class)
public class SpringRedisTest {
@Resource
private RedisTemplate
@Test
public void testRedis() throws Exception {
ValueOperations
ops.set("name", "hankin1117");
String value = ops.get("name");
System.out.println(value);
}
}
RabbitMQ在后面SpringCloud的章节还会用到,在这会简单的安装使用下RabbitMq,至于RabbitMQ的详细使用请参考分布式消息队列课题。
先安装Erlang,下载地址:http://erlang.org/download/otp_win64_20.3.exe
RabbitMQ Server 3.7.4下载地址:
https://bintray.com/rabbitmq/all/download_file?file_path=rabbitmq-server%2F3.7.4%2Frabbitmq-server-3.7.4.exe
安装好后,启动服务:
开启web插件,进入rabbitmq安装目录的sbin目录,在命令行界面开启web插件:
./rabbitmq-plugins enable rabbitmq_management
重新启动reabbitmq服务,在地址栏输入http://localhost:15672/
使用默认用户 guest/guest登陆界面
用新创建的admin用户登陆,发现新用户已经新增成功,设置权限:
点击这用户,设置虚拟主机的权限(全部可读,可写,可配置)
这个时候一个admin新用户设置完毕
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
## rabbitmq config
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
@Configuration
public class RabbitConfig {
@Bean
public Queue firstQueue() {
// 创建一个队列,名称为:hankin
return new Queue("hankin");
}
}
@Component
public class Sender {
@Resource
private AmqpTemplate rabbitTemplate;
public void send() {
rabbitTemplate.convertAndSend("hankin","this is a message!");
}
}
@Component
@RabbitListener(queues = "hankin") //TODO 定义该类需要监听的队列
public class Receiver {
// 指定对消息的处理
@RabbitHandler
public void process(String msg) {
System.out.println("receive msg : " + msg);
}
}
6)新增单元测试
@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还会用到这功能,就可以看到界面了)
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
# 加载所有的端点/默认只加载了 info / health
management.endpoints.web.exposure.include=*
# 描述信息
info.blog-url=https://blog.csdn.net/m0_37661458
info.author=hankin
info.version=@project.version@
重新启动,在地址栏输入: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 |
查看自定义应用信息 |
在学习SpringBoot的过程中,不管是集成redis还是RabbitMQ,甚至是前面集成mybatis已经学习了很多starter,这些starter都是springboot为我们提供的一些封装;这些starter能非常方便快捷的增加功能,并不需要很多配置,即使需要配置也就在application.properties稍微配置下就可以了,那么接下来就学习下怎么创建属于自己的starter。
前面已经使用过spring-boot-starter-data-redis,这个starter是用来集成redis的,那么接下来完成一个starter,这个starter也就集成下redis。新建一个项目,这个项目不需要web功能。
1)pom文件内容如下:
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.0.1version>
dependency>
2)创建一个RedisProperties用于加载Redis需要的配置,这里为简单起见,并没有设置密码
@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;
}
}
3)创建一个配置类,这个配置类用于加载配置,并实例化Jedis客户端
@Configuration //开启配置
@ConditionalOnClass(Jedis.class)
@EnableConfigurationProperties(RedisProperties.class) // TODO 开启使用映射实体对象
@ConditionalOnProperty//TODO 存在对应配置信息时初始化该配置类
(
prefix = "redis",//存在配置前缀redis
value = "enabled",//开启
matchIfMissing = true//缺失检查
)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Jedis jedis(RedisProperties redisProperties){
return new Jedis(redisProperties.getHost(), redisProperties.getPort());
}
}
4)自动化配置代码中有很多我们之前没有用到的注解配置,我们从上开始讲解
@Configuration:这个配置就不用多做解释了,我们一直在使用。
@EnableConfigurationProperties:这是一个开启使用配置参数的注解,value值就是我们配置实体参数映射的ClassType,将配置实体作为配置来源。
有关@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运作原理后才可以完成后续编码。
在注解@SpringBootApplication上存在一个开启自动化配置的注解@EnableAutoConfiguration来完成自动化配置,注解源码如下所示:
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class>[] exclude() default {};
String[] excludeName() default {};
}
在@EnableAutoConfiguration注解内使用到了@import注解来完成导入配置的功能,而EnableAutoConfigurationImportSelector内部则是使用了SpringFactoriesLoader.loadFactoryNames方法进行扫描具有META-INF/spring.factories文件的jar包。我们可以先来看下spring-boot-autoconfigure包内的spring.factories文件内容,如下所示:
可以看到配置的结构形式是Key=>Value形式,多个Value时使用‘,’隔开,那我们在自定义starter内也可以使用这种形式来完成,我们的目的是为了完成自动化配置,所以我们这里Key则是需要使用org.springframework.boot.autoconfigure.EnableAutoConfiguration
我们在src/main/resource目录下创建META-INF目录,并在目录内添加文件spring.factories,具体内容如下所示:
#配置自定义Starter的自动化配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.chj.redis.RedisAutoConfiguration
目前为止自定义的starter已经开发完毕。
这里我们新建项目springboot-redis-startertest做测试,这项目用来测试前面创建的redis-starter。
代码地址:https://gitee.com/hankin_chj/springboot-platform.git
1)Pom文件配置如下:
<dependency>
<groupId>com.chjgroupId>
<artifactId>springboot-redis-starterartifactId>
<version>0.0.1-SNAPSHOTversion>
dependency>
2)application.properties在里面配置redis连接相关信息:
redis.host=127.0.0.1
redis.port=6379
3)准备好这些后,启动redis,新建立一个测试类,运行测试方法:
@SpringBootTest(classes = App.class)
@RunWith(SpringRunner.class)
public class RedisTest {
@Resource
private Jedis jedis;
@Test
public void test() {
jedis.set("hankin","hankintest");
String str = jedis.get("hankin");
System.out.println(str);
}
}
到这一步,自定义redis-stater搞定。
Spring Boot CLI是一个命令行工具,如果想使用Spring进行快速开发可以使用它。它允许你运行Groovy脚本,这意味着你可以使用熟悉的类Java语法,并且没有那么多的模板代码。你可以通过Spring Boot CLI启动新项目,或为它编写命令。
Groovy是个基于JVM(Java虚拟机)的敏捷开发语音,既然是基于jvm,那么在groovy里面使用任何java的组件他都是可以支持识别的,在大概5,6年前Groovy比较火,尤其微信公众刚开放的那段时间,很多微信的后端程序都是基于grails开发的。
下载地址:
https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/2.1.2.RELEASE/spring-boot-cli-2.1.2.RELEASE-bin.zip
为了方便你可以配置环境变量,但由于这个cli使用得并不频繁,就不配置了。
使用命令: .\spring.bat --version可以查看Spring CLI的版本信息。
1)新建一个groovy的文件 hello.groovy
@RestController
class WebApplication {
@RequestMapping("/")
String home() {
"Hello World!"
}
}
2)使用命令可以启动:.\spring.bat run .\hello.groovy
这个时候他会自动下载依赖,并运行项目,直接访问上面的接口,返回结果如下:
刚开始学习springboot的时候经常使用http://start.spring.io/ 构建项目,其实这个功能你完全也可以使用 SpringBoot CLI完成构建。
使用命令:
.\spring init --build=maven --java-version=1.8 --dependencies=web --packaging=jar --boot-version=2.1.3.RELEASE --groupId=chj --artifactId=demo
项目创建成功!
在默认情况下,我们会使用@SpringBootApplication注解来自动获取应用的配置信息,但这样也会带来一些副作用。使用这个注解后,会触发自动配置(auto-configuration)和组件扫描(component scanning),这跟使用@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解的作用是一样的。这样做给开发带来方便的同时,会有以下的一些影响:
本着有问题就要解决的心态,针对以上的问题,我们要怎么解决呢?很明显,既然@SpringBootApplication加载了一些不必要的配置,那么我们想是否可以就加载我们自己指定的配置呢?
我们的思路不使用@SpringBootApplication,并且不使用@ComponentScan注解(此注解会自动扫描我们注解了@Controller,@Service的注解的类,加载到Spring IOC容器中),然后我们使用@Configuration和@EnableAutoConfiguration进行配置启动类。
新建立一个项目,pom文件仅仅引入web依赖,新建一个测试controller,新增启动类;这个代码非常简单,优化的效果也不会很明显,但重点需要理解的是这原理,前面已经说过直接用@SpringBootApplication有各种问题,那么现在改变启动类:
//@SpringBootApplication
@EnableAutoConfiguration
@Configuration
@ComponentScan("com.chj.controller")
public class SpringbootOptimizeApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootOptimizeApplication.class, args);
}
}
这样直接使用自己定义ComponentScan的扫描类,让他只扫描自己需要的组件能增加启动时间,并且介绍spring容器bean的数目。前面讲自定义starter的时候讲过,@EnableAutoConfiguration注解会导入META-INF/spring.factories里面配置的很多Configuration,这些Configuration他都会去扫描,在启动VM参数里(VM options)面加入 -Ddebug:
重新启动,发现在控制台:
控制台输入的信息,大概分成四大类:
1)Positive matches:匹配(以及匹配的原因)
2)Negative matches:忽略匹配(以及忽略的原因)
3)Exclusions:排除的配置类
4)Unconditional classes:没有带任何条件,肯定要扫描的类
根据上面的理论知识,我们只需要在启动的时候,显式地引入这些组件,需要的组件=Positive matches+Unconditional classes,我们可以不使用@EnableAutoConfiguration,转而显示的使用@Import来导入需要的配置类:
//@SpringBootApplication
//@EnableAutoConfiguration
@Configuration
@Import({
CodecsAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
EmbeddedWebServerFactoryCustomizerAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
HttpEncodingAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
JacksonAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class,
WebMvcAutoConfiguration.class,
ValidationAutoConfiguration.class,
MultipartAutoConfiguration.class,
JmxAutoConfiguration.class,
RestTemplateAutoConfiguration.class,
WebSocketServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class,
TaskSchedulingAutoConfiguration.class,
ConfigurationPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
ProjectInfoAutoConfiguration.class
})
@ComponentScan("com.chj.controller")
public class SpringbootOptimizeApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootOptimizeApplication.class, args);
}
}
另外也可以删除一些虽然匹配到了,但是在项目中目前并没有使用到的配置,比如:
任务调度:TaskExecutionAutoConfiguration,TaskSchedulingAutoConfiguration
WebSocket:WebSocketServletAutoConfiguration
附件上传:MultipartAutoConfiguration
JMX:JmxAutoConfiguration等等……
启动App,使用jdk里面jvisualvm.exe可以吃查看应用的内存情况:
这个时候内存分配了2G,可以根据需要,判断是否需要这么大,一般来说1G足够,尤其是微服务,另外还发现最大值和最小值两个设置的并不一样,来看下会有什么问题。
1)设置JVM参数:-XX:+PrintGCDetails -Xmx32M -Xms1M
这样设置后发现有大量的GC,更可怕的还有大量的FULL GC存在。
频繁的GC对性能影响是很大的,频繁调用http://localhost:8080/hi,发现垃圾回收特别频繁:
2)设置JVM参数,把最大的内存数设置成1024:-XX:+PrintGCDetails -Xmx1024M -Xms1M
GC次数明显减少,但既然还会出现几个full GC;频繁调用http://localhost:8080/hi,发现垃圾回收依然特别频繁,这不断的申请内存,释放内存对性能是有不小的影响。
3)置JVM参数,把最大的内存数设置成1024,最小内存也设置成1024
-XX:+PrintGCDetails -Xmx1024M -Xms1024M
这个时候再重启,发现不管是gc还是full gc 都明显的减少了次数。
这个时候再来频繁调用http://localhost:8080/hi,发现内存的使用比较均匀了:
默认情况下,Spring Boot使用Tomcat来作为内嵌的Servlet容器,可以将Web服务器切换到Undertow来提高应用性能。Undertow是一个采用Java开发的灵活的高性能Web服务器,提供包括阻塞和基于NIO的非堵塞机制,Undertow是红帽公司的开源产品。
1)先测试下默认情况tomcat性能,启动Apache JMeter,选择添加--》线程组,弹出页面设置线程数以及循环次数(这里我们设置1个线程请求1000次)。
2)然后右键:添加==》Sampler==》HTTP请求,填写路径:http://localhost:8080/hello
3)添加一个“聚合报告”,右键:添加==》监听器--》聚合报告
重复测试3次,求一个平均值:
结果为:1313、1761、1720,可见tomcat在1秒内处理请求数不到2000。
修改pom文件,先删除掉Tomcat然后引入undertow:
<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>
重新启动后发现容器以及切换成undertow:
继续使用JMeter测试:1494、972、226发现性能提升还是比较大的。
本章节主要学习多数据源与jta+atomikos分布式事务,SpringBoot默认是集成事务的,只要在方法上加上@Transactional既可,但某些项目用到了多个数据库,也就代表有多个数据源。
两个数据库:
数据库1:Databases:spring
数据库2:Databases:spring2
新建一个项目,在pom文件里面增加mybatis等依赖信息
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
可通过mybatis的generatorConfig.xml生产,代码省略。
注意:由于这两mapper接口对应不同的数据库,为了配置与管理方便,不同的数据库的mapper接口单独放置在一个package中。
为了管理方便,xml的配置也根据数据库的不同放置到不同的文件夹中。
例如:mapper文件夹下面分别新建order、user文件夹,用来存放各自的映射文件。
新建service接口与实现类OrderService以及实现类OrderServiceImpl,上面只是准备工作,目前未知没有配置与数据源相关的任何配置。
新增application.properties,里面配置数据源相关信息,分别配置了两个数据库:
spring.datasource.spring.driverClassName=com.mysql.jdbc.Driver
spring.datasource.spring.jdbcUrl=jdbc:mysql://127.0.0.1:3306/spring?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://127.0.0.1:3306/spring2?serverTimezone=GMT%2B8
spring.datasource.spring2.username=root
spring.datasource.spring2.password=root
@Configuration
@MapperScan(basePackages = "com.chj.dao.user", 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 = "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);
}
}
数据源配置类2:
@Configuration
@MapperScan(basePackages = "com.chj.dao.order", 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/order/*.xml"));
return bean.getObject();
}
@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 MoreDataBaseTest {
@Resource
private OrderService orderService;
@org.junit.Test
public void test1() {
User user = new User();
user.setUserName("hankin");
user.setPassword("123");
user.setId(1001);
Order orders = new Order();
orders.setAccount("123");
orders.setName("zhangsan");
orders.setUserId("1001");
orderService.addOrder(orders,user);
}
}
这个时候以及集成了2套数据源,并且已经测试发现可以同时入库。
增加atomikos支持
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jta-atomikosartifactId>
dependency>
DBConfig1代码示例:
@ConfigurationProperties(prefix = "spring.datasource.spring")
@Component
public class DBConfig2 {
private String driverClassName;
private String jdbcUrl;
private String username;
private String password;
Getter/setter...
}
DBConfig2代码示例:
@ConfigurationProperties(prefix = "spring.datasource.spring2")
@Component
public class DBConfig2 {
private String driverClassName;
private String jdbcUrl;
private String username;
private String password;
Getter/setter...
}
数据源配置类1,添加事务修改:
/**
* 数据源配置类1 添加事务修改
*/
@Configuration
@MapperScan(basePackages = "com.chj.dao.user", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSource1Config1 {
@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 = "test1SqlSessionFactory")
@Primary
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/user/*.xml"));
return bean.getObject();
}
// @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);
}
}
数据源配置类2,添加事务修改:
/**
* 数据源配置类2,添加事务修改
*/
@Configuration
@MapperScan(basePackages = "com.chj.dao.order", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSource2Config2 {
@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 = "test2SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/order/*.xml"));
return bean.getObject();
}
// @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);
}
}
2.4、修改Service方法
public class OrderServiceImpl implements OrderService {
@Resource
private UserMapper userMapper;
@Resource
private OrderMapper orderMapper;
@Transactional
@Override
public void addOrder(Order order, User user) {
userMapper.insertSelective(user);
int i=10/0;
orderMapper.insertSelective(order);
}
}
使用之前的单元测试。