从业务的角度来说说Spring为什么不推荐字段注入

在我们的日常开发中,会发现一些代码中充斥着 @Autowired,这种做法在我们的业务代码中非常的常见,但是如果仔细观察,你就会发现 IntelliJ 会出现一个 warning

image-20221022151757174

不推荐使用字段进行注入???我用了这么长时间的注入方式,竟然被 Spring 不推荐了?

然后我们的其他同学就放弃了 @Autowired 注解,转而使用 @Resource 注解,虽然说 IDEA 不再告警,但是从业务的角度上来说,还是不推荐这样处理。

看到这里,顺带提一句,Spring 有哪几种构造方式?这个是经典八股了,如果没有背熟的,各打一个大板,答案是:

阅读更多

RestTemplate 处理转义的相关细节

在业务开发中,常见的 Http 请求开源框架有如下几个:

  1. JDK 自带的 Http 请求库
  2. Apache 提供的 HttpCLient
  3. OkHttp
  4. 由 JDK 封装而来的 RestTempalte

其中因为我们是用的 Spring 框架,所以自然而言的优先选择 RestTemlate,优点非常的多,很多配置都可以做到低耦合,并且可以将请求和相应做单独的处理。

但是在使用的过程中,遇到了转义的坑了,在此记录下

场景复现

阅读更多

jackson中的范型

在 jackson 将字符串转为对象的时候,如果是不带有范型的数据类,那么在 strig 转 obj 的时候不会出现什么问题,如果你的对象带有范型的话,那么就需要注意了,稍不注意就会抛出如下异常

java.util.LinkedHashMap cannot be cast to XX

出现该原因的原因在于 jackson 转换对象的时候,如果没有识别到原始类型,会默认将其转为 LinkedHashMap,后续一旦使用该类,就会抛出上述错误
demo如下:

如果你是使用如下方法进行 string 转 object,那么范型会被映射称为 LinkedHashMap

1
public <T> T readValue(String content, Class<T> valueType)
阅读更多

java validation 的国际化

背景

在一个完整的项目里面,肯定是有各种各样的入参校验的,如果业务上的一些逻辑校验,可以放在 Service 层面进行,但是如果是 Controller 里面的校验,直接可以用 validation 进行验证。配合注解可以很方便的实现各种各样的入参校验。
如下:

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
public class User {

@NotBlank(message = "用户名称不能为空")
private String name;

@Max(value = 18)
@Min(value = 10)
private Integer age;

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;
}
}

然后在Controller里面

1
2
3
4
5
6
7
8
9
@PostMapping(value = "test")
public String testError(@Valid @RequestBody User user ,Errors errors){
if(errors.hasErrors()){
for (ObjectError err : errors.getAllErrors()) {
return err.getDefaultMessage();
}
}
return "OK";
}
阅读更多

为什么Spring官方推荐通过构造器注入

我们在使用Spring的时候,最方便的就是它的IOC(控制反转),也就是所有的Bean都交由

Spring进行管理,那么我们在看网上的文章或者自己在写代码的时候经常会像这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class TestService {

@Autowired
TestDao TestDao;

public void getInfo(){
}
}

@Service
public class TestService {

private final TestDao TestDao;

@Autowired
public TestService(TestDao TestDao){
this.TestDao = TestDao;
}

public void getInfo(){
}

}

但是貌似许多人都会使用第一种方式,因为这样简单方便,如果是采用第二种的话,每一次新增加一个bean,都需要在构造器的入参上面加一个参数,就会显得有点麻烦。

阅读更多

在Spring中全局处理异常

随着业务的发展,在每一个业务模块里面都可能会出现一些自定义的通用异常,例如·验证失败权限不足等等 ,这些自定义的异常很可能会被所有模块公用,在代码里面,最常见的一种写法是在每一个方法里面进行捕获,然后在Controller里面进行catch,最后进行相应处理

常见写法

第一种写法:

这是一个常规的写法,每一个方法都处理自己的特定异常
Controller层

1
2
3
4
5
6
7
8
9
10
11
public void login(){
try{
//逻辑处理
}catch(AuthException e){
XXX
}catch(CrsfException e){
XXX
}catch(Exception e){
XXX
}
}
阅读更多

将Jackson替换成Fastjosn

记得有一次的面试是。如何在Spring中将JackSon 替换为 FastJson,emmmm…当时的回答是只需要替换 pom.xml,然后在使用的时候引入FastJosn就行了,但是在当时显然没有理解到面试官的意图,既然面试官强调的是如何替换,那么修改pom.xml很显然不是面试官所想要的答案,那还有什么答案呢?

有一个方法可能是面试官想要的,那就是重写Spring的HttpMesageConverter方法,在这个方法里面引入FastJson的配置,然后替换掉Spring默认的Jackson

替换方式有几种,一种是返回一个HttpMesageConverter,另一种是继承WebMvcConfigurerAdapter 来实现 configureMessageConverters

代码如下:

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
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
// 输出空置字段
SerializerFeature.WriteMapNullValue,
// list字段如果为null,输出为[],而不是null
SerializerFeature.WriteNullListAsEmpty,
// 数值字段如果为null,输出为0,而不是null
SerializerFeature.WriteNullNumberAsZero,
// Boolean字段如果为null,输出为false,而不是null
SerializerFeature.WriteNullBooleanAsFalse,
// 字符类型字段如果为null,输出为"",而不是null
SerializerFeature.WriteNullStringAsEmpty);

fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastJsonConfig.setDateFormat("yyyy-MM-dd hh:mm:ss");
FastJsonHttpMessageConverter Converter = new FastJsonHttpMessageConverter();
Converter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = Converter;
converters.add(0,converter);
super.configureMessageConverters(converters);
}
阅读更多

spring中使用MappingJackson2HttpMessageConverter遇到的一个坑

今天遇到的一个问题就是在Spring中如果继承 WebMvcConfigurerAdapter 然后实现 configureMessageConverters 方法来实现一个 Json 的转换的时候,此时会出现一个情况就是:
如果在Controller里面的那个参数是String的话就会一直提示一个错误 org.springframework.http.converter.HttpMessageNotReadableException","message":"JSON parse error: Can not deserialize instance of java.lang.String out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token\n at [Source: java.io.PushbackInputStream@35b22c23; line: 1, column: 1]","path":"/tetsjson"}
刚开始一直找不出原因,于是在 Spring 源码中 DEBUG :
在如下两处打断点发现是可以获取请求体的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {

for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
if (advice.supports(parameter, targetType, converterType)) {
request = advice.beforeBodyRead(request, parameter, targetType, converterType);
}
}
return request;
}


@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
if (advice.supports(parameter, targetType, converterType)) {
body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
}
return body; // 可以正常解析出请求的body
}

但是一旦加上请求头Content-Type : application/json.在方法进入 beforeBodyRead 里面的 for循环 之后便跳转到了 org.springframework.web.servlet.mvc.method.annotationgetMatchingAdvice() 方法,然后继续走下去发现有一处提示如下:

1
2
3
4
5
6
7
8
9
@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {

// ConverType :"class org.springframework.http.converter.json.MappingJackson2HttpMessgaeConverter"
return (AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType) &&
methodParameter.getParameterAnnotation(JsonView.class) != null);
// methodParamter: "method 'XXX' 'Controller方法' paramter 0"
}

阅读更多

springcloud使用feign导致的Excepted authority 错误(三)

今天在用Feign的时候遇到了一个BUG,这个BUG虽然不是很难,但是由于网上没什么解决办法,而是自己的DEBUG解决的,所以暂且记录下:

异常就是java.net.URISyntaxException: Expected authority at index 7: http://,刚开始的时候一头雾水,在Google了一遍之后并未找出解决办法,后来又尝试了在代码中进行了DEBUG,发现代码嵌套的太深了,所以没办法走下去,之后便去StackOverflow中查看了下也没有什么头绪
在这里也检查过了是不是服务没注册还是Feign的注解微服务名称是不是有问题,都显示是正常的,并且可以通过这个名称获取其对应的地址

后来今早起来的时候再次逛StackOverflow看到有人提示try to debug into the LoadBalancerFeignClient.cleanUrl() 。查看了下这个方法的代码:

1
2
3
static URI cleanUrl(String originalUrl, String host) {
return URI.create(originalUrl.replaceFirst(host, ""));
}

猜测了下应该是负责创建Feign相关URL的一个类,所以尝试在这里DEBUG,然后再开启一个正常的可以使用Feign的服务,最后发现这两个服务的区别是,在这个出错的微服务里面发出的请求是http://xxx,而正常的微服务是http://xxx/user/name/XX,所以问题马上就定位出来了,就是请求的问题,

阅读更多

在Spring中使用JWT生成token来验证用户

首先JWT全程是 JSON WEB TOKEN

与Spring进行一个整合:

获取JWT

首先需要在pom中引入几个需要的jar包:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
阅读更多