第二章 Springboot进阶部分笔记

一、集成Redis

1、修改pom文件,增加依赖


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

2、在properties增加redis配置

# 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

3、新建单元测试

/**
 * 测试连接redis(连接地址为虚拟机redis服务,需要手动开启)
 */
@SpringBootTest(classes = {App.class})
@RunWith(SpringRunner.class)
public class SpringRedisTest {
    @Resource
    private RedisTemplate redisTemplate;
    @Test
    public void testRedis() throws Exception {
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("name", "hankin1117");
        String value = ops.get("name");
        System.out.println(value);
    }
}

二、集成RabbitMQ

RabbitMQ在后面SpringCloud的章节还会用到,在这会简单的安装使用下RabbitMq,至于RabbitMQ的详细使用请参考分布式消息队列课题。

1、Windows安装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登陆界面

2、新增用户,并设置权限

用新创建的admin用户登陆,发现新用户已经新增成功,设置权限:

 

点击这用户,设置虚拟主机的权限(全部可读,可写,可配置)

 

第二章 Springboot进阶部分笔记_第1张图片

 

这个时候一个admin新用户设置完毕

3、SpringBoot中使用RabbitMQ

1)修改pom文件,增加rabbitmq依赖:


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-amqpartifactId>
dependency>

2)修改配置properties文件,增加rabbitmq的连接信息

## rabbitmq config
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin

3)创建Rabbit配置类,用来配置队列,交换器,路由等高级信息

@Configuration
public class RabbitConfig {
    @Bean
    public Queue firstQueue() {
        // 创建一个队列,名称为:hankin
        return new Queue("hankin");
    }
}

4)创建消息的生产者:

@Component
public class Sender {
    @Resource
    private AmqpTemplate rabbitTemplate;
    public void send() {
        rabbitTemplate.convertAndSend("hankin","this is a message!");
    }
}

5)创建消息的消费者

@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();
    }
}

 

第二章 Springboot进阶部分笔记_第2张图片

三、Actuator监控管理

Actuator是spring boot的一个附加功能,可帮助你在应用程序生产环境时监视和管理应用程序;可以使用HTTP的各种请求来监管、审计、收集应用的运行情况,特别对于微服务管理十分有意义。

缺点:没有可视化界面(Spring cloud还会用到这功能,就可以看到界面了)

1、修改pom文件,添加依赖:


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-actuatorartifactId>
dependency>

2、修改application.properties文件,启动监控端点

# 加载所有的端点/默认只加载了 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

查看自定义应用信息

四、自定义Starter

在学习SpringBoot的过程中,不管是集成redis还是RabbitMQ,甚至是前面集成mybatis已经学习了很多starter,这些starter都是springboot为我们提供的一些封装;这些starter能非常方便快捷的增加功能,并不需要很多配置,即使需要配置也就在application.properties稍微配置下就可以了,那么接下来就学习下怎么创建属于自己的starter。

1、redis-starter插件

前面已经使用过spring-boot-starter-data-redis,这个starter是用来集成redis的,那么接下来完成一个starter,这个starter也就集成下redis。新建一个项目,这个项目不需要web功能。

1.1、环境搭建

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,将配置实体作为配置来源。

1.2、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运作原理后才可以完成后续编码。

1.3、Starter自动化运作原理

在注解@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文件内容,如下所示:

 

第二章 Springboot进阶部分笔记_第3张图片

 

可以看到配置的结构形式是Key=>Value形式,多个Value时使用‘,’隔开,那我们在自定义starter内也可以使用这种形式来完成,我们的目的是为了完成自动化配置,所以我们这里Key则是需要使用org.springframework.boot.autoconfigure.EnableAutoConfiguration

1.4、自定义spring.factories

我们在src/main/resource目录下创建META-INF目录,并在目录内添加文件spring.factories,具体内容如下所示:

#配置自定义Starter的自动化配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.chj.redis.RedisAutoConfiguration

目前为止自定义的starter已经开发完毕。

2、新建项目测试startser

这里我们新建项目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搞定。

第二章 Springboot进阶部分笔记_第4张图片

 

五、SpringBoot CLI

Spring Boot CLI是一个命令行工具,如果想使用Spring进行快速开发可以使用它。它允许你运行Groovy脚本,这意味着你可以使用熟悉的类Java语法,并且没有那么多的模板代码。你可以通过Spring Boot CLI启动新项目,或为它编写命令。

Groovy是个基于JVM(Java虚拟机)的敏捷开发语音,既然是基于jvm,那么在groovy里面使用任何java的组件他都是可以支持识别的,在大概5,6年前Groovy比较火,尤其微信公众刚开放的那段时间,很多微信的后端程序都是基于grails开发的。

1、解压安装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

为了方便你可以配置环境变量,但由于这个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进阶部分笔记_第5张图片

这个时候他会自动下载依赖,并运行项目,直接访问上面的接口,返回结果如下:

 

第二章 Springboot进阶部分笔记_第6张图片

2、构建项目

刚开始学习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

项目创建成功!

六、性能优化

1、扫描优化

在默认情况下,我们会使用@SpringBootApplication注解来自动获取应用的配置信息,但这样也会带来一些副作用。使用这个注解后,会触发自动配置(auto-configuration)和组件扫描(component scanning),这跟使用@Configuration、@EnableAutoConfiguration和@ComponentScan三个注解的作用是一样的。这样做给开发带来方便的同时,会有以下的一些影响:

  • 会导致项目启动时间变长(原因:加载了我们不需要使用的组件,浪费了cpu资源和内存资源)。当启动一个大的应用程序,或将做大量的集成测试启动应用程序时,影响会特别明显。
  • 会加载一些不需要的多余的实例(beans)。
  • 会增加CPU消耗和内存的占用。

本着有问题就要解决的心态,针对以上的问题,我们要怎么解决呢?很明显,既然@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:

 

重新启动,发现在控制台:

 

第二章 Springboot进阶部分笔记_第7张图片

控制台输入的信息,大概分成四大类:

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等等……

2、JVM参数调优

 启动App,使用jdk里面jvisualvm.exe可以吃查看应用的内存情况:

  第二章 Springboot进阶部分笔记_第8张图片 

这个时候内存分配了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,发现内存的使用比较均匀了:

 

3、Undertow容器

默认情况下,Spring Boot使用Tomcat来作为内嵌的Servlet容器,可以将Web服务器切换到Undertow来提高应用性能。Undertow是一个采用Java开发的灵活的高性能Web服务器,提供包括阻塞和基于NIO的非堵塞机制,Undertow是红帽公司的开源产品。

3.1、Tomcat测试

1)先测试下默认情况tomcat性能,启动Apache JMeter,选择添加--》线程组,弹出页面设置线程数以及循环次数(这里我们设置1个线程请求1000次)。

2)然后右键:添加==》Sampler==》HTTP请求,填写路径:http://localhost:8080/hello

3)添加一个“聚合报告”,右键:添加==》监听器--》聚合报告

重复测试3次,求一个平均值:

 

第二章 Springboot进阶部分笔记_第9张图片

结果为:1313、1761、1720,可见tomcat在1秒内处理请求数不到2000。

3.2、Undertow测试

修改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:

第二章 Springboot进阶部分笔记_第10张图片

继续使用JMeter测试:1494、972、226发现性能提升还是比较大的。

七、多数据源与jta+atomikos

本章节主要学习多数据源与jta+atomikos分布式事务,SpringBoot默认是集成事务的,只要在方法上加上@Transactional既可,但某些项目用到了多个数据库,也就代表有多个数据源。

1、多数据源

1.1、数据库准备

两个数据库:

数据库1:Databases:spring  

数据库2:Databases:spring2

1.2、pom文件

新建一个项目,在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>

1.3、新增model类、新增mapper接口

可通过mybatis的generatorConfig.xml生产,代码省略。

注意:由于这两mapper接口对应不同的数据库,为了配置与管理方便,不同的数据库的mapper接口单独放置在一个package中。

1.4、Mapper的XML配置

为了管理方便,xml的配置也根据数据库的不同放置到不同的文件夹中。

例如:mapper文件夹下面分别新建order、user文件夹,用来存放各自的映射文件。

1.5、新建业务测试类

新建service接口与实现类OrderService以及实现类OrderServiceImpl,上面只是准备工作,目前未知没有配置与数据源相关的任何配置。

1.6、application.properties

新增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

2、数据源配置类

@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配置的位置等

3、单元测试

@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套数据源,并且已经测试发现可以同时入库。

2、jta+atomikos分布式事务

2.1、修改pom文件

增加atomikos支持


<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-jta-atomikosartifactId>
dependency>

2.2、新增配置类DBConfig1与DBConfig2

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...
}

2.3、修改数据源配置类

数据源配置类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);
    }
}

使用之前的单元测试。

 

 

你可能感兴趣的:(springboot入门,Springboot进阶部分)