为什么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
}
}
阅读更多

Spring的本质-Servlet初探

现在Java的web开发体系中,Spring以其轻量级,低耦合而占据了老大的地位,但是Spring的本质是什么,为什么在Spring里面不需要像以前写Servlet项目一样,需要配置web.xml。这些都需要我们去刨根问底的。

Servlet是什么

按照Servlet规范所解释的那样,Servlet是一个Web组件,就是类似于生物里面的病毒宿主一样,病毒还是那个病毒,但是离开了宿主之后就不能单独生存了。而宿主就是一个Servlet容器。(tomcat就是一个Servlet容器)

Servlet 是基于 Java 技术的 web 组件,容器托管的,用于生成动态内容。像其他基于 Java 的组件技术一样,
Servlet 也是基于平台无关的 Java 类格式,被编译为平台无关的字节码,可以被基于 Java 技术的 web server
动态加载并运行。容器,有时候也叫做 servlet 引擎,是 web server 为支持 servlet 功能扩展的部分。客户端
通过 Servlet 容器实现的请求/应答模型与 Servlet 交互

在Tomcat的源码包里面,Servlet其实是一个接口,如下所示:

阅读更多

将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>
阅读更多

利用Jquery和Spring异步上传文件

异步上传文件:

用Jquery的异步上传文件的时候需要引入一个js文件jquery.form.min.js,用这个文件里面的$.ajaxSubmit()方法来实现一个异步的文件上传功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$(this).ajaxSubmit({
type:'POST',
url: "/uploadfile",
dataType: 'json',
data: serializeData,
contentType: false,
cache: false,
processData:false,
beforeSubmit: function() {
},
uploadProgress: function (event, position, total, percentComplete){
},
success:function(){

},
error:function(data){
alert('上传图片出错');
}
});

在这里的话$("")函数需要是form的id,而且beforeSubmit可以在上传文件之前可以做一些检查,例如文件后缀或者文件大小之类的检查。

在后端的话接受上传的文件其实跟Servlet差不多,主要是从request中获取请求流,参数的话需要标记为这个 @RequestParam("file") MultipartFile file,最后在SpringMvc中有一个方法可以将上传的文件通过移动或者复制然后转移到我们指定的文件夹中:

阅读更多

spring连接redis的一些做法以及mybatis的一些感想

Redis启动

首先开启redis服务,windows的redis下载在github windows的redis下载地址,然后解压出来最后开启那个redis-server。
启动之后显示如下图:

spring配置:

配置

在Spring中也可以通过配置文件redis.properties配置,但是由于在这个项目中的配置文件已经太多了,所以选择使用类的方式进行配置:

阅读更多

关于Unicode和其他的字符集以及Spring乱码处理

Spring出现乱码的解决办法:

若需要快速的解决乱码问题可以直接看配置文件:

后台逻辑:

在项目中的web.xml中添加Spring的字符过滤器,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<filter>
<filter-name>SpringEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpringEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
阅读更多