当前位置 : 首页 » 文章分类 :  开发  »  Spring Boot 学习笔记

Spring Boot 学习笔记

Spring Boot 学习笔记


spring boot 2.x 中jedis改为lettuce

随着Spring Boot2.x的到来,支持的组件越来越丰富,也越来越成熟,其中对Redis的支持不仅仅是丰富了它的API,更是替换掉底层Jedis的依赖,取而代之换成了Lettuce(生菜)

Lettuce
Lettuce 和 Jedis 的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全,除非使用连接池,为每个Jedis实例增加物理连接。Lettuce基于Netty的连接实例(StatefulRedisConnection),可以在多个线程间并发访问,且线程安全,满足多线程环境下的并发访问,同时它是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。

一起来学SpringBoot | 第九篇:整合Lettuce Redis
https://blog.battcn.com/2018/05/11/springboot/v2-nosql-redis/

RedisTemplate中的executePipelined

// 创建结果集
Map<Long, Long> result = Maps.newLinkedHashMap();

// 加入从redis中没取到
// values为空时从数据库查
userIds.forEach(u -> result.put(u, getAccountIdByUserId(u)));

// 查完批量放入redis(其实直接用mset一条命令即可,不用管道)
executorService.execute(() -> {
    try {
        // 批量放入redis
        redisClient.executePipelined(redisConnection -> {
            RedisSerializer<String> serializer = new StringRedisSerializer();
            result.forEach((userId, accountId) -> {
                if (accountId != null) {
                    String key = Joiner.on("-").join(USER_ID_PRE, userId);
                    redisConnection.set(serializer.serialize(key), serializer.serialize(String.valueOf(accountId)));
                }
            });
            return null;
        });
    } catch (RedisConnectionFailureException e) {
        logger.error("[Redis] Exception catched when set mapping of user id and account id", e);
    }
});

redis pipeline简介
https://www.jianshu.com/p/a8e33e058518


NamedThreadLocal

org.springframework.core.NamedThreadLocal<T>
Spring提供的一个命名的ThreadLocal实现。

package org.springframework.core;

import org.springframework.util.Assert;

public class NamedThreadLocal<T> extends ThreadLocal<T> {
    private final String name;

    public NamedThreadLocal(String name) {
        Assert.hasText(name, "Name must not be empty");
        this.name = name;
    }

    public String toString() {
        return this.name;
    }
}

记录请求处理时间

实现分析:
1、在进入处理器之前记录开始时间,即在拦截器的preHandle记录开始时间;
2、在结束请求处理之后记录结束时间,即在拦截器的afterCompletion记录结束实现,并用结束时间-开始时间得到这次请求的处理时间。

问题:
我们的拦截器是单例,因此不管用户请求多少次都只有一个拦截器实现,即线程不安全,那我们应该怎么记录时间呢?
解决方案是使用ThreadLocal,它是线程绑定的变量,提供线程局部变量(一个线程一个ThreadLocal,A线程的ThreadLocal只能看到A线程的ThreadLocal,不能看到B线程的ThreadLocal)

public class StopWatchHandlerInterceptor extends HandlerInterceptorAdapter {
    private NamedThreadLocal<Long>  startTimeThreadLocal = new NamedThreadLocal<Long>("StopWatch-StartTime");

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long beginTime = System.currentTimeMillis();//1、开始时间
        startTimeThreadLocal.set(beginTime);//线程绑定变量(该数据只有当前请求的线程可见)
        return true;//继续流程
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        long endTime = System.currentTimeMillis();//2、结束时间
        long beginTime = startTimeThreadLocal.get();//得到线程绑定的局部变量(开始时间)
        long consumeTime = endTime - beginTime;//3、消耗的时间
        if(consumeTime > 500) {//此处认为处理时间超过500毫秒的请求为慢请求
            //TODO 记录到日志文件
            System.out.println(String.format("%s consume %d millis", request.getRequestURI(), consumeTime));
        }
    }
}

Spring MVC学习(五)——-处理器拦截器详解
http://blog.51cto.com/3001448/1205771


@Value注解默认值

变量名后加冒号加默认值,语法为:

@Value("${some.key:my default value}")
private String stringWithDefaultValue;

例如:

// int默认值
@Value("${datasource.maximum-pool-size:100}")
private int mysqlMaximumPoolSize;

// string默认值
@Value("${some.key:my default value}")
private String stringWithDefaultValue;

// 默认空字符串
@Value("${some.key:}")
private String stringWithBlankDefaultValue;

// boolean默认值
@Value("${some.key:true}")
private boolean booleanWithDefaultValue;

数组默认值,可自动解析逗号分隔字符串为Array数组

@Value("${some.key:one,two,three}")
private String[] stringArrayWithDefaults;

@Value("${some.key:1,2,3}")
private int[] intArrayWithDefaults;

Using Spring @Value with Defaults
https://www.baeldung.com/spring-value-defaults


SpringBoot国际化

spring boot默认就支持国际化的,而且不需要你过多的做什么配置,只需要在resources/下定义国际化配置文件即可,注意名称必须以messages开头。

定义如下几个文件:
messages.properties (默认,当找不到语言的配置的时候,使用该文件进行展示)。
messages_zh_CN.properties(中文)
messages_en_US.properties(英文)

不同语言环境下看到不同内容如何实现的?

为了让web应用程序支持国际化,必须识别每个用户的首选区域,并根据这个区域显示内容。在Spring MVC应用程序中,用户的区域是通过区域解析器来识别的,它必须是实现LocaleResolver接口。Spring MVC提供了几个LocaleResolver实现,让你可以按照不同的条件来解析区域。除此之外,你还可以实现这个接口创建自己的区域解析器。如果没有做特殊的处理的话,Spring 采用的默认区域解析器是AcceptHeaderLocaleResolver。它通过检验HTTP请求的头部信息accept-language来解析区域。这个头部是由用户的wb浏览器底层根据底层操作系统的区域设置进行设定的。请注意,这个区域解析器无法改变用户的区域,因为它无法修改用户操作系统的区域设置。

58 Spring Boot国际化(i18n)【从零开始学Spring Boot】
http://412887952-qq-com.iteye.com/blog/2312274


@RequestMapping默认method

缺省 method 的值

@RequestMapping("/enter")
public String enter(){
    return "example_enter_page";
}

method 若是缺省没指定,并不是说它默认只处理 GET 方式的请求,而是它可以处理任何方式的 http method 类型的请求。
指定 method 是为了细化映射 ( 缩小处理方法的映射范围 ),在 method 没有指定的情况下,它的映射范围是最大的。

@RequestParam和@PathVariable

@RequestParam 和 @PathVariable 注解是用于从request中接收请求的,两个都可以接收参数,关键点不同的是@RequestParam 是从request里面拿取值,而 @PathVariable 是从一个URI模板里面来填充
http://localhost:8080/springmvc/hello/101?param1=10&param2=20
根据上面的这个URL,你可以用这样的方式来进行获取

public String getDetails(
    @RequestParam(value="param1", required=true) String param1,
        @RequestParam(value="param2", required=false) String param2){
...
}

@RequestParam默认值

public UdsHttpResponse queryLeadsFollowDetailsAPPV1(@PathVariable("userId") Long userId,
        @RequestParam(name = "offset", required = false, defaultValue = "0") Long offset,
        @RequestParam(name = "count", required = false, defaultValue = "10") Integer count) {
  ... ...
}

@RequestParam 支持下面四种参数
defaultValue 如果本次请求没有携带这个参数,或者参数为空,那么就会启用默认值
name 绑定本次参数的名称,要跟URL上面的一样
required 这个参数是不是必须的
value 跟name一样的作用,是name属性的一个别名

@PathVariable
这个注解能够识别URL里面的一个模板,我们看下面的一个URL
http://localhost:8080/springmvc/hello/101?param1=10&param2=20
上面的一个url你可以这样写:

@RequestMapping("/hello/{id}")
    public String getDetails(@PathVariable(value="id") String id,
    @RequestParam(value="param1", required=true) String param1,
    @RequestParam(value="param2", required=false) String param2){
.......
}

@RequestParam,@PathParam,@PathVariable等注解区别
https://blog.csdn.net/u011410529/article/details/66974974


@Around比@ExceptionHandler先拦截到异常

@ControllerAdvice加@ExceptionHandler进行异常统一处理

@ControllerAdvice,是spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强。
该注解使用@Component注解,这样的话当我们使用context:component-scan扫描时也能扫描到

即把@ControllerAdvice注解内部使用@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法。非常简单,不过只有当使用@ExceptionHandler最有用,另外两个用处不大。

@ExceptionHandler指明要捕获的异常
@ExceptionHandler注解的方法就当做一个处理异常的Controller,可以返回自定义的错误response

/**
 * 处理UdsException
 * @param e
 * @param httpServletRequest
 * @return
 */
@ResponseBody
@ExceptionHandler({UdsRuntimeException.class})
@ResponseStatus(HttpStatus.OK)
public UdsHttpResponse handleServerError(UdsRuntimeException e, HttpServletRequest httpServletRequest) {
    logger.warn("[Handled] Uds exception", e);
    falconClient.avgByTime("uds_exception_count", 1, 30);
    return constructResponse(UdsHttpResponse.createFailedResponse(e.getErrorCode(), e.getMessage(), e.getData()),
            httpServletRequest);
}

Spring3.2新注解@ControllerAdvice
http://jinnianshilongnian.iteye.com/blog/1866350

Spring MVC中@ControllerAdvice注解实现全局异常拦截
https://www.cnblogs.com/EasonJim/p/7887646.html

Spring MVC 中 HandlerInterceptorAdapter的使用

一般情况下,对来自浏览器的请求的拦截,是利用Filter实现的,这种方式可以实现Bean预处理、后处理。
Spring MVC的拦截器不仅可实现Filter的所有功能,还可以更精确的控制拦截精度。
Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此类,可以非常方便的实现自己的拦截器。他有三个方法:
分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)
在preHandle中,可以进行编码、安全控制等处理;
在postHandle中,有机会修改ModelAndView;
在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。

Spring MVC 中 HandlerInterceptorAdapter的使用
https://blog.csdn.net/liuwenbo0920/article/details/7283757


IDEA spring boot打包

View -> Tool Windows -> Maven Projects 调出Maven Projects窗口,打开“项目名”下的Lifecycle,双击package,开始自动打包

跳过测试

再Maven Projects边栏中,点击闪电图标 Toggle ‘Skip Tests’ Mode,选中后即打开跳过测试模式。
或者
mvn clean package -DskipTests
或mvn clean package -Dmaven.test.skip=true
或者在pom中配置:

<plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <skip>true</skip>
        </configuration>
      </plugin>
</plugins>

spring boot参数配置

实际上,Spring Boot应用程序有多种设置途径,Spring Boot能从多重属性源获得属性,包括如下几种:
根目录下的开发工具全局设置属性(当开发工具激活时为~/.spring-boot-devtools.properties)。
测试中的@TestPropertySource注解。
测试中的@SpringBootTest#properties注解特性。
命令行参数
SPRING_APPLICATION_JSON中的属性(环境变量或系统属性中的内联JSON嵌入)。
ServletConfig初始化参数。
ServletContext初始化参数。
java:comp/env里的JNDI属性
JVM系统属性
操作系统环境变量
随机生成的带random.* 前缀的属性(在设置其他属性时,可以应用他们,比如${random.long})
应用程序以外的application.properties或者appliaction.yml文件
打包在应用程序内的application.properties或者appliaction.yml文件
通过@PropertySource标注的属性源
默认属性(通过SpringApplication.setDefaultProperties指定).

这里列表按组优先级排序,也就是说,任何在高优先级属性源里设置的属性都会覆盖低优先级的相同属性,列如我们上面提到的命令行属性就覆盖了application.properties的属性。


创建Spring boot项目

通过IntelliJ IDEA使用(个人推荐)
IntelliJ IDEA是非常流行的IDE,IntelliJ IDEA 14.1已经支持Spring Boot了。
创建Spring Boot操作步骤如下:
1.在File菜单里面选择 New > Project,然后选择Spring Initializr


Spring Boot父级依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

这块配置就是Spring Boot父级依赖,有了这个,当前的项目就是Spring Boot项目了,spring-boot-starter-parent是一个特殊的starter,它用来提供相关的Maven默认依赖,使用它之后,常用的包依赖可以省去version标签。关于Spring Boot提供了哪些jar包的依赖,可查看C:\Users\用户.m2\repository\org\springframework\boot\spring-boot-dependencies\1.5.1.RELEASE\spring-boot-dependencies-1.5.1.RELEASE.pom

如果你不想使用某个依赖默认的版本,您还可以通过覆盖自己的项目中的属性来覆盖各个依赖项,例如,要升级到另一个Spring Data版本系列,您可以将以下内容添加到pom.xml中。


Spring Boot Maven插件

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

上面的配置就是Spring Boot Maven插件,Spring Boot Maven插件提供了许多方便的功能:
1、把项目打包成一个可执行的超级JAR,包括把应用程序的所有依赖打入JAR文件内,并为JAR添加一个描述文件,其中的内容能让你用java -jar来运行应用程序。
2、搜索public static void main()方法来标记为可运行类。


spring boot自定义属性乱码问题

#设置spring-boot 编码格式
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
spring.messages.encoding=UTF-8

设置 File Encodings的Transparent native-to-ascii conversion为true,具体步骤如下:
依次点击
File -> Settings -> Editor -> File Encodings
将Properties Files (*.properties)下的Default encoding for properties files设置为UTF-8,将Transparent native-to-ascii conversion前的勾选上。

Spring Boot 自定义属性 以及 乱码问题
https://blog.csdn.net/m0_37995707/article/details/77506184

Spring Boot干货系列:(一)优雅的入门篇
http://tengj.top/2017/02/26/springboot1/

Spring Boot干货系列总纲
http://tengj.top/2017/04/24/springboot0/


上一篇 DataGrip使用笔记

下一篇 Phabricator使用笔记

阅读
3,104
阅读预计13分钟
创建日期 2018-06-11
修改日期 2018-09-19
类别
百度推荐