(不好看)SpringBoot 从入门到进阶系列官方小册。自定属性,swagger2,redis,mongoDB,Mybatis,JPA,Druid,log4j,Aop

社区开源项⽬ http://www.spring4all.com/projects

1. ⾃定义属性与加载

有坑

application.properties 配置中⽂值的时候,

  • 读取出来的属性值会出现乱码问题。
  • 但是 application.yml不会出现乱码问题。
  • 原因是, Spring Boot 是以 iso-8859 的编码⽅式读取 application.properties 配置⽂件。

注意这⾥, 还有⼀个坑:

  • 如果定义⼀个键值对 user.name=xxx ,这⾥会读取不到对应写的属性值。
    • Spring Boot 的默认 StandardEnvironment ⾸先将会加载 “systemEnvironment" 作为⾸个PropertySource.
    • ⽽ source 即为System.getProperties().当 getProperty时,按照读取顺序,返回 “systemEnvironment" 的值.即
      System.getProperty(“user.name”)
    • Mac 机⼦会读⾃ ⼰的登录账号 。win 也是读取自己的 如lisi,如姓李,和 账户文件夹一样

@Value基本读取

com.didispace.blog.name=程序猿DD
com.didispace.blog.title=Spring Boot教程
@Component
public class BlogProperties {
    @Value("${com.didispace.blog.name}")
    private String name;
    @Value("${com.didispace.blog.title}")
    private String title;
// 省略getter和setter
}

@Autowired
private BlogProperties blogProperties;
  • 参数间的引用
com.didispace.blog.name=程序猿DD
com.didispace.blog.title=Spring Boot教程
com.didispace.blog.desc=${com.didispace.blog.name}正在努⼒写《${com.didispace.blog.title}》

配置文件 成为对象 ConfigurationProperties

## 家乡属性 Dev
home.province=ZheJiang
home.city=WenLing
home.desc=dev: I'm living in ${home.province} ${home.city}
## 家乡属性
home:
  province: 浙江省
  city: 温岭松⻔
  desc: 我家住在${home.province}的${home.city}
@Component
@Data
@ConfigurationProperties(prefix = "home")
public class HomeProperties {
    /**
     * 省份
     */
    private String province;
    /**
     * 城市
     */
    private String city;
    /**
     * 描述
     */
    private String desc;
}

使用随机数

## 随机属性
user:
  uuid: ${random.uuid}
  ## 39546799-bff9-4f85-9845-ba2763a843c7
# 随机字符串 3f969cbff90a483561b0a01ddfd4e94b
com.didispace.blog.value=${random.value}
# 随机int 1517556883
com.didispace.blog.number=${random.int}
# 随机long -4657472138644442323
com.didispace.blog.bignumber=${random.long}


# 10以内的随机数 8
com.didispace.blog.test1=${random.int(10)}
# 10-20的随机数 17
com.didispace.blog.test2=${random.int[10,20]}
value=9c1a7d09c3b4cc3e49f0839d5abef437, 

number=1087803704, 

bignumber=3872231150398265501, 

test1=0, 
test2=13

运行时 设置

java -jar xxx.jar --server.port=8888

java -jar xxx.jar --spring.profiles.active=test
java -jar -Dspring.profiles.active=prod 1.jar
设置就能屏蔽: SpringApplication.setAddCommandLineProperties(false)

多环境配置

如: spring.profiles.active=test 就会加载 application-test.properties 配置⽂件内容

application-{profile}.properties 
 
application-dev.properties : 开发环境
application-test.properties : 测试环境
application-prod.properties : ⽣产环境
java -jar xxx.jar --spring.profiles.active=test

2. RESTful API

REST 是属于 WEB ⾃身的⼀种架构⻛格, 是在 HTTP 1.1 规范下实现的。

  • Representational State Transfer 全称翻译为表现层状态转化。
    • Resource: 资源。 ⽐如 newsfeed;
    • Representational: 表现形式, ⽐如⽤JSON, 富⽂本等;
    • State Transfer: 状态变化。 通过HTTP 动作实现。

RESTful API具体设计如下:

请求类型URL功能说明
GET/users查询⽤户列表
POST/users创建⼀个⽤户
GET/users/id根据id查询⼀个⽤户
PUT/users/id根据id更新⼀个⽤户
DELETE/users/id根据id删除⼀个⽤户

User 和 controller

@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
}
@RestController
@RequestMapping(value = "/users")
public class UserController {
    /**
     * 创建线程安全的Map
     */
    static Map<Long, User> users = Collections.synchronizedMap(new HashMap<Long, User>());

    @RequestMapping(value = "/", method = RequestMethod.GET) // 需要/才能访问
    @RequestMapping(value={""}, method=RequestMethod.GET) //这样写更好。
    public List<User> getUserList() {
        List<User> r = new ArrayList<User>(users.values());
        return r;
    }

    @RequestMapping(value = "/", method = RequestMethod.POST)
    public String postUser(@ModelAttribute User user) {
        // 除了@ModelAttribute绑定参数之外, 还可以通过@RequestParam从⻚⾯中传递参数
        users.put(user.getId(), user);
        return "success";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public User getUser(@PathVariable Long id) {

        return users.get(id);
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    public String putUser(@PathVariable Long id, @ModelAttribute User user) {
        User u = users.get(id);
        u.setName(user.getName());
        u.setAge(user.getAge());
        users.put(id, u);
        return "success";
    }

    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    public String deleteUser(@PathVariable Long id) {

        users.remove(id);
        return "success";
    }
}

测试

http://localhost:8080/users/	[]

http://localhost:8080/users/?id=1&name=张三&age=22	success

http://localhost:8080/users/1
{
    "id": 1,
    "name": "张三",
    "age": 22
}

http://localhost:8080/users/1?id=1&name=张三&age=222	success

http://localhost:8080/users/1		success

3. controller注解说明

@RequestMapping 处理请求地址映射。

  • method - 指定请求的⽅法类型: POST/GET/DELETE/PUT 等
  • value - 指定实际的请求地址
  • consumes - 指定处理请求的提交内容类型, 例如 Content-Type 头部设置application/json,
    text/html 。限制对方。
  • produces - 指定返回的内容类型。提供给对方。

@PathVariable URL 映射时, ⽤于绑定请求参数到⽅法参数
@RequestBody 这⾥注解⽤于读取请求体 boy 的数据, 通过 HttpMessageConverter 解析绑定到对象

HTTP 知识补充

  • GET 请求获取Request-URI所标识的资源
  • POST 在Request-URI所标识的资源后附加新的数据
  • HEAD 请求获取由Request-URI所标识的资源的响应消息报头
  • PUT 请求服务器存储⼀个资源, 并⽤Request-URI作为其标识
  • DELETE 请求服务器删除Request-URI所标识的资源
  • TRACE 请求服务器回送收到的请求信息, 主要⽤于测试或诊断
    • CONNECT 保留将来使⽤ 。postMan上都没有
  • OPTIONS 请求查询服务器的性能, 或者查询与资源相关的选项和需求

4. 整合Swagger2

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId> //核心
    <version>2.2.2</version>
</dependency>

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId> //ui
    <version>2.2.2</version>
</dependency>

创建Swagger2配置类

@Configuration
@EnableSwagger2 //开启配置
public class Swagger2 {

    @Bean
    public Docket createRestApi() { //摘要 api信息 选择 apis 路径 构建
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.didispace.web"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {//api构建者 标题 说明 关系和服务url 联系 版本 构建
        return new ApiInfoBuilder()
                .title("Spring Boot中使用Swagger2构建RESTful APIs")
                .description("更多Spring Boot相关文章请关注:http://blog.didispace.com/")
                .termsOfServiceUrl("http://blog.didispace.com/")
                .contact("程序猿DD")
                .version("1.0")
                .build();
    }

}
docket 
英 /ˈdɒkɪt/  美 /ˈdɑːkɪt/ 
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. 摘要;记事表;(待判决的)诉讼事件表
vt. 在……上附加摘要

terms 
英 /tɜːmz/  美 /tɜːrmz/  全球(美国)  
简明 牛津 柯林斯 例句  百科
n. 地位,关系;[法]条款;术语;措辞;价钱(term 的复数形式)
v. 把……称为(term 的三单形式)

term 
英 /tɜːm/  美 /tɜːrm/  全球(美国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. (某人做某事或某事发生的)时期,期限,任期;学期,开庭期;到期,期满;术语,专有名词;措辞,说话的方式(terms);方面,角度(terms);(人们之间的)关系(terms);条件,条款(terms);付款条件,购买(出售)条件(terms);(结束战争,解决争端的)条件(terms);(数学运算中的)项;(逻)(三段论中的)项;(尤指苏格兰的)法定结账日(term day);<法律> 有限期租用的地产;(建筑)界标
v. 把……称为,把……叫做

contact 
英 /ˈkɒntækt/  美 /ˈkɑːntækt/  全球(美国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. 联系,联络;接触,触摸;联络人,熟人,社会关系;会见,往来,接触;传染病接触者;隐形眼镜;接通电,触点,接头;(无线电)通信
v. 联系,联络;接触
adj. 供联络的;接触性的,通过接触而起作用的

通过 @Configuration 注解, 让Spring来加载该类配置。 再通过 @EnableSwagger2 注解
来启⽤Swagger2。

  • 再通过 createRestApi 函数创建 Docket 的Bean之后,
  • apiInfo() ⽤来创建该Api的基本信息(这些基本信息会展现在⽂档⻚⾯中) 。
  • select() 函数返回⼀个 ApiSelectorBuilder 实例⽤来控制哪些接⼝暴露给Swagger来展现,
    • 本例采⽤指定扫描的包路径来定义,
  • Swagger会扫描该包下所有Controller定义的API, 并产⽣⽂档内容(除了被 @ApiIgnore 指定的请求) 。

我们通过 @ApiOperation 注解来给API增加说明、

  • 通过 @ApiImplicitParams 、
  • @ApiImplicitParam 注解来给参数增加说明。

真正的使用

  • 注意此时 @ModelAttribute User user,会出现 普通传递参数的 swagger样式
    @ApiIgnore
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    
	//value 简单说明。notes详细说明
    @ApiOperation(value="创建用户", notes="根据User对象创建用户")
	//参数是 user,value说明。必须需要,类型是用户类型的。
    @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")

    @RequestMapping(value="", method=RequestMethod.POST)
    public String postUser(@RequestBody User user) {

	//参数是 id,必须传递,long类型的。参数类型是 path
    @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long", paramType = "path")
    @RequestMapping(value="/{id}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long id) {

    //多个参数 ({}),id是path类型,user的数据类型是User
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "用户ID", required = true, dataType = "Long", paramType = "path"),
            @ApiImplicitParam(name = "user", value = "用户详细实体user", required = true, dataType = "User")
    })
    @RequestMapping(value="/{id}", method=RequestMethod.PUT)
    public String putUser(@PathVariable Long id, @RequestBody User user) {

http://localhost:8080/swagger-ui.html

5. 整合 redis

Redis是⼀个开源的

使⽤ANSI C语⾔编写、 ⽀持⽹络、

可基于内存亦可持久化的⽇ 志型、

Key-Value数据库。

引⼊依赖 和 参数配置

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-redis</artifactId>
		</dependency>
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0

# Redis服务器地址
spring.redis.host=localhost

# Redis服务器连接端⼝
spring.redis.port=6379

# Redis服务器连接密码(默认为空)
spring.redis.password=

# 连接池最⼤连接数(使⽤负值表示没有限制)
spring.redis.pool.max-active=8

# 连接池最⼤阻塞等待时间(使⽤负值表示没有限制)
spring.redis.pool.max-wait=-1

# 连接池中的最⼤空闲连接
spring.redis.pool.max-idle=8

# 连接池中的最⼩空闲连接
spring.redis.pool.min-idle=0

# 连接超时时间(毫秒)
spring.redis.timeout=0
spring:
  redis:
    database: 0
    host:
    port: 6379
    password:
    pool:
      max-active: 8
      max-wait: -1
      max-idle: 8
      min-idle: 0
    timeout: 0

其中spring.redis.database的配置通常使⽤0即可,

Redis在配置的时候可以设置数据库数量, 默认为
16, 可以理解为数据库的schema

测试访问

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Spring4allApplication.class)
public class ApplicationTests {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void test() throws Exception {
        // 保存字符串
        stringRedisTemplate.opsForValue().set("aaa", "111");
        Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));
    }
}
  • 使用 controller 也可以测试

如果有使⽤过spring-data-redis的开发者
⼀定熟悉 RedisTemplate 接⼝, StringRedisTemplate 就相当于 RedisTemplate 的实现。

6. MongoDB简介

MongoDB是⼀个

  • 基于分布式 ⽂件存储的数据库,

  • 它是⼀个介于关系数据库和⾮关系数据库之间的产
    品,

  • 其主要⽬标是在 键/值存储⽅式(提供了⾼性能和⾼度伸缩性)

  • MongoDB⽀持的数据结构⾮常松散, 是类似json的bson格式, 因此可以存储⽐较复杂的数据类型,

  • 其语法有点类似于⾯向对象的查询语⾔, ⼏乎
    可以实现类似关系数据库单表查询的绝⼤部分功能, ⽽且还⽀持对数据建⽴索引。

  • 较常⻅的, 我们可以直接⽤MongoDB来存储键值对类型的数据, 如: 验证码、 Session等; 由于
    MongoDB的横向扩展能⼒,

  • 也可以⽤来存储数据规模会在未来变的⾮常巨⼤的数据, 如: ⽇ 志、 评论
    等;

  • 由于MongoDB存储数据的弱类型, 也可以⽤来存储⼀些多变json数据, 如: 与外系统交互时经常
    变化的JSON报⽂。 ⽽对于⼀些对数据有复杂的⾼事务性要求的操作, 如: 账户交易等就不适合使⽤
    MongoDB来存储。

引入

spring-boot-starter-data-mongodb 引⼊对mongodb的访问
⽀持依赖。 它的实现依赖 spring-data-mongodb 。

  • ⼜是 spring-data 的⼦项⽬,
    • 之前介绍过 spring-data-jpa 、
    • spring-data-redis
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

User 和 Dao

public class User {
    @Id
    private Long id;
    private String username;
    private Integer age;
    
    public User(Long id, String username, Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    } // 省略getter和setter
}

实现User的数据访问对象: UserRepository

public interface UserRepository extends MongoRepository<User, Long> {
	User findByUsername(String username);
}

测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class ApplicationTests {
    @Autowired
    private UserRepository userRepository;

    @Before
    public void setUp() {
        userRepository.deleteAll();
    }

    @Test
    public void test() throws Exception {

        // 创建三个User, 并验证User总数
        userRepository.save(new User(1L, "didi", 30));
        userRepository.save(new User(2L, "mama", 40));
        userRepository.save(new User(3L, "kaka", 50));

        Assert.assertEquals(3, userRepository.findAll().size());

        // 删除⼀个User, 再验证User总数
        User u = userRepository.findOne(1L);
        userRepository.delete(u);
        Assert.assertEquals(2, userRepository.findAll().size());

        // 删除⼀个User, 再验证User总数
        u = userRepository.findByUsername("mama");
        userRepository.delete(u);
        Assert.assertEquals(1, userRepository.findAll().size());
    }
}

参数配置

spring.data.mongodb.uri=mongodb://name:pass@localhost:27017/test

在尝试此配置时, 记得在mongo中对test库创建具备读写权限的⽤户(⽤户名为name, 密码为
pass) , 不同版本的⽤户创建语句不同,

若使⽤mongodb 2.x, 也可以通过如下参数配置, 该⽅式不⽀持mongodb 3.x。

spring.data.mongodb.host=localhost 
spring.data.mongodb.port=27017  

这边没有使⽤ Mybatis Annotation 这种, 是使⽤ xml 配置 SQL。

7. springboot-mybatis

1.数据库准备 和 引入

CREATE DATABASE springbootdb;

DROP TABLE IF EXISTS `city`;
CREATE TABLE `city` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '城市编号',
    `province_id` int(10) unsigned NOT NULL COMMENT '省份编号',
    `city_name` varchar(25) DEFAULT NULL COMMENT '城市名称',
    `description` varchar(25) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

INSERT city VALUES (1 ,1,'温岭市','BYSocket 的家在温岭。 ');

SELECT VERSION();
5.7.25-log
5.7还可以,在高 如8.0.26。就会报各种错。
http://localhost:8080/api/city?cityName=温岭市

{
    "id": 1,
    "provinceId": 1,
    "cityName": "温岭市",
    "description": "我的家在温岭。 "
}
  • 引入pom
<?xml version="1.0" encoding="UTF-8"?>
<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>springboot</groupId>
    <artifactId>springboot-mybatis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-mybatis</name>

    <!-- Spring Boot 启动父依赖 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <properties>
        <mybatis-spring-boot>1.2.0</mybatis-spring-boot>
        <mysql-connector>5.1.39</mysql-connector>
    </properties>

    <dependencies>

        <!-- Spring Boot Web 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Test 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Spring Boot Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot}</version>
        </dependency>

        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector}</version>
        </dependency>

        <!-- Junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

</project>

2. 配置mybatis

  • 在 application.properties ,应⽤配置⽂件, 增加 Mybatis 相关配置
## 数据源配置
spring.datasource.url=jdbc:mysql://192.168.2.219:3306/springbootdb?useUnicode=true&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

## Mybatis 配置
mybatis.typeAliasesPackage=org.spring.springboot.domain
mybatis.mapperLocations=classpath:mapper/*.xml

mybatis.typeAliasesPackage 配置为 org.spring.springboot.domain, 指向实体类包路径。
mybatis.mapperLocations 配置为 classpath 路径下 mapper 包下, * 代表会扫描所有 xml ⽂件。

mybatis 其他配置相关详解如下:

mybatis.config = mybatis 配置⽂件名称

mybatis.mapperLocations = mapper xml ⽂件地址
mybatis.typeAliasesPackage = 实体类包路径

mybatis.typeHandlersPackage = type handlers 处理器包路径
mybatis.check-config-location = 检查 mybatis 配置是否存在, ⼀般命名为 mybatis-config.xml
mybatis.executorType = 执⾏模式。 默认是 SIMPLE
// Spring Boot 应⽤的标识
@SpringBootApplication

// mapper 接⼝类扫描包配置
@MapperScan("org.spring.springboot.dao")
public class Application {
    public static void main(String[] args) {
        
        // 程序启动⼊⼝
        // 启动嵌⼊式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
        SpringApplication.run(Application.class,args);
        }
}

mapper 接⼝类扫描包配置注解 MapperScan : ⽤这个注解可以注册 Mybatis mapper 接⼝类。

3. 添加相应的 controller service dao domain

@Data
public class City {
    /**
     * 城市编号
     */
    private Long id;
    /**
     * 省份编号
     */
    private Long provinceId;
    /**
     * 城市名称
     */
    private String cityName;
    /**
     * 描述
     */
    private String description;
}
@RestController
public class CityRestController {

    @Autowired
    private CityService cityService;

    @RequestMapping(value = "/api/city", method = RequestMethod.GET)
    public City findOneCity(@RequestParam(value = "cityName", required = true) String cityName) {
        return cityService.findCityByName(cityName);
    }

}

public interface CityService {

    /**
     * 根据城市名称,查询城市信息
     * @param cityName
     */
    City findCityByName(String cityName);
}


@Service
public class CityServiceImpl implements CityService {

    @Autowired
    private CityDao cityDao;

    public City findCityByName(String cityName) {
        return cityDao.findByName(cityName);
    }

}

public interface CityDao {
    /**
     * 根据城市名称, 查询城市信息
     * *
     *
     * @param cityName 城市名
     */
    City findByName(@Param("cityName") String cityName);
}

5. mapper.xml

src/main/resources/mapper/CityMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.spring.springboot.dao.CityDao">
	<resultMap id="BaseResultMap" type="org.spring.springboot.domain.City">
		<result column="id" property="id" />
		<result column="province_id" property="provinceId" />
		<result column="city_name" property="cityName" />
		<result column="description" property="description" />
	</resultMap>

	<sql id="Base_Column_List">
		id, province_id, city_name, description
	</sql>

	<select id="findByName" resultMap="BaseResultMap" parameterType="java.lang.String">
		select
		<include refid="Base_Column_List" />
		from city
		where city_name = #{cityName}
	</select>

</mapper>

三、 其他
利⽤ Mybatis-generator⾃动⽣成代码 http://www.cnblogs.com/yjmyzz/p/4210554.html Mybatis 通⽤
Mapper3 https://github.com/abel533/Mapper

Mybatis 分⻚插件 PageHelper
https://github.com/pagehelper/Mybatis-PageHelper

4. annotation配置

@Mapper // 标志为 Mybatis 的 Mapper
public interface CityDao {
    /**
     * 根据城市名称, 查询城市信息
     * *
     @param cityName 城市名
     */
    @Select("SELECT * FROM city where city_name=${cityName}")
// 返回 Map 结果集
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "provinceId", column = "province_id"),
            @Result(property = "cityName", column = "city_name"),
            @Result(property = "description", column = "description"),
    })
    City findByName(@Param("cityName") String cityName);
}

8. jpa

引入pom

由于Spring-data-jpa依赖于Hibernate。

        <!-- Spring Data JPA 依赖 :: 数据持久层框架 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!-- h2 数据源依赖 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
## 是否启动日志 SQL 语句
spring.jpa.show-sql=true

model 和 Jpa

@Entity
public class Book implements Serializable {

    /**
     * 编号
     */
    @Id
    @GeneratedValue
    private Long id;

    /**
     * 书名
     */
    private String name;

    /**
     * 作者
     */
    private String writer;

    /**
     * 简介
     */
    private String introduction;
    
    }
public interface BookRepository extends JpaRepository<Book, Long> {
    
    User findByName(String name);
    
	@Query("from User u where u.name=:name")
	User findUser(@Param("name") String name);
    
}
spring.jpa.properties.hibernate.hbm2ddl.auto=create-drop


spring.jpa.properties.hibernate.hbm2ddl.auto 是hibernate的配置属性, 其主要作⽤是: ⾃动创
建、 更新、 验证数据库表结构。 该参数的⼏种配置如下

  • create
  • create-drop
  • update
  • validate

9. Druid

Druid是阿里巴巴的一个开源项目,

  • 号称为监控而生的数据库连接池,

  • 在功能、性能、扩展性方面都超过其他

    • 例如 DBCP、C3P0、BoneCP、Proxool、JBoss、DataSource 等连接池,
    • 而且Druid已经在阿里巴巴部署了超过600个应用,通过了极为严格的考验
  • Druid 是 Java 的数据库连接池组件

Druid 能够提供强⼤的监控和扩展功能。

  • ⽐如可以监控 SQL , 在监控业务可以查询 慢查询 SQL 列表等。

Druid 核⼼主要包括三部分:

  1. DruidDriver 代理 Driver, 能够提供基于 Filter- Chain 模式的插
    件体系。
  2. DruidDataSource ⾼效可管理的数据库连接池
  3. SQLParser 当业务数据量达到了⼀定程度, DBA 需要合理配置数据库资源。
    1. 即配置主库的机器⾼配置, 把核⼼⾼频的数据放在主库上;
    2. 把次要的数据放在从库, 低配置。 开源节流嘛,
      1. 把数据放在不同的数据库⾥,
      2. 就需要通过不同的数据源进⾏操作数据。

user ⽤户表在主库 master 上, 地址表 city 在从库 cluster 上。

需要从主库和从库中分别获取数据, 并在业务逻辑层组装返回。

CREATE DATABASE springbootdb; - 从库,获取地址
CREATE DATABASE springbootdb_cluster; - 主库,获取用户名

http://localhost:8080/api/user?userName=泥瓦匠

{
"id": 1,
"userName": "泥瓦匠",
"description": "他有⼀个⼩⽹站 bysocket.com",
    "city": {
        "id": 1,
        "provinceId": 1,
        "cityName": "温岭市",
        "description": "BYSocket 的家在温岭。 "
    }
}

导入和properties

<!-- Druid 数据连接池依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>${druid}</version>
</dependency>
  1. application.properties 配置两个数据源配置 数据源配置会被数据源数据源配置如下
## master 数据源配置
master.datasource.url=jdbc:mysql://localhost:3306/springbootdb?useUnicode=true&characterEn
coding=utf8
master.datasource.username=root
master.datasource.password=123456
master.datasource.driverClassName=com.mysql.jdbc.Driver

## cluster 数据源配置
cluster.datasource.url=jdbc:mysql://localhost:3306/springbootdb_cluster?useUnicode=true&ch
aracterEncoding=utf8
cluster.datasource.username=root
cluster.datasource.password=123456
cluster.datasource.driverClassName=com.mysql.jdbc.Driver

java配置

  1. 数据源配置 多数据源配置的时候注意, 必须要有⼀个主数据源, 即 MasterDataSourceConfig 配
@Configuration
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {

    // 精确到 master 目录,以便跟其他数据源隔离
    static final String PACKAGE = "org.spring.springboot.dao.master";
    static final String MAPPER_LOCATION = "classpath:mapper/master/*.xml";

    @Value("${master.datasource.url}")
    private String url;

    @Value("${master.datasource.username}")
    private String user;

    @Value("${master.datasource.password}")
    private String password;

    @Value("${master.datasource.driverClassName}")
    private String driverClass;

    @Bean(name = "masterDataSource")
    @Primary
    public DataSource masterDataSource() {
        //druid配置
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        return dataSource;
    }

    //DataSourceTransactionManager
    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSource());
    }

    
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
            throws Exception {
        //创建 SqlSessionFactoryBean
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        
        //设置 数据源
        sessionFactory.setDataSource(masterDataSource);
        
        //设置本地的 的mapper ,
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(MasterDataSourceConfig.MAPPER_LOCATION));
        
        return sessionFactory.getObject();
    }
}

这个server有两个实现类如何区分开这两个impl呢?

Qualifier的意思是合格者,通过这个标示,表明了哪个实现类才是我们所需要的,添加@Qualifier注解,需要注意的是@Qualifier的参数名称为我们之前定义@Service注解的名称之一。

@MapperScan 扫描 Mapper 接⼝并
容器管理, 包路径精确到 master, 为了和下⾯ cluster 数据源做到精确区分 @Value 获取全局配置⽂

application.properties 的 kv 配置,并⾃动装配 sqlSessionFactoryRef 表示定义了 key , 表示⼀个唯
⼀ SqlSessionFactory 实例 同理可得, 从数据源 ClusterDataSourceConfig 配置如下:

@Configuration
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = ClusterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "clusterSqlSessionFactory")
public class ClusterDataSourceConfig {

    // 精确到 cluster 目录,以便跟其他数据源隔离
    static final String PACKAGE = "org.spring.springboot.dao.cluster";
    static final String MAPPER_LOCATION = "classpath:mapper/cluster/*.xml";

    @Value("${cluster.datasource.url}")
    private String url;

    @Value("${cluster.datasource.username}")
    private String user;

    @Value("${cluster.datasource.password}")
    private String password;

    @Value("${cluster.datasource.driverClassName}")
    private String driverClass;

    @Bean(name = "clusterDataSource")
    public DataSource clusterDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean(name = "clusterTransactionManager")
    public DataSourceTransactionManager clusterTransactionManager() {
        return new DataSourceTransactionManager(clusterDataSource());
    }

    @Bean(name = "clusterSqlSessionFactory")
    public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource clusterDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(clusterDataSource);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(ClusterDataSourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }
}

10. 格式化⽇志

基本格式

Spring Boot在所有内部⽇ 志中使⽤

  • Commons Logging, 但是默认配置也提供了对常⽤⽇ 志的⽀持,
    如:
  • Java Util Logging,
  • Log4J,
  • Log4J2和
  • Logback。 每种Logger都可以通过配置使⽤控制台或者⽂件
    输出⽇ 志内容。

默认的⽇ 志输出如下

2016-04-13 08:23:50.120 INFO 37397 --- [ main] org.hibernate.Version : HHH000412: Hiberna
te Core {4.3.11.Final}

输出内容元素具体如下:

  • 时间⽇ 期: 精确到毫秒

  • ⽇ 志级别: ERROR, WARN, INFO, DEBUG or TRACE

  • 进程ID

  • 分隔符: — 标识实际⽇ 志的开始

  • 线程名: ⽅括号括起来(可能会截断控制台输出)

  • Logger名: 通常使⽤源代码的类名

  • ⽇ 志内容

控制台输出

在Spring Boot中默认配置了 ERROR 、 WARN 和 INFO 级别的⽇ 志输出到控制台。
我们可以通过两种⽅式切换⾄ DEBUG 级别:
在运⾏命令后加⼊ --debug 标志,

  • 如: $ java -jar myapp.jar --debug
  • 在 application.properties 中配置 debug=true , 该属性置为true的时候, 核⼼Logger(包含嵌⼊
    式容器、 hibernate、 spring) 会输出更多内容, 但是你⾃⼰应⽤的⽇ 志并不会输出为DEBUG级
    别。

多彩输出

如果你的终端⽀持ANSI, 设置彩⾊输出会让⽇ 志更具可读性。 通过在 application.properties 中设
置 spring.output.ansi.enabled 参数来⽀持。

⽂件输出

若要增加⽂件输出, 需要在 application.properties 中配置 logging.file 或 logging.path 属性。

  • logging.file, 设置⽂件, 可以是绝对路径, 也可以是相对路径。
    • 如: logging.file=my.log‘
    • 默认在项目的classpath目录,就是pom文件 src同目录
  • logging.path, 设置⽬录, 会在该⽬录下创建spring.log⽂件, 并写⼊⽇ 志内容,
    • 如: logging.file.path=/var/log
    • 发现没用

⽇ 志⽂件会在10Mb⼤⼩的时候被截断, 产⽣新的⽇ 志⽂件, 默认级别为: ERROR、 WARN、 INFO

级别控制

配置格式: logging.level.*=LEVEL

  • logging.level : ⽇ 志级别控制前缀, * 为包名或Logger名
  • LEVEL : 选项TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF

举例:

  • logging.level.com.didispace=DEBUG : com.didispace 包下所有class以DEBUG级别输出

  • logging.level.root=WARN : root⽇ 志以WARN级别输出

⾃ 定义⽇ 志配置

由于⽇ 志服务⼀般都在ApplicationContext创建前就初始化了, 它并不是必须通过Spring的配置⽂件控
制。 因此通过系统属性和传统的Spring Boot外部配置⽂件依然可以很好的⽀持⽇ 志控制和管理。

据不同的⽇ 志系统, 你可以按如下规则组织配置⽂件名, 就能被正确加载:

  • Logback: logback-spring.xml , logback-spring.groovy , logback.xml , logback.groovy
  • Log4j: log4j-spring.properties , log4j-spring.xml , log4j.properties , log4j.xml
  • Log4j2: log4j2-spring.xml , log4j2.xml
  • JDK (Java Util Logging): logging.properties

Spring Boot官⽅推荐优先使⽤带有 -spring 的⽂件名作为你的⽇ 志配置(如使⽤ logbackspring.xml , ⽽不是 logback.xml )

⾃ 定义输出格式

在Spring Boot中可以通过在 application.properties 配置如下参数控制输出格式:

logging.pattern.console: 定义输出到控制台的样式(不⽀持JDK Logger)
logging.pattern.file: 定义输出到⽂件的样式(不⽀持JDK Logger)

11. 引⼊log4j依赖

spring-boot-starter , 其中包含了 spring-boot-starterlogging

引入

Spring Boot默认的⽇ 志框架Logback

所以我们在引⼊log4j之前, 需要先
排除该包的依赖, 再引⼊log4j的依赖,

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
		<exclusions>
			<exclusion>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-logging</artifactId>
			</exclusion>
		</exclusions>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-log4j</artifactId>
	</dependency

在引⼊了log4j依赖之后, 只需要在 src/main/resources ⽬录下加⼊ log4j.properties 配置⽂件, 就
可以开始对应⽤的⽇ 志进⾏配置使⽤。

log4j-spring.properties 更好吧

log4j配置

# LOG4J配置。写在 log4j.properties 配置文件的最前边
# stdout是控制台。file是root日志输出。errorfile是错误日志输出。
log4j.rootCategory=INFO, stdout, file, errorfile

log4j.category.com.didispace=DEBUG, didifile

log4j.logger.error=errorfile

控制台输出

设定root⽇ 志的输出级别为INFO, appender为控制台输出stdout

# 控制台输出。统一为:log4j.appender现在配置 控制台。
log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

#%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L -%m%n

root⽇志输出

⽣产或测试环境, 或许持久化⽇ 志内容, ⽅
便追溯问题原因。 可以通过添加如下的appender内容, 按天输出到不同的⽂件中去, 同时还需要
为 log4j.rootCategory 添加名为file的appender, 这样root⽇ 志就可以输出到 logs/all.log ⽂件中
了。


# root⽇志输出
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender

log4j.appender.file.file=logs/all.log

log4j.appender.file.DatePattern='.'yyyy-MM-dd


log4j.appender.file.layout=同上

log4j.appender.file.layout.ConversionPattern=同上

分类输出

  • (错误日志 和 自定义目录)

如:

  • 可以按不同package进⾏输出。 通过定义输出到 logs/my.log 的appender,
  • 并对 com.didispace 包下的⽇ 志级别设定为DEBUG级别、
  • appender设置为输出到 logs/my.log 的
    名为 didifile 的appender。
# error日志输出
log4j.appender.errorfile=同上DailyRollingFileAppender

log4j.appender.errorfile.file=logs/error.log

log4j.appender.errorfile.DatePattern=同上

log4j.appender.errorfile.Threshold = ERROR

log4j.appender.errorfile.layout=同
log4j.appender.errorfile.layout.ConversionPattern=同
# com.didispace下的⽇ 志输出
log4j.appender.didifile=同DailyRollingFileAppender

log4j.appender.didifile.file=logs/my.log

log4j.appender.didifile.DatePattern=同

log4j.appender.didifile.layout=同

log4j.appender.didifile.layout.ConversionPattern=同

12. 使用Aop处理Web请求

AOP为Aspect Oriented Programming的缩写,

  • 切面 面向 程序
Aspect 
英 /ˈæspekt/  美 /ˈæspekt/  全球(美国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. 方面,特色;朝向,方位;外表,外观;(动词的)体
v. (行星与另一天体)形成角度关系

Oriented 
英 /ˈɔːrientɪd/  美 /ˈɔːrientɪd/  全球(美国)  
简明 韦氏  柯林斯 例句  百科
adj. 以……为方向的,重视……的(=orientated)
v. 朝向,面对,使适合;定向放置(某物);使熟悉,帮助适应(orient 的过去式和过去分词)
GMAT

orient 
英 /ˈɔːrient/  美 /ˈɔːrient/  全球(英国)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
v. 朝向,面对,使适合;定向放置(某物);确定方位,认识方向(orient oneself);引导;使熟悉,帮助适应


program 
英 /ˈprəʊɡræm/  美 /ˈproʊɡræm/  全球(加拿大)  
简明 牛津 新牛津  韦氏  柯林斯 例句  百科
n. (计算机)程序(=programme);计划,规划;(音乐会、戏剧等演出的)节目单,(体育运动、比赛等的)项目介绍;(电视或广播)节目;<美>课程,大纲
v. (给计算机)编写程序,设计程序;设置,预设;使……有倾向;为……安排节目

通过预编译⽅式和运⾏期动态代
理实现程序功能的统⼀维护的⼀种技术。

对既有程序定
义⼀个切⼊点, 然后在其前后切⼊不同的执⾏内容, ⽐如常⻅的有: 打开数据库连接/关闭数据库连
接、 打开事务/关闭事务、 记录⽇ 志等。

controller

@RestController
public class HelloController {
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello(@RequestParam String name) {
    return "Hello " + name;
    }
}

引⼊AOP依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

只要引⼊
了AOP依赖后, 默认已经增加了 @EnableAspectJAutoProxy 。

# AOP
spring.aop.auto=true # Add @EnableAspectJAutoProxy.
spring.aop.proxy-target-class=false # Whether subclass-based (CGLIB) proxies are to be cre
ated (true) as
opposed to standard Java interface-based proxies (false).

⽽当我们需要使⽤CGLIB来实现AOP的时候, 需要配置 spring.aop.proxy-target-class=true , 不然
默认使⽤的是标准Java的实现。

实现Web层的⽇志切⾯

实现AOP的切⾯主要有以下⼏个要素:

  • 使⽤ @Aspect 注解将⼀个java类定义为切⾯类

  • 使⽤ @Pointcut 定义⼀个切⼊点, 可以是⼀个规则表达式, ⽐如下例中某个package下的所有函
    数, 也可以是⼀个注解等。

  • 根据需要在切⼊点不同位置的切⼊内容

  • 使⽤ @Before 在切⼊点开始处切⼊内容

  • 使⽤ @After 在切⼊点结尾处切⼊内容

  • 使⽤ @AfterReturning 在切⼊点return内容之后切⼊内容(可以⽤来对处理返回值做⼀些加⼯
    处理)

  • 使⽤ @Around 在切⼊点前后切⼊内容, 并⾃⼰控制何时执⾏切⼊点⾃身的内容

  • 使⽤ @AfterThrowing ⽤来处理当切⼊内容部分抛出异常之后的处理逻辑

@Aspect
@Order(5)
@Component
public class WebLogAspect {

    private Logger logger = Logger.getLogger(getClass());

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    //定义切点com.didispace.web,就是controller包下的所有
    @Pointcut("execution(public * com.didispace.web..*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        startTime.set(System.currentTimeMillis());

        // 接收到请求,记录请求内容。得到ServletRequestAttributes
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //得到 HttpServletRequest
        HttpServletRequest request = attributes.getRequest();

        // 记录下请求内容 http://localhost:8080/hello
        logger.info("URL : " + request.getRequestURL().toString());
        //HTTP_METHOD : GET
        logger.info("HTTP_METHOD : " + request.getMethod());
        //0:0:0:0:0:0:0:1
        logger.info("IP : " + request.getRemoteAddr());
        
        //com.didispace.web.HelloController.hello
        logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        //ARGS : [didi]
        logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));

    }

    //记录请求返回的对象
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容。RESPONSE:Hello didi
        logger.info("RESPONSE : " + ret);
        //打印 共计的毫秒数
        logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get()));
    }


}

测试

  • http://localhost:8080/hello?name=didi
2022-02-27 14:24:14,415  INFO WebLogAspect:47 - URL : http://localhost:8080/hello
2022-02-27 14:24:20,048  INFO WebLogAspect:48 - HTTP_METHOD : GET
2022-02-27 14:24:24,523  INFO WebLogAspect:49 - IP : 0:0:0:0:0:0:0:1
2022-02-27 14:24:26,480  INFO WebLogAspect:50 - CLASS_METHOD : com.didispace.web.HelloController.hello
2022-02-27 14:24:30,516  INFO WebLogAspect:51 - ARGS : [didi]

2022-02-27 14:24:43,003  INFO WebLogAspect:59 - RESPONSE : Hello didi
2022-02-27 14:24:44,199  INFO WebLogAspect:60 - SPEND TIME : 35499

优化: AOP切⾯的优先级

@Order(i) 注解来标识切⾯的优先级

i的值越⼩,优先级越⾼。

  • 在 @Before 中优先执⾏ @Order(5) 的内容, 再执⾏ @Order(10) 的内容
  • 在 @After 和 @AfterReturning 中优先执⾏ @Order(10) 的内容, 再执⾏ @Order(5) 的内容
    • 先5的before,在10的before,
    • 在具体的方法,
    • 在优先执行10的afterReturning,在执行 5的afterReturning

13. 监控端点

监控与管理, 它就是: spring-boot-starter-actuator

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • boot 2.6.2,啥也没有了
{
	"_links": {
		"self": {
			"href": "http://localhost:9898/actuator",
			"templated": false
		},
		"health": {
			"href": "http://localhost:9898/actuator/health",
			"templated": false
		},
		"health-path": {
			"href": "http://localhost:9898/actuator/health/{*path}",
			"templated": true
		}
	}
}

访问 health
{"status":"UP"}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值