一、SpringBoot相关知识理解

  1. SpringBoot简介
    在您第1次接触和学习Spring框架的时候,是否因为其繁杂的配置而退却了?在你第n次使用Spring框架的时候,是否觉得一堆反复黏贴的配置有一些厌烦?那么您就不妨来试试使用Spring Boot来让你更易上手,更简单快捷地构建Spring应用!
    Spring Boot让我们的Spring应用变的更轻量化。比如:你可以仅仅依靠一个Java类来运行一个Spring应用。你也可以打包你的应用为jar并通过使用java -jar来运行你的Spring Web应用。
    SpringBoot并不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。
    :SpringBoot是一个独立的应用程序,单独war工程,不需要集成tomcat插件运行,因为SpringBoot自身已经内嵌了tomcat。创建SpringBoot项目时可以jar也可以是war,但是不建议建成war工程(如果要使用jsp可以建成war,但是SpringBoot不建议使用jsp),因为SpringBoot是一个独立的应用程序,所以建议选择jar创建SpringBoot项目。
    Spring Boot的主要优点:
  1. 为所有Spring开发者更快的入门(简化Maven配置、自动配置Spring)。
  2. 开箱即用,提供各种默认配置来简化项目配置。同时也可以修改默认值来满足特定的需求。
  3. 内嵌式容器简化Web项目(嵌入的Tomcat,无需部署war文件)。
  4. 没有冗余代码生成和XML配置的要求。
  5. 提供了一些大型项目中常见的非功能特性:生产就绪功能,如:指标,健康检查和外部配置等。
  1. 使用SpringBoot环境要求:
    Java 7及以上,Spring Framework 4.1.5及以上。

二、SpringBoot快速入门及环境搭建(jdk1.8+springboot1.3.2)

  1. 创建一个maven管理的jar工程,在此不做示例。
  2. 在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.0</modelVersion>
  <groupId>chuancy</groupId>
  <artifactId>SpringBoot</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <!-- 必须要引入继承springboot-parent,因为该父工程帮助实现了很多jar包的依赖管理,不要写jar包版本,这样可以控制jar包版本冲突的问题 。-->
  <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.3.3.RELEASE</version>
  </parent>
  <dependencies>
      <!-- 不需要引入SpringMVC,SpringBoot默认集成,只需要引入SpringBoot的web组件:springboot-web即可。 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
  </dependencies>
      
</project>
  1. spring-boot-starter-parent作用:
    在pom.xml中引入spring-boot-start-parent,可以提供dependency management,也就是说依赖管理,引入以后再声明其它dependency的时候就不需要version了。
  2. spring-boot-starter-web作用:
    springweb 核心组件,包含springmvc等。
  1. 创建测试类测试框架是否搭建成功:
package chauncy.controller;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**   
 * @classDesc: 功能描述(第一个springboot)
 * 注解:@RestController是SpringBoot提供的http restful风格,等同于 :类上@Controller+每个方法@ResponseBody,表示该类所有请求方法返回json格式。
 * @author: ChauncyWang
 * @version: 1.0  
 */  
@RestController
@RequestMapping("/TestController")
@EnableAutoConfiguration
public class TestController {
	
	@RequestMapping("/hello")
	public String hello(){
		return "success";
	}
	
	public static void main(String[] args) {
		//运行SpringBoot应用,不要写多个main函数,因为SpringBoot是一个独立的应用程序。
		SpringApplication.run(TestController.class, args);
	}
}
  1. 相关知识汇总:
  1. @RestController解释:
    在类上加上RestController 表示修饰该Controller类所有的方法返回JSON格式,直接可以编写Restful接口。
  2. @EnableAutoConfiguration解释:
    该注解作用在于:让Spring Boot根据应用所声明的依赖来对Spring框架进行自动配置。
    这个注解告诉Spring Boot根据添加的jar依赖猜测开发设计者想如何配置Spring。由于spring-boot-starter-web添加了Tomcat和Spring MVC,所以auto-configuration将假定开发设计者正在开发一个web应用并相应地对Spring进行设置。
  3. SpringApplication.run(TestController.class, args);解释
    标识TestController为启动类。
  4. Spring启动方式:
  1. 启动方式1:
    以上环境搭建第3步。
    Springboot默认端口号为8080,启动成功后,打开浏览器访问http://127.0.0.1:8080/TestController/hello,可以看到页面输出:success。
    但是因为SpringBoot默认端口为8080,再写一个Controller同样也需要启动的话,如果写两个main方法同时启动会报端口号被占用的异常,所以不建议写在某一个业务Controller中进行启动,建议写通用类进行启动配置的操作,例以下启动方式2.
  2. 启动方式2:
    @ComponentScan(basePackages = “chauncy.controller”)–控制器扫包范围
@ComponentScan(basePackages = "chauncy.controller")
@EnableAutoConfiguration
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

三、使用SpringBoot进行Web开发

  1. 静态资源访问:
    在进行Web应用开发的时候,需要引用大量的js、css、图片等静态资源。
    默认配置:
    Spring Boot默认提供静态资源目录位置需置于classpath下,目录名需符合如下规则:
    /static
    /public
    /resources
    /META-INF/resources
    举例:我们可以在src/main/resources/目录下创建static,在该位置放置一个图片文件。启动程序后,尝试访问http://localhost:8080/test.jpg(访问不需要加static)。如能显示图片,配置成功。
  2. 全局捕获异常:
    @ExceptionHandler 表示拦截异常。
    @ControllerAdvice 是controller的一个辅助类,最常用的就是作为全局异常处理的切面类。
    @ControllerAdvice 可以指定扫描范围。
    @ControllerAdvice 约定了几种可行的返回值:
  1. 如果是直接返回 model 类的话,需要使用 @ResponseBody 进行 json 转换。
  2. 返回 String,表示跳到某个 view。
  3. 返回 modelAndView。

代码示例,当应用有运行时异常进行捕获处理:

package chauncy.controller.catchexception;

import java.util.HashMap;
import java.util.Map;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @classDesc: 功能描述:(全局捕获异常)
 * @author: ChauncyWang
 * @verssion: v1.0
 */
@ControllerAdvice
public class GlobalExceptionHandler {
	
	@ExceptionHandler(RuntimeException.class)
	@ResponseBody//拦截异常返回json格式的结果
	public Map<String,Object> execptionHandler(){
		Map<String, Object> result = new HashMap<String, Object>();
		result.put("code", "500");
		result.put("msg", "系统错误,请稍后重试。");
		return result;
	}
}
  1. 渲染Web页面
    在以上的代码中,都是通过@RestController来处理请求,所以返回的内容为json对象。那么如果需要渲染html页面的时候,要如何实现呢?
    在动态HTML实现上,Spring Boot依然可以完美胜任,并且提供了多种模板引擎的默认配置支持,所以在推荐的模板引擎下,可以很快的上手开发动态网站。
    Spring Boot提供了默认配置的模板引擎主要有以下几种:
  1. Thymeleaf
  2. FreeMarker
  3. Velocity
  4. Groovy
  5. Mustache

Spring Boot建议使用以上这些模板引擎,避免使用JSP,若一定要使用JSP将无法实现Spring Boot的多种特性,具体可见后文:支持JSP的配置。
当使用上述模板引擎中的任何一个,它们默认的模板配置路径为:src/main/resources/templates。当然也可以修改这个路径,具体如何修改,可在后续各模板引擎的配置属性中查询并修改。
PS:模板引擎主要作用是动态页面静态化,为什么要动态页面静态化,因为好让搜索引擎搜索到,就是SEO(Search Engine Optimization搜索引擎优化),最终转化成html(伪静态),可以提高互联网站在搜索引擎中的排名,获取流量收益等。

  1. 使用Freemarker模板引擎渲染web视图
  1. 在pom.xml文件中引入freemarker依赖(注意一定要使用springboot相关依赖,不要从maven库中找):
<!-- 引入freeMarker的依赖包. -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  1. 在默认路径src/main/resources/templates下,创建一个后缀名为ftl的文件,内容如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8" />
<title></title>
</head>
<body>
    ${name}<br/>
    <#if sex==1>
                男
    <#elseif sex==2>
                女
    <#else>
                其他      
    </#if>
    <br/>
    <#list userList as user>
        ${user}
    </#list>
   
</body> 
</html>
  1. 后台编写请求访问创建的ftl文件:
package chauncy.controller.web;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/IndexController")
public class IndexController {
	
	
	@RequestMapping("/index")
	public String index(ModelMap map){
		//ModelMap类似于HttpServletRequest的request.setAttribute,只不过ModelMap是SpringMVC封装好的。
		map.put("name", "ChauncyWang");
		map.put("sex", 1);
		List<String> userlist=new ArrayList<String>();
		userlist.add("chauncy1");
		userlist.add("chauncy2");
		userlist.add("chauncy3");
		map.put("userList", userlist);
		return "index";
	}
}
  1. Freemarker的一些配置:
    在src/main/resources下新建application.properties文件,添加如下配置(具体属性含义及修改参考互联网资料):
########################################################
###FREEMARKER (FreeMarkerAutoConfiguration)
########################################################
spring.freemarker.allow-request-override=false
spring.freemarker.cache=true
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
#spring.freemarker.prefix=
#spring.freemarker.request-context-attribute=
#spring.freemarker.settings.*=
spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=classpath:/templates/
#comma-separated list
#spring.freemarker.view-names= # whitelist of view names that can be resolved
  1. 使用JSP渲染Web视图
    SpringBoot要求使用jsp的话项目必须构建为war工程。
  1. 创建一个maven管理的war工程,在此不做示例。
  2. 在pom.xml文件中引入相关依赖:
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.3.3.RELEASE</version>
</parent>
<dependencies>
	<!-- SpringBoot 核心组件 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-tomcat</artifactId>
	</dependency>
	<dependency>
		<groupId>org.apache.tomcat.embed</groupId>
		<artifactId>tomcat-embed-jasper</artifactId>
	</dependency>
</dependencies>
  1. 在src/main/resources下新建application.properties文件,添加如下配置:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
  1. 在src/main/webapp下创建文件夹WEB-INF/jsp,创建文件index.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title></title>
</head>
<body>
    this is springboot!
</body>
</html>
  1. 后台编写请求访问index.jsp文件(简化启动,运行main即可):
package chauncy.controller;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/IndexController")
@EnableAutoConfiguration
public class IndexController {
	
	@RequestMapping("/index")
	public String index(){
		return "index";
	}
	
	public static void main(String[] args) {
		SpringApplication.run(IndexController.class, args);
	}
}

四、数据访问

  1. springboot整合使用JdbcTemplate:
  1. 创建一个maven管理的jar工程,在此不做示例。
  2. 在pom.xml文件中引入相关依赖:
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-jdbc</artifactId>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.15</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>
  1. 在src/main/resources下新建application.properties文件,添加如下配置:
spring.datasource.url=jdbc:mysql://localhost:3306/architect?serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  1. 编写后台请求实现使用JdbcTemplate对数据库表增加记录的操作:
package chauncy.service;

public interface UserService {
	
	public void createJdbcUser();
}
package chauncy.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import chauncy.service.UserService;

@Service
public class UserServiceImpl implements UserService {

	@Autowired
	private JdbcTemplate jdbcTemplate;

	@Override
	public void createJdbcUser() {
		jdbcTemplate.update("insert into users values(null,?,?);", "chauncy", 18);

	}

}
package chauncy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import chauncy.service.UserService;

@RestController
@RequestMapping("/IndexController")
public class IndexController {
	
	@Autowired
	UserService userService;
	
	@RequestMapping("/index")
	public String index(){
		userService.createJdbcUser();
		return "add success";
	}
}
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "chauncy.*")
@EnableAutoConfiguration
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 使用浏览器访问请求,若正常造成请求,数据库users表应该增加一条记录。

注意:使用JdbcTemplate需要spring-boot-starter-parent在1.5以上。

  1. springboot整合使用mybatis:
  1. 创建一个maven管理的jar工程,在此不做示例。
  2. 在pom.xml文件中引入相关依赖:
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.3.2.RELEASE</version>
	<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>1.1.1</version>
	</dependency>
	<dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>8.0.15</version>
       </dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>
  1. 在src/main/resources下新建application.properties文件,添加如下配置:
spring.datasource.url=jdbc:mysql://localhost:3306/architect?serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  1. 编写后台请求实现使用Mybatis对数据库表条件查询的操作:
package chauncy.entity;

public class UserEntity {

	private Integer id;

	private String name;

	private Integer age;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

}
package chauncy.mapper;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import chauncy.entity.UserEntity;


public interface UserMapper {
	
	@Select("select * from users where name=#{name}")
	UserEntity findUser(@Param("name")String name);
}
package chauncy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import chauncy.entity.UserEntity;
import chauncy.mapper.UserMapper;

@Controller
@RequestMapping("/IndexController")
public class IndexController {

	@Autowired
	private UserMapper userMapper;

	@ResponseBody
	@RequestMapping("/getUserName")
	public UserEntity getUserName(String name) {
		return userMapper.findUser(name);

	}
}
package chauncy;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "chauncy.*")
@MapperScan(basePackages="chauncy.*")
@EnableAutoConfiguration
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 使用浏览器造成get请求进行访问,若条件匹配应有json数据返回。
  1. springboot整合使用springjpa:
    什么是SpringJPA?SpringJPA其实就是对Hibernate的封装。
  1. 创建一个maven管理的jar工程,在此不做示例。
  2. 在pom.xml文件中引入相关依赖:
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.4.2.RELEASE</version>
</parent>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>8.0.15</version>
       </dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>
  1. 在src/main/resources下新建application.properties文件,添加如下配置:
spring.datasource.url=jdbc:mysql://localhost:3306/architect?serverTimezone=GMT
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  1. 编写后台请求实现使用SpringJPA对数据库表条件查询的操作:
package chauncy.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

//注解都是使用javax反射包下的
@Entity(name = "users")
public class UserEntity {
	
	@Id
	@GeneratedValue
	private Integer id;

	@Column
	private String name;

	@Column
	private Integer age;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "UserEntity [id=" + id + ", name=" + name + ", age=" + age + "]";
	}
	
	
}
package chauncy.dao;

import org.springframework.data.jpa.repository.JpaRepository;

import chauncy.entity.UserEntity;

//规范:使用jpa的dao层必须要继承JpaRepository<实体类, 序列化主键>接口
public interface UserDao extends JpaRepository<UserEntity, Integer> {

}
package chauncy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import chauncy.dao.UserDao;
import chauncy.entity.UserEntity;

@RestController
@RequestMapping("/IndexController")
public class IndexController {

	@Autowired
	private UserDao userDao;

	@RequestMapping("/index")
	public String index(Integer id) {
		UserEntity userEntity = userDao.findOne(id);
		return userEntity.toString();
	}
}
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableAutoConfiguration
@ComponentScan(basePackages = "chauncy.*")
@EnableJpaRepositories(basePackages = "chauncy.*")
@EntityScan("chauncy.*")
public class APP {
	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 使用浏览器造成get请求进行访问,若条件匹配应有toString数据返回。
  1. springboot整合多数据源:
    有的时候一个项目需要操作多个JDBC,此时就需要整合多数据源。
    例:大型互联网公司分为base数据库(字典、相同配置信息)、主数据库,需要操作多数据源。
    操作多数据源的方式:1.使用注解,但是较繁琐,需要写很多,不推荐。2.分包进行配置,一个包对一个数据源进行操作。
    PS:Spring默认支持多数据源事务回滚。
  1. 创建一个maven管理的jar工程,在此不做示例。
  2. 在pom.xml文件中引入相关依赖:
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.3.2.RELEASE</version>
	<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>1.1.1</version>
	</dependency>
	<dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>8.0.15</version>
       </dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>
  1. 在src/main/resources下新建application.properties文件,添加如下配置:
spring.datasource.test1.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.test1.url = jdbc:mysql://localhost:3306/test01?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8
spring.datasource.test1.username = root
spring.datasource.test1.password = root

spring.datasource.test2.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.test2.url = jdbc:mysql://localhost:3306/test02?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8
spring.datasource.test2.username = root
spring.datasource.test2.password = root
  1. 使用java代码增加两个数据源的配置:
package chauncy.datasource;


import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

/**   
 * @classDesc: 功能描述(数据源1的配置)  
 * @author: ChauncyWang
 * @version: 1.0  
 */ 
@Configuration // 注册到spring容器中
@MapperScan(basePackages="chauncy.test01",sqlSessionFactoryRef="test1SqlSessionFactory")
public class Datasource1 {
	
	/**   
	 * @methodDesc: 功能描述(配置test1数据库)  
	 * @author: ChauncyWang
	 * @param: @return   
	 * @returnType: DataSource  
	 */  
	@Bean(name = "test1DataSource")
	@Primary
	@ConfigurationProperties(prefix = "spring.datasource.test1")
	public DataSource testDataSource() {
		return DataSourceBuilder.create().build();
	}

	
	/**   
	 * @methodDesc: 功能描述(test1 sql会话工厂)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return
	 * @param: @throws Exception   
	 * @returnType: SqlSessionFactory  
	 */  
	@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:mybatis/mapper/test1/*.xml"));
		return bean.getObject();
	}


	/**   
	 * @methodDesc: 功能描述(test1 事物管理)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return   
	 * @returnType: DataSourceTransactionManager  
	 */  
	@Bean(name = "test1TransactionManager")
	@Primary
	public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}

	@Bean(name = "test1SqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}
package chauncy.datasource;


import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

/**   
 * @classDesc: 功能描述(数据源2的配置)  
 * @author: ChauncyWang
 * @version: 1.0  
 */ 
@Configuration // 注册到spring容器中
@MapperScan(basePackages="chauncy.test02",sqlSessionFactoryRef="test2SqlSessionFactory")
public class Datasource2 {
	
	/**   
	 * @methodDesc: 功能描述(配置test2数据库)  
	 * @author: ChauncyWang
	 * @param: @return   
	 * @returnType: DataSource  
	 */  
	@Bean(name = "test2DataSource")
	@ConfigurationProperties(prefix = "spring.datasource.test2")
	public DataSource testDataSource() {
		return DataSourceBuilder.create().build();
	}

	
	/**   
	 * @methodDesc: 功能描述(test2 sql会话工厂)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return
	 * @param: @throws Exception   
	 * @returnType: SqlSessionFactory  
	 */  
	@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:mybatis/mapper/test2/*.xml"));
		return bean.getObject();
	}


	/**   
	 * @methodDesc: 功能描述(test2 事物管理)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return   
	 * @returnType: DataSourceTransactionManager  
	 */  
	@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);
	}

}
  1. 编写分包请求,对两个数据源进行操作:
package chauncy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import chauncy.entity.UserEntity;
import chauncy.mapper.UserMapper;
import chauncy.service.UserService;
import chauncy.test01.service.UserService1;
import chauncy.test02.service.UserService2;

@RestController
@RequestMapping("/IndexController")
public class IndexController {

	@Autowired
	private UserService1 userService1;

	@Autowired
	private UserService2 userService2;
	
	@RequestMapping("/add")
	public String add(String name, Integer age) {
		userService1.addUser(name, age);
		userService2.addUser(name, age);
		return "success";
	}

}
package chauncy.test01.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import chauncy.test01.mapper.UserMapper1;

@Service
public class UserService1 {
	
	@Autowired
	private UserMapper1 userMapper1;
	
	
	public int addUser(String name,Integer age){
		return userMapper1.addUser(name, age);
	}
}
package chauncy.test01.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

public interface UserMapper1 {
	
	@Insert("insert into users values(null,#{name},#{age})")
	public int addUser(@Param("name")String name,@Param("age")Integer age);
}
package chauncy.test02.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import chauncy.test02.mapper.UserMapper2;

@Service
public class UserService2 {
	
	@Autowired
	private UserMapper2 userMapper2;
	
	
	public int addUser(String name,Integer age){
		return userMapper2.addUser(name, age);
	}
}
package chauncy.test02.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

public interface UserMapper2 {
	
	@Insert("insert into users values(null,#{name},#{age})")
	public int addUser(@Param("name")String name,@Param("age")Integer age);
}
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;

@ComponentScan(basePackages = "chauncy.*")
@EnableAutoConfiguration
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 使用浏览器造成get请求进行访问,返回success后比对test01、test02数据库是否都有数据新增,若是则证明springboot整合多数据源成功。

五、事务管理

  1. springboot整合事物管理:
    springboot默认集成事物,只主要在方法上加上@Transactional即可。
  2. 分布式事务(操作多个数据源)解决方案:
    jta+atomikos(不仅限于springboot)、两段提交协议、MQ推送。
  3. SpringBoot分布式事物管理:
    使用springboot+jta+atomikos分布式事物管理。把所有的数据源都交给atomikos进行事务管理。
  4. 操作多个数据源进行事务管理时,问题重现:
package chauncy.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import chauncy.entity.UserEntity;
import chauncy.mapper.UserMapper;
import chauncy.service.UserService;
import chauncy.test01.service.UserService1;
import chauncy.test02.service.UserService2;

@RestController
@RequestMapping("/IndexController")
public class IndexController {

	@Autowired
	private UserService1 userService1;

	@RequestMapping("/addUser1AndUser2")
	public String addUser1AndUser2(){
		userService1.addUser1AndUser2();
		return "success";
	}

}
package chauncy.test01.service;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import chauncy.test01.mapper.UserMapper1;
import chauncy.test02.mapper.UserMapper2;

@Service
public class UserService1 {
	
	@Autowired
	private UserMapper1 userMapper1;
	@Autowired
	private UserMapper2 userMapper2;
	
	@Transactional
	public int addUser1AndUser2(){
		userMapper1.addUser("test01", 18);
		System.out.println("数据库01插入完毕。。");
		userMapper2.addUser("test02", 19);
		int i=1/0;
		return 0;
	}
}
package chauncy.test01.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

public interface UserMapper1 {
	
	@Insert("insert into users values(null,#{name},#{age})")
	public int addUser(@Param("name")String name,@Param("age")Integer age);
}
package chauncy.test02.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;

public interface UserMapper2 {
	
	@Insert("insert into users values(null,#{name},#{age})")
	public int addUser(@Param("name")String name,@Param("age")Integer age);
}
package chauncy.datasource;


import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

/**   
 * @classDesc: 功能描述(数据源1的配置)  
 * @author: ChauncyWang 
 * @version: 1.0  
 */ 
@Configuration // 注册到spring容器中
@MapperScan(basePackages="chauncy.test01",sqlSessionFactoryRef="test1SqlSessionFactory")
public class Datasource1 {
	
	/**   
	 * @methodDesc: 功能描述(配置test1数据库)  
	 * @author: ChauncyWang
	 * @param: @return 
	 * @returnType: DataSource  
	 */  
	@Bean(name = "test1DataSource")
	@Primary
	@ConfigurationProperties(prefix = "spring.datasource.test1")
	public DataSource testDataSource() {
		return DataSourceBuilder.create().build();
	}

	
	/**   
	 * @methodDesc: 功能描述(test1 sql会话工厂)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return
	 * @param: @throws Exception  
	 * @returnType: SqlSessionFactory  
	 */  
	@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:mybatis/mapper/test1/*.xml"));
		return bean.getObject();
	}


	/**   
	 * @methodDesc: 功能描述(test1 事物管理)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return  
	 * @returnType: DataSourceTransactionManager  
	 */  
	@Bean(name = "test1TransactionManager")
	@Primary
	public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}

	@Bean(name = "test1SqlSessionTemplate")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}

}
package chauncy.datasource;


import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

/**   
 * @classDesc: 功能描述(数据源2的配置)  
 * @author: ChauncyWang
 * @version: 1.0  
 */ 
@Configuration // 注册到spring容器中
@MapperScan(basePackages="chauncy.test02",sqlSessionFactoryRef="test2SqlSessionFactory")
public class Datasource2 {
	
	/**   
	 * @methodDesc: 功能描述(配置test2数据库)  
	 * @author: ChauncyWang
	 * @param: @return     
	 * @returnType: DataSource  
	 */  
	@Bean(name = "test2DataSource")
	@ConfigurationProperties(prefix = "spring.datasource.test2")
	public DataSource testDataSource() {
		return DataSourceBuilder.create().build();
	}

	
	/**   
	 * @methodDesc: 功能描述(test2 sql会话工厂)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return
	 * @param: @throws Exception    
	 * @returnType: SqlSessionFactory  
	 */  
	@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:mybatis/mapper/test2/*.xml"));
		return bean.getObject();
	}


	/**   
	 * @methodDesc: 功能描述(test2 事物管理)  
	 * @author: ChauncyWang
	 * @param: @param dataSource
	 * @param: @return     
	 * @returnType: DataSourceTransactionManager  
	 */  
	@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);
	}

}
  1. 问题:上述代码会出现,造成访问请求报错后,一个方法中两个不同数据源的事务无法统一进行回滚的问题,即test01数据库的users表没有新增记录,test02数据库的users表新增了记录,正确结果是都不应该增加记录。
  2. 使用springboot+jta+atomikos分布式事物管理,解决以上问题:
  1. 在pom文件中新增引入jta依赖:
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
  1. 在application.properties中创建两个数据源:
# Mysql 1
mysql.datasource.test1.url = jdbc:mysql://localhost:3306/test01?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8
mysql.datasource.test1.username = root
mysql.datasource.test1.password = root

mysql.datasource.test1.minPoolSize = 3
mysql.datasource.test1.maxPoolSize = 25
mysql.datasource.test1.maxLifetime = 20000
mysql.datasource.test1.borrowConnectionTimeout = 30
mysql.datasource.test1.loginTimeout = 30
mysql.datasource.test1.maintenanceInterval = 60
mysql.datasource.test1.maxIdleTime = 60
mysql.datasource.test1.testQuery = select 1


# Mysql 2
mysql.datasource.test2.url =jdbc:mysql://localhost:3306/test02?serverTimezone=GMT&useUnicode=true&characterEncoding=utf-8
mysql.datasource.test2.username =root
mysql.datasource.test2.password =root

mysql.datasource.test2.minPoolSize = 3
mysql.datasource.test2.maxPoolSize = 25
mysql.datasource.test2.maxLifetime = 20000
mysql.datasource.test2.borrowConnectionTimeout = 30
mysql.datasource.test2.loginTimeout = 30
mysql.datasource.test2.maintenanceInterval = 60
mysql.datasource.test2.maxIdleTime = 60
mysql.datasource.test2.testQuery = select 1

server.port = 80
server.context.path=/
  1. 读取application.properties配置文件的信息:
package chauncy.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mysql.datasource.test1")
public class DBConfig1 {

	private String url;
	private String username;
	private String password;
	private int minPoolSize;
	private int maxPoolSize;
	private int maxLifetime;
	private int borrowConnectionTimeout;
	private int loginTimeout;
	private int maintenanceInterval;
	private int maxIdleTime;
	private String testQuery;

	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 int getMinPoolSize() {
		return minPoolSize;
	}

	public void setMinPoolSize(int minPoolSize) {
		this.minPoolSize = minPoolSize;
	}

	public int getMaxPoolSize() {
		return maxPoolSize;
	}

	public void setMaxPoolSize(int maxPoolSize) {
		this.maxPoolSize = maxPoolSize;
	}

	public int getMaxLifetime() {
		return maxLifetime;
	}

	public void setMaxLifetime(int maxLifetime) {
		this.maxLifetime = maxLifetime;
	}

	public int getBorrowConnectionTimeout() {
		return borrowConnectionTimeout;
	}

	public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
		this.borrowConnectionTimeout = borrowConnectionTimeout;
	}

	public int getLoginTimeout() {
		return loginTimeout;
	}

	public void setLoginTimeout(int loginTimeout) {
		this.loginTimeout = loginTimeout;
	}

	public int getMaintenanceInterval() {
		return maintenanceInterval;
	}

	public void setMaintenanceInterval(int maintenanceInterval) {
		this.maintenanceInterval = maintenanceInterval;
	}

	public int getMaxIdleTime() {
		return maxIdleTime;
	}

	public void setMaxIdleTime(int maxIdleTime) {
		this.maxIdleTime = maxIdleTime;
	}

	public String getTestQuery() {
		return testQuery;
	}

	public void setTestQuery(String testQuery) {
		this.testQuery = testQuery;
	}

}
package chauncy.config;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mysql.datasource.test2")
public class DBConfig2 {

	private String url;
	private String username;
	private String password;
	private int minPoolSize;
	private int maxPoolSize;
	private int maxLifetime;
	private int borrowConnectionTimeout;
	private int loginTimeout;
	private int maintenanceInterval;
	private int maxIdleTime;
	private String testQuery;

	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 int getMinPoolSize() {
		return minPoolSize;
	}

	public void setMinPoolSize(int minPoolSize) {
		this.minPoolSize = minPoolSize;
	}

	public int getMaxPoolSize() {
		return maxPoolSize;
	}

	public void setMaxPoolSize(int maxPoolSize) {
		this.maxPoolSize = maxPoolSize;
	}

	public int getMaxLifetime() {
		return maxLifetime;
	}

	public void setMaxLifetime(int maxLifetime) {
		this.maxLifetime = maxLifetime;
	}

	public int getBorrowConnectionTimeout() {
		return borrowConnectionTimeout;
	}

	public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
		this.borrowConnectionTimeout = borrowConnectionTimeout;
	}

	public int getLoginTimeout() {
		return loginTimeout;
	}

	public void setLoginTimeout(int loginTimeout) {
		this.loginTimeout = loginTimeout;
	}

	public int getMaintenanceInterval() {
		return maintenanceInterval;
	}

	public void setMaintenanceInterval(int maintenanceInterval) {
		this.maintenanceInterval = maintenanceInterval;
	}

	public int getMaxIdleTime() {
		return maxIdleTime;
	}

	public void setMaxIdleTime(int maxIdleTime) {
		this.maxIdleTime = maxIdleTime;
	}

	public String getTestQuery() {
		return testQuery;
	}

	public void setTestQuery(String testQuery) {
		this.testQuery = testQuery;
	}

}
  1. 创建多数据源,放入Atomikos数据源中进行集中管理:
package chauncy.datasource;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.mysql.cj.jdbc.MysqlXADataSource;

import chauncy.config.DBConfig1;

@Configuration
//basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "chauncy.test01", sqlSessionTemplateRef = "testSqlSessionTemplate1")
public class TestMybatisConfig1 {
	// 配置数据源
	@Primary
	@Bean(name = "testDataSource1")
	public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(testConfig.getUrl());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
		mysqlXaDataSource.setPassword(testConfig.getPassword());
		mysqlXaDataSource.setUser(testConfig.getUsername());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("testDataSource1");

		xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
		xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
		xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
		xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
		xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
		xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
		xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
		xaDataSource.setTestQuery(testConfig.getTestQuery());
		return xaDataSource;
	}

	@Bean(name = "testSqlSessionFactory1")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource1") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Bean(name = "testSqlSessionTemplate1")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("testSqlSessionFactory1") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}
package chauncy.datasource;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.mysql.cj.jdbc.MysqlXADataSource;

import chauncy.config.DBConfig1;

@Configuration
//basePackages 最好分开配置 如果放在同一个文件夹可能会报错
@MapperScan(basePackages = "chauncy.test02", sqlSessionTemplateRef = "testSqlSessionTemplate2")
public class TestMybatisConfig2 {
	// 配置数据源
	@Bean(name = "testDataSource2")
	public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
		MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
		mysqlXaDataSource.setUrl(testConfig.getUrl());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
		mysqlXaDataSource.setPassword(testConfig.getPassword());
		mysqlXaDataSource.setUser(testConfig.getUsername());
		mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

		AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
		xaDataSource.setXaDataSource(mysqlXaDataSource);
		xaDataSource.setUniqueResourceName("testDataSource2");

		xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
		xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
		xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
		xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
		xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
		xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
		xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
		xaDataSource.setTestQuery(testConfig.getTestQuery());
		return xaDataSource;
	}

	@Bean(name = "testSqlSessionFactory2")
	public SqlSessionFactory testSqlSessionFactory(@Qualifier("testDataSource2") DataSource dataSource)
			throws Exception {
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
		bean.setDataSource(dataSource);
		return bean.getObject();
	}

	@Bean(name = "testSqlSessionTemplate2")
	public SqlSessionTemplate testSqlSessionTemplate(
			@Qualifier("testSqlSessionFactory2") SqlSessionFactory sqlSessionFactory) throws Exception {
		return new SqlSessionTemplate(sqlSessionFactory);
	}
}
  1. 启动加载配置:
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;

import chauncy.config.DBConfig1;
import chauncy.config.DBConfig2;

@ComponentScan(basePackages = "chauncy.*")
@EnableAutoConfiguration
@EnableConfigurationProperties(value={DBConfig1.class,DBConfig2.class})
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 变更成以上配置后,再次验证“4. 操作多个数据源进行事务管理时,问题重现”中所提问题,不会重现,则证明使用springboot+jta+atomikos可解决操作多个数据源事务管理的问题。

六、日志管理

  1. 使用log4j记录日志:
  1. 新建log4j配置文件:
#log4j.rootLogger=CONSOLE,info,error,DEBUG
log4j.rootLogger=info,error,CONSOLE,DEBUG
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender     
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout     
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n     
log4j.logger.info=info
log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.layout=org.apache.log4j.PatternLayout     
log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n  
log4j.appender.info.datePattern='.'yyyy-MM-dd
log4j.appender.info.Threshold = info   
log4j.appender.info.append=true   
#log4j.appender.info.File=/home/admin/pms-api-services/logs/info/api_services_info
log4j.appender.info.File=D://EclipseWorkspace/EclipseMars2/ArchitectFirstPhase/AllProjectLogs/SpringBoot-Mybatis.log
log4j.logger.error=error  
log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.layout=org.apache.log4j.PatternLayout     
log4j.appender.error.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n  
log4j.appender.error.datePattern='.'yyyy-MM-dd
log4j.appender.error.Threshold = error   
log4j.appender.error.append=true   
#log4j.appender.error.File=/home/admin/pms-api-services/logs/error/api_services_error
log4j.appender.error.File=D://EclipseWorkspace/EclipseMars2/ArchitectFirstPhase/AllProjectLogs/SpringBoot-Mybatis.log
log4j.logger.DEBUG=DEBUG
log4j.appender.DEBUG=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DEBUG.layout=org.apache.log4j.PatternLayout     
log4j.appender.DEBUG.layout.ConversionPattern=%d{yyyy-MM-dd-HH-mm} [%t] [%c] [%p] - %m%n  
log4j.appender.DEBUG.datePattern='.'yyyy-MM-dd
log4j.appender.DEBUG.Threshold = DEBUG   
log4j.appender.DEBUG.append=true   
#log4j.appender.DEBUG.File=/home/admin/pms-api-services/logs/debug/api_services_debug
log4j.appender.DEBUG.File=D://EclipseWorkspace/EclipseMars2/ArchitectFirstPhase/AllProjectLogs/SpringBoot-Mybatis.log
  1. 使用log4j进行日志打印:
package chauncy.controller;

import org.apache.log4j.Logger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/IndexController")
public class IndexController {
	
	private static Logger logger = Logger.getLogger(IndexController.class);

	@RequestMapping("/log")
	public String log(){
		logger.info("test log");
		return "success";
	}
}
  1. 使用浏览器造成请求,监视控制台打印信息,若正确打印,证明springboot整合log4j成功。
  1. 使用AOP统一处理Web请求日志:
  1. 在pom文件中新增引入依赖:
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 编写切面类对日志进行处理:
package chauncy.log;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class WebLogAspect {
	
	private static Logger logger=LoggerFactory.getLogger(WebLogAspect.class);
	
	@Pointcut("execution(public * chauncy.controller.*.*(..))")
	public void webLog() {
	}
	
	@Before("webLog()")
	public void doBefore(JoinPoint joinPoint) throws Throwable {
		// 接收到请求,记录请求内容
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = attributes.getRequest();
		// 记录下请求内容
		logger.info("URL : " + request.getRequestURL().toString());
		logger.info("HTTP_METHOD : " + request.getMethod());
		logger.info("IP : " + request.getRemoteAddr());
		Enumeration<String> enu = request.getParameterNames();
		while (enu.hasMoreElements()) {
			String name = (String) enu.nextElement();
			logger.info("name:{},value:{}", name, request.getParameter(name));
		}
	}
	@AfterReturning(returning = "ret", pointcut = "webLog()")
	public void doAfterReturning(Object ret) throws Throwable {
		// 处理完请求,返回内容
		logger.info("RESPONSE : " + ret);
	}


}
  1. 对chauncy.controller包下任意类任意方法造成请求,都会打印相关详细信息,如请求URL、当前服务器IP、请求类型、参数名称、参数值、请求状态(是否成功)等。

七、缓存支持

  1. 缓存分为:1.jvm缓存(只能在java中使用,并且只在当前jvm有效) 2. 内存缓存(redis、mongodb等非关系型数据库)
  2. 为什么要用到缓存?
    因为使用缓存可以减轻数据库压力。
  3. 注解配置与EhCache使用:
  1. pom文件引入
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  1. 在src/main/resources下新建ehcache.xml文件,添加如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
	updateCheck="false">
    <!--  
        name:缓存名称。  
        maxElementsInMemory:缓存最大个数。  
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。  
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。  
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。  
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。  
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。  
        maxElementsOnDisk:硬盘最大缓存个数。  
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.  
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。  
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。  
        clearOnFlush:内存数量最大时是否清除。  
    --> 
	
	<diskStore path="java.io.tmpdir/Tmp_EhCache" />

	<!-- 默认配置 -->
	<defaultCache maxElementsInMemory="5000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120"
		memoryStoreEvictionPolicy="LRU" overflowToDisk="false" />

	<cache name="baseCache" maxElementsInMemory="10000"
		maxElementsOnDisk="100000" />

</ehcache>
  1. 编写后台请求,持久化层查询使用缓存:
package chauncy.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import chauncy.entity.UserEntity;
import chauncy.mapper.UserMapper;
import chauncy.service.UserService;
import chauncy.test01.mapper.UserMapper1;
import chauncy.test01.service.UserService1;
import chauncy.test02.service.UserService2;

@RestController
@RequestMapping("/IndexController")
public class IndexController {
	
	private static Logger logger = Logger.getLogger(IndexController.class);
	
	@Autowired
	private UserMapper1 userMapper1;

	@RequestMapping("/getUserName")
	public UserEntity getUserName(String name){
		return userMapper1.findByUserName(name);
	}
}
package chauncy.test01.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;

import chauncy.entity.UserEntity;

@CacheConfig(cacheNames = "baseCache")
public interface UserMapper1 {
	
	@Select("select * from users t where t.name=#{name}")
	@Cacheable
	public UserEntity findByUserName(@Param("name")String name);
}
  1. 编写启动类,启动容器:
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;

import chauncy.config.DBConfig1;
import chauncy.config.DBConfig2;

@ComponentScan(basePackages = "chauncy.*")
@EnableCaching // 开启缓存注解
@EnableAutoConfiguration
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 清除缓存的请求代码:
package chauncy.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/IndexController")
public class IndexController {
	
	private static Logger logger = Logger.getLogger(IndexController.class);

	@Autowired
	private CacheManager cacheManager;
	
	@RequestMapping("/removeCache")
	public String removeCache() {
		cacheManager.getCache("baseCache").clear();
		return "success";
	}
}

八、其他内容

  1. 使用@Scheduled创建定时任务:
    在Spring Boot的主类中加入@EnableScheduling注解,启用定时任务的配置(以下示例为简单demo,没考虑分布式、异常等情况):
package chauncy;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTask {
	
	@Scheduled(fixedRate=1000)
	public void add(){
		System.out.println("正在执行。。。"+System.currentTimeMillis());
	}
}
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;

import chauncy.config.DBConfig1;
import chauncy.config.DBConfig2;

@ComponentScan(basePackages = {"chauncy.*","chauncy"})
@EnableScheduling//开启定时任务注解
@EnableAutoConfiguration
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 使用@Async实现异步调用(多线程的使用):
    启动加上@EnableAsync,需要执行异步方法上加入@Async。
    PS:多线程的情况,执行路径会打乱。使用多线程的好处:提高程序的效率。
package chauncy.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/IndexController")
public class IndexController {
	
	private static Logger logger = Logger.getLogger(IndexController.class);

	@Autowired
	private UserService1 userService1;
	
	@RequestMapping("/sendSms")
	public String sendSms(){
		System.out.println("userService1->sendSms()开始调用");
		userService1.sendSms();
		System.out.println("userService1->sendSms()结束调用");
		return "success";
	}
}
package chauncy.test01.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class UserService1 {
	
	//当一个方法执行时间比较长,就要使用多线程
	@Async
	public void sendSms(){
		System.out.println("sendSms()方法开始执行。。。");
		for (int i = 0; i < 5; i++) {
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
			System.out.println("i:"+i);
		}
		System.out.println("sendSms()方法结束执行。。。");
	}
}
package chauncy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

import chauncy.config.DBConfig1;
import chauncy.config.DBConfig2;

@ComponentScan(basePackages = {"chauncy.*","chauncy"})
@EnableAsync//开启异步注解	
@EnableAutoConfiguration
@EnableConfigurationProperties(value = { DBConfig1.class, DBConfig2.class })
public class APP {

	public static void main(String[] args) {
		SpringApplication.run(APP.class, args);
	}
}
  1. 获取自定义参数:
  1. 配置文件中创建自定义参数:
name=ChauncyWang
  1. 代码中获取自定义参数:
package chauncy.controller;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/IndexController")
public class IndexController {
	
	private static Logger logger = Logger.getLogger(IndexController.class);
	
	@Value("${name}")
	private String name;

	@RequestMapping("/getName")
	public String getName(){
		return name;
	}
}
  1. 多环境区分:
  1. 实际项目开发中分为:Dev(开发环境)、Sit(集成测试环境)、Pre(预生产环境、灰度环境)、Prd(生产环境),针对于不同环境需要进行不同的配置处理,此时就要进行多环境区分。
  2. 具体实现:
  1. 新建两个不同环境配置文件:
  1. application-pre.properties
name=pre.ChauncyWang
  1. application-prd.properties
name=prd.ChauncyWang
  1. 在主环境配置文件中声明使用哪个环境:
spring.profiles.active=prd
  1. 获取自定义参数步骤如上述3。
  1. 修改端口号:
    在配置文件中写入:
server.port = 80
server.context.path=/
  1. SpringBootYml使用:
    创建application.yml(注意ymL规范,冒号:后面要有空格):
server:
  port: 80
  context-path: /
  1. 发布打包
  1. 使用命令mvn package进行打包。
  2. 打包完成会在项目target目录下生成相应的包。
  3. 使用java -jar 包名 运行包。
    如果报错“没有主清单”,在pom文件中增加:
<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
			<configuration>
				<source>1.8</source>
				<target>1.8</target>
			</configuration>
		</plugin>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<configuration>
				<maimClass>chauncy.App</maimClass>
			</configuration>
			<executions>
				<execution>
					<goals>
						<goal>repackage</goal>
					</goals>
				</execution>
			</executions>

		</plugin>
	</plugins>
</build>