SpringBoot集成Mybatis时mybatis.mapper-locations和@MapperScan的作用

mybatis.mapperLocations
如果xxMapper.xml放的位置和包路径不同就需要加配置以便MyBatis根据xml生成实现类。若和包一样就不需要显式设置该项

@MapperScan
就是spring扫描包,MyBatis扫描的是接口,自动根据xml生成实现来加入spring的ioc容器中

综上

  • 如果mapper接口上加@Mapper就不需要@MapperScan,如果没加就需要@MapperScan
  • 如果mapper类和mapper.xml不在同一文件夹下,就需要在yml中配置mybatis.mapperLocations

Spring中@ConfigurationProperties的使用方法

补充:@EnableConfigurationProperties注解作用

@ConfigurationProperties

Spring源码中大量使用了ConfigurationProperties注解,比如server.port就是由该注解获取到的,通过与其他注解配合使用,能够实现Bean的按需配置。

该注解有一个prefix属性,通过指定的前缀,绑定配置文件中的配置,该注解可以放在类上,也可以放在方法上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.context.properties;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Indexed;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface ConfigurationProperties {
@AliasFor("prefix")
String value() default "";

@AliasFor("value")
String prefix() default "";

boolean ignoreInvalidFields() default false;

boolean ignoreUnknownFields() default true;
}

可以从注解源码中看到,当将该注解作用于方法上时,如果想要有效的绑定配置,那么该方法需要有@Bean注解且所属Class需要有@Configuration注解。

简单一句话概括就是:Sring的有效运行是通过上下文(Bean容器)中Bean的配合完成的,Bean可以简单理解成对象,有些对象需要指定字段内容,那么这些内容我们可以通过配置文件进行绑定,然后将此Bean归还给容器

作用于方法

比较常见的就是配置读写分离的场景。

配置文件内容

1
2
3
4
5
6
7
8
9
10
#数据源
spring.datasource.druid.write.url=jdbc:mysql://localhost:3306/jpa
spring.datasource.druid.write.username=root
spring.datasource.druid.write.password=1
spring.datasource.druid.write.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.druid.read.url=jdbc:mysql://localhost:3306/jpa
spring.datasource.druid.read.username=root
spring.datasource.druid.read.password=1
spring.datasource.druid.read.driver-class-name=com.mysql.jdbc.Driver

java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
public class DruidDataSourceConfig {
/**
* DataSource 配置
* @return
*/
@ConfigurationProperties(prefix = "spring.datasource.druid.read")
@Bean(name = "readDruidDataSource")
public DataSource readDruidDataSource() {
return new DruidDataSource();
}


/**
* DataSource 配置
* @return
*/
@ConfigurationProperties(prefix = "spring.datasource.druid.write")
@Bean(name = "writeDruidDataSource")
@Primary
public DataSource writeDruidDataSource() {
return new DruidDataSource();
}
}

也许有的人看到这里会比较疑惑,prefix并没有指定配置的全限定名,那它是怎么进行配置绑定的呢?

相信大家肯定了解@Value注解,它可以通过全限定名进行配置的绑定,这里的ConfigurationProperties其实就类似于使用多个@Value同时绑定,绑定的对象就是DataSource类型的对象,而且是 隐式绑定 的,意味着在配置文件编写的时候需要与对应类的字段名称 相同

作用于Class类及其用法

配置文件内容

1
2
3
4
5
Copyspring.datasource.url=jdbc:mysql://127.0.0.1:8888/test?useUnicode=false&autoReconnect=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@ConfigurationProperties(prefix = "spring.datasource")
@Component
public class DatasourcePro {

private String url;

private String username;

private String password;

// 配置文件中是driver-class-name, 转驼峰命名便可以绑定成
private String driverClassName;

private String type;

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 String getDriverClassName() {
return driverClassName;
}

public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}

public String getType() {
return type;
}

public void setType(String type) {
this.type = type;
}
}

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Copy@Controller
@RequestMapping(value = "/config")
public class ConfigurationPropertiesController {

@Autowired
private DatasourcePro datasourcePro;

@RequestMapping("/test")
@ResponseBody
public Map<String, Object> test(){

Map<String, Object> map = new HashMap<>();
map.put("url", datasourcePro.getUrl());
map.put("userName", datasourcePro.getUsername());
map.put("password", datasourcePro.getPassword());
map.put("className", datasourcePro.getDriverClassName());
map.put("type", datasourcePro.getType());

return map;
}
}

总结

  1. @ConfigurationProperties 和 @value 有着相同的功能,但是 @ConfigurationProperties的写法更为方便
  2. @ConfigurationProperties 的 POJO类的命名比较严格,因为它必须和prefix的后缀名要一致, 不然值会绑定不上, 特殊的后缀名是“driver-class-name”这种带横杠的情况,在POJO里面的命名规则是 下划线转驼峰 就可以绑定成功,所以就是 “driverClassName”

MacOS下Docker数据卷挂载问题

问题背景及描述

Mac机安装docker镜像,使用了容器卷的方式挂载目录

1
2
3
docker volume create jenkins

docker run -d --name jenkins -p 8080:8080 -p 50000:50000 -v jenkins:/var/jenkins_home jenkins/jenkins:lts

因为忘记jenkins密码,想要修改users/config.xml ,但容器中未安装vim,不能直接修改。就想可以在本地配置文件中修改。 先查看容器卷的本地文件路径,运行如下命令。

1
docker inspect jenkins

找到本地地址如下:

1
2
3
4
5
6
7
8
9
10
11
"Mounts": [
{
"Type": "volume",
"Name": "jenkins-data",
"Source": "/var/lib/docker/volumes/jenkins-data/_data",
"Destination": "/var/jenkins_home",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
},

可是在本地查找对应路径时,却提示没有对应路径或者文件。

解决方案

运行如下命令

1
docker run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh

本地机器再查找容器卷默认路径(ls /var/lib/docker/volumes)路径可查。

原因及解法分析

为什么如此,是因为MacOS 是在本地运行xhyve 虚拟机管理的docker,容器卷是在虚拟机的文件系统中创建, 在macOS的FileSystem无法直接访问。