Spring Boot加载yaml配置到Bean

Spring Boot可能遇到项目启动无法加载yaml配置的情况,可以使用如下配置注入PropertySourcesPlaceholderConfigurer

1
2
3
4
5
6
7
8
@Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("xxx.yml"));
configurer.setProperties(yaml.getObject());
return configurer;
}

时间轴,一年前的自己(2017-2018)

发现已经好久没有写过关于自己生活上的博客文章了,今天还是5月4日青年节。前几日就想写一写自己在一年中有发生了什么变化。

去年的这个时候,准备毕业的我已经从北京返回了哈尔滨。回忆下在毕业前到现在自己都做了些什么吧。17年4月份,我从实习单位提出了离职,当时公司还有很多的变化,实习单位是一个复式办公的环境,在我走之后之前的小伙伴们搬到了楼上,楼梯上看到一句话让我深深的记在了心里,勿忘初心。

image

还有那最后一次的地铁,坐上地铁心情并没有很复杂,相反变得平静了很多。

image

4月末给自己留出了几天的假期,其他的日子早上都在写毕业论文,而晚上则是学习新的知识,也逐渐的在GitHub上坚持了一个礼拜的push记录,两个项目:第一个是把实习单位所收获的做了积累,并完成了AprilDragonSpring虽然是个初学者项目,懵懂时期的自己也是需要有从0到1的过程吧。第二个项目是个成型的web项目Ins_fb_pictureSpider_WEB具体内容要是感兴趣就去看Readme吧。

刚才有提到几天自己的小假期,去了北京天安门城楼、故宫博物院、牛街、中关村、西北旺……
还记得自己发过的一条微博,说自己有朝一日一定要去中关村上班,感受互联网工作文化,梦想也在毕业后实现了。

image

还有一件对于我来说很重要的事情,去了G.E.M.的演唱会,第二次的演唱会,有一些找不到第一次看演唱会的感觉,但气氛棒的没说儿!

image

四月的最后一天,坐上了回家的火车。

image

五月返校到六月份,偶尔也会出去喝点,但绝大部分的时间都是在寝室修改论文格式,要不就是守望先锋,当时吃鸡还玩的比较少。最后的大学时光可能也没有很珍惜,心里一直想着赶快毕业,到时候能找个好一点的工作,大学最后的生活过得很快,两个月就这么过去了,四年的日子也画上了句号。失去后才懂得珍惜过去,可是那还会怎样呢…

image

毕业后在家混了几天的日子就发现自己有些待不住了,整理了下面试的知识然后看了下一位朋友送的面试宝典,带着那所谓的梦后重新踏上了北京的路。7月份炎热的北京,只要出去就会汗流浃背。找工作的路上还是顺利的,1号收到offer,3号入职,有运气有贵人,可能在找工作上这小‘命’还是不错的说。工作上也学到的很多知识,开阔了眼界,掌握了许多较为流行的开发思想,一直都在努力学习,并坚持向GitHub上push积累的知识,也算是自己成长道路上的点滴记录。

我很喜欢在之前的时间轴上走上那么一走,于是8月份去了趟朝阳公园,每次去的感觉都会有不一样的收获,不知道有没有朋友和我有一样的感觉。十月份假期回了趟家,工作上转正了,给老爹买了部手机,给老妈买了些北京特产,其实北京的特产也没太多值得带回去的,每年都会买一大盒稻香村,虽然妈妈总说太沉了就别往家里带了,但也算自己的心意吧。貌似是十月份回来去的恭王府,可能具体的时间想不起来了,不知道为什么记忆力会变得很差哎。

image

十一月份除了双十一剁手之外,还听了JD和阿里云溪大会的讲座,这也是我第一次参加这样的大会,JD基础架构还是干货挺多的,可因为自己水平有限,听懂的很少,作为开拓眼界还是足够的。

image

十二月末还代表技术部门去总部参加了年会表演,第一次去河南,长葛市,参观了总部的工厂和流水线,也让我在业务上进一步的了解了生鲜行业和供应链体系。有句话叫做见多识广,也很荣幸在毕业后就可以有机会更多的了解社会‘生态’。

image

先说过年吧,18年到来了,这也是二月份的事情了,回到家,年味依旧没有小时候那么浓郁,初一亲戚来到家里,也就聊一聊一年各家都怎么样,有要考学的,有要复习的,上班的,生子的,各家过着各家的小日子。‘年’对于我来说除了回家看看,就是见见同学朋友,吃吃饭、聊聊天、冰天雪地浪一浪也就算过完了。

image

回到了工作上,一月份的公司组织调整,两个业态的整合,也预示着单位3月份要有紧张的工作任务了,年前梳理好工作计划,而自己也帮助领到完成我力所能及的工作,构建项目,制定自己的工作计划,能做的或者预支的工作我都会主动承担,不管做多做少,反正收获的都是自己的,不论未来怎样,做好自己的工作就是最好的回报。经历了‘331’的上线,一个月都在加班中付出着,加班100个小时的自己,学会的很多也成长了很多。感受到了‘互联网’模式的开发周期,也深深的体会到了程序猿这个行业的效率。附上一张早晨5点钟的中关村吧。

image

四月份总是想写一下一年的总结,心里可能是想了很多的事情让自己失眠并稍微有些抑郁。再加上偷懒直到今日才了却心中的这件事情,希望自己在今后的路,继续加油,不忘初心。

image

This is ICO Wallet

1
2
BTC:18SopfAEbGqosgUw8oTHSv5xTVYxLXziVy
ETH:0xfaeA5Db637f902451Ce173bf0DC08FDBF86C2dd9

MapStruct实体与模型之间的映射(初体验)

工程中引入mapstruct依赖

1
2
3
4
5
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.2.0.Final</version>
</dependency>

首先对于注解@Mapper(uses = Info.class)这个注解用于表示需要对什么类进行映射。
比如:

1
2
3
4
5
6
7
8
9
10
11
@Mapper(uses = Info.class)
public interface OrderMapper {
OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);
@Mappings({
@Mapping(target = "orderCode", source = "resourceCode",
@Mapping(target = "businessType", constant = "10"),
@Mapping(target = "weight",
expression = "java(MapStructConvertor.convertTonToKg(info.getWeight()))")
)})
Object infoToObject(Info info);
}

1
2
3
4
5
6
7
8
9
其中@Mapper(uses = Info.class)为需要被转换的类
#单例模式获取实例对象
OrderMapper INSTANCE = Mappers.getMapper(OrderMapper.class);
#其源代码:
T mapper = classLoader.loadClass(clazz.getName() + "Impl").newInstance();
@Mappings,@Mapping为映射对应字段。
target为转换字段,source为源字段,constant,定义常量值,expression可以自定义方法转换
1
2
3
4
5
6
7
8
9
public class MapStructConvertor {
public static String convertTonToKg(BigDecimal ton) {
if (ton == null) {
return "0.00";
}
DecimalFormat df = new DecimalFormat("#.00");
return df.format(ton.multiply(new BigDecimal(1000)));
}
}

数据库查询字段后,指定格式化类型

推荐使用工具类:

1
2
3
4
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>

应用场景中的使用:
1.在对时间的处理,查询数据库获取的时间格式为Date,获取到的数据形式为毫秒级别,此时,可以使用注解:

1
2
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

处理时间格式,将其转换为通用时间格式。
2.如存储的数据格式为字符串,但有与之对应的枚举格式,场景如下:

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
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum BusinessTypeEnum {
BUSINESS_ONE("1", "1"),
BUSINESS_TWO("2", "2");
private String code;
private String desc;
BusinessTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}

可以加入上面注解,此注解可以将枚举类以对象的方式进行序列化。
在shape中有如下转换形式,可按需使用。

1
2
3
4
5
6
7
8
9
10
11
12
public static enum Shape {
ANY,
NATURAL,
SCALAR,
ARRAY,
OBJECT,
NUMBER,
NUMBER_FLOAT,
NUMBER_INT,
STRING,
BOOLEAN;
}

2018年3月2日

2018年,元宵节快乐。
这应该是第二年在北京过十五了,最近项目进度紧张,从年后开始,也第一次这么快的转换假期综合症。刚刚给爸妈打电话,给姥姥买的座机铃声放大器也到了,老爸也给安装上了,心想这回姥姥应该会很开心吧。家里的网在临走的时候也给升级到50M了,不知道爸妈有没有感觉到网速变得快了。
紧张的工作之后,晚上看几集赵丽颖的电视剧(陆贞传奇)心里也是美滋滋的,自从看了女儿国电影,突然间就路转粉了赵丽颖。现在也变成了她的小迷弟。
写一写最近的心感,每次回家过完年都感觉自己突然间就成长了好多,对于生活更加珍惜了,还是那么喜欢找自己的回忆,等忙过这一段时间,帝都的天气也暖和了,一定要去过去去过的地方再次追忆下,感受时间的变化。
还有几天也快过生日了,愿一切顺利。

SpringBoot+MyBatis+Mapper+Druid整合

为SpringBoot添加Druid连接池,添加通用Mapper。
首先SpringBoot的Maven配置就不再叙述了。
在pom中引入依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- Ali Druid Pool -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.29</version>
</dependency>
<!-- MyBatis starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!-- tk Mapper starter -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>1.1.4</version>
</dependency>
<!-- pageHelper starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.1</version>
</dependency>

配置文件中加入以下配置:

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
#数据库配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=true
spring.datasource.username=username
spring.datasource.password=password
# 下面为连接池的补充设置,应用到上面所有数据源中# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# MyBatis
mybatis.type-aliases-package=com.dragon.domain
mybatis.mapper-locations=classpath:com/dragon/mapper/*.xml
# PageHelper
pagehelper.offset-as-page-num=true
pagehelper.row-bounds-with-count=true
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql
# MyMapper
mapper.mappers=com.dragon.config.MyMapper
mapper.not-empty=false
mapper.identity=MYSQL

下面是Druid的配置:DruidDataSourceConfig、

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.xescm.owner.model.enums.BusinessTypeEnum;
import com.xescm.resource.enums.AttachmentTypeEnum;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.EnumTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import java.sql.SQLException;
/**
* Druid-DataResource配置类
* @author dragon
*/
@Configuration
@EnableTransactionManagement
@MapperScan(value = "com.dragon.mapper")
public class DruidDataSourceConfig implements EnvironmentAware {
private Environment environment;
private RelaxedPropertyResolver propertyResolver;
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
this.propertyResolver = new RelaxedPropertyResolver(environment,"spring.datasource.");
}
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(propertyResolver.getProperty("driver-class-name"));
druidDataSource.setUrl(propertyResolver.getProperty("url"));
druidDataSource.setUsername(propertyResolver.getProperty("username"));
druidDataSource.setPassword(propertyResolver.getProperty("password"));
druidDataSource.setInitialSize(Integer.parseInt(propertyResolver.getProperty("initialSize")));
druidDataSource.setMinIdle(Integer.parseInt(propertyResolver.getProperty("minIdle")));
druidDataSource.setMaxActive(Integer.parseInt(propertyResolver.getProperty("maxActive")));
druidDataSource.setMaxWait(Integer.parseInt(propertyResolver.getProperty("maxWait")));
druidDataSource.setTimeBetweenEvictionRunsMillis(Long.parseLong(propertyResolver.getProperty("timeBetweenEvictionRunsMillis")));
druidDataSource.setMinEvictableIdleTimeMillis(Long.parseLong(propertyResolver.getProperty("minEvictableIdleTimeMillis")));
druidDataSource.setValidationQuery(propertyResolver.getProperty("validationQuery"));
druidDataSource.setTestWhileIdle(Boolean.parseBoolean(propertyResolver.getProperty("testWhileIdle")));
druidDataSource.setTestOnBorrow(Boolean.parseBoolean(propertyResolver.getProperty("testOnBorrow")));
druidDataSource.setTestOnReturn(Boolean.parseBoolean(propertyResolver.getProperty("testOnReturn")));
druidDataSource.setPoolPreparedStatements(Boolean.parseBoolean(propertyResolver.getProperty("poolPreparedStatements")));
druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(Integer.parseInt(propertyResolver.getProperty("maxPoolPreparedStatementPerConnectionSize")));
druidDataSource.setFilters(propertyResolver.getProperty("filters"));
return druidDataSource;
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
// 加入下面配置可以让MyBatis按照驼峰命名形式映射字段
sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dataSource());
}
@Bean
public ServletRegistrationBean druidServlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", "admin");
reg.addInitParameter("loginPassword", "admin");
return reg;
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
}

Maven打包添加源代码

在Maven pom.xml添加插件,打包jar文件时会自动引入注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<build>
<plugins>
<plugin>
<!--引入source插件 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<!-- 绑定source插件到Maven的生命周期,并在生命周期后执行绑定的source的goal -->
<executions>
<execution>
<!-- 绑定source插件到Maven的生命周期 -->
<phase>compile</phase>
<!--在生命周期后执行绑定的source插件的goals -->
<goals>
<goal>jar-no-fork</goal>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Swagger配置

首先在Maven pom中加入Swagger依赖库

1
2
3
4
5
6
7
8
9
10
11
<!-- 添加swagger支持 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.5.0</version>
</dependency>

SwaggerConfig配置如下即可

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
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/**
* Swagger配置
* @author: dragon
* apiInfo - 自定义Swagger基础信息头
* select - 选择哪些路径和API是需要生成document
* apis API接口包扫描路径
* - 参数 : RequestHandlerSelectors.any() api接口包扫描路径,对所有api进行监控
* - 参数 : RequestHandlerSelectors.basePackage(...) 过滤其他包,仅对选中包进行监控
* - 参数 : RequestHandlerSelectors.withClassAnnotation(...) 通过类注解形式进行过滤监控
* - 参数 : RequestHandlerSelectors.withMethodAnnotation(...) 通过类注解形式进行过滤监控
* paths 可以根据接口url路径设置哪些请求加入文档,忽略哪些请求
* - 参数 : PathSelectors.any() 对所有接口路径进行监控
* - 参数 : PathSelectors.regex("/comm.*") 对接口路径进行过滤监控
*/
@Bean
public Docket reservationApi() {
return new Docket(DocumentationType.SWAGGER_2).
apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.web.restful"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("Swagger Title")
.description("Swaggere Description.").version("1.0").build();
}
}

在类前添加:

1
@Api(value = "Controller", tags = "XX相关接口", description = "XX描述", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)

在方法前添加:

1
@ApiOperation(notes = "返回类型", httpMethod = "POST", value = "XX接口")

在传参前添加:
注解ModelAttribute可带出DTO中Swagger API注释

1
@ApiParam(name = "xxDTO",value = "XX请求参数") @ModelAttribute xxDTO

Java Builder设计模式

适用场景:复杂对象的组装和创建
建造者模式,是为了将构建复杂对象的过程和它的部件解耦。
它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们。
使用者可以在不知道内部的具体构建细节。

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
public class Address {
private final String province;
private final String city;
private final String area;
private final String street;
public static class Builder {
private String province;
private String city;
private String area;
private String street;
public Builder() {
}
public Builder province(String province) {
this.province = province;
return this;
}
public Builder city(String city) {
this.city = city;
return this;
}
public Builder area(String area) {
this.area = area;
return this;
}
public Builder street(String street) {
this.street = street;
return this;
}
public Address build() {
return new Address(this);
}
}
private Address(Builder builder) {
province = builder.province;
city = builder.city;
area = builder.area;
street = builder.street;
}
}