需要得到Spring相关信息

Aware接口

XXXAware在Spring中表示对XXX可以感知,通俗的说就是 : 如果某个类中想要使用Spring的一些东西,
只要实现XXXAware接口即可.

比如 : userController中想使用ApplicationContext,那么只需要实现ApplicationContextAware接口即可.

  • EnvironmentAware
    该接口具有Environment(环境)的能力,封装了ServletContext,ServletConfig,JndiProperty,系统环境变量和系统属性.这些都封装到了propertySources属性下.

ServletConfigPropertySource : : 存放ServletConfig
ServletContextPropertySource : 存放ServletContext
JndiPropertySource : 存的是Jndi
MapPropertySource : 所有虚拟机的属性
SystemEnvironmentPropertySource : 系统变量

1
2
3
4
5
6
7
final StandardServletEnvironment standardServletEnvironment = ( StandardServletEnvironment ) environment;
final MutablePropertySources propertySources = standardServletEnvironment.getPropertySources();
final PropertySource< ? > systemProperties = propertySources.get( "systemProperties" );
final Object source = systemProperties.getSource();
final Properties properties = ( Properties ) source;
System.err.println( "java.vendor = " + properties.getProperty( "java.vendor" ) ); // 这样就可以得到环境的所有信息了
System.err.println( "XXX = " + properties.getProperty( "XXXX" ) );

  • BeanFactoryAware
    首先继承这个接口是为了得到BeanFactory的引用,进而得到想要的Bean。但是这样做是违反DI注入原则的,所以Spring是不建议这样做的。所以你会发现,想继承这个接口必须在applicationContext.xml里实例Bean。而且我怀疑,这个BeanFactroyAware只是一个标识接口(自己理解的叫法),和BeanNameAware接口一样,只是在实例化的时候,判断是否继承这些接口了,进而通过反射固定的方法,然后给对应的调用方法。

  • ApplicationContextAware
    Spring将调用setApplicationContext (),将bean所在的上下文的引用传入进来

  • xxxAware
    … …
    更多可看源码,想要什么在源码中看注释选择即可.

初始化信息?

  • BeanPostProcessor

  • InitializingBean

实现该接口的方法,初始化时会被调用

  • BeanPostProcessor

同上

  • DisposableBean

与上面相反,上下文被销毁后会调用实现该接口的方法.

当然还有注解形式的

  • @PostConstruct @PreDestroy
1
2
3
4
5
6
7
8
9
10
11
// 初始化
@PostConstruct
public void init () {
System.err.println("init");
}

// 销毁
@PreDestroy
public void destroy () {
System.err.println("destroy");
}

初始化还不满足需求?我想在Spring上下文全部加载完后做点什么

虽然对Spring很了解,但是Spring组件加载顺序不可能都能记清楚,我就想等Spring组件全部加载完后,然后获取Spring相关组件,如果在初始化中获取,不清楚加载顺序,可能不一定能获取到

  • ApplicationListener

初始化之后事件

  • 我想获取应用的所有RequestMapping信息,我尝试过在init中获取,因为加载顺序问题,获取为空,那么我在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
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
/**
* @author : 披荆斩棘
* @date : 2017/6/22
*/
@Configuration
@ConditionalOnExpression( "${aidijing.basic-bean.enabled:false}" )
public class BasicBeanConfig implements ApplicationContextAware, ApplicationListener< ContextRefreshedEvent > {


private ApplicationContext applicationContext;
/**
* 所有 {@link org.springframework.web.bind.annotation.RequestMapping#value()} 值 <br/>
* 比如 : [[/authentication], [/authentication], [/api/user/{id}], [/api/user/{id}]]
*/
private List< Set< String > > requestMappingUris = new CopyOnWriteArrayList<>();
/**
* {@link org.springframework.web.bind.annotation.RequestMapping} 原始信息
*/
private Map< RequestMappingInfo, HandlerMethod > handlerMethods;
/**
* [{[/authentication]=[POST]}, {[/authentication]=[PUT]}, {[/captcha/login]=[GET]}, {[/captcha]=[GET]}, {[/api/user/{id}]=[GET]}, {[/api/user/{id}]=[PUT]}, {[/api/user/{id}]=[DELETE]}, {[/api/user]=[POST]}, {[/api/user/test]=[GET]}, {[/api/user]=[GET]}, {[/api/user/distributed-lock]=[GET]}, {[/api/user/async]=[GET]}, {[/api/user/list]=[GET]}, {[/v2/api-docs]=[GET]}, {[/swagger-resources/configuration/security]=[]}, {[/swagger-resources/configuration/ui]=[]}, {[/swagger-resources]=[]}, {[/error]=[]}, {[/error]=[]}]
*/
private List< Map< Set< String >, Set< RequestMethod > > > requestMappingInfos = new ArrayList<>();
/** 所有请求方法 **/
private Set< RequestMethod > requestMethodsRequestConditionAll = new LinkedHashSet<>(
Arrays.asList(
RequestMethod.GET,
RequestMethod.HEAD,
RequestMethod.POST,
RequestMethod.PUT,
RequestMethod.PATCH,
RequestMethod.DELETE,
RequestMethod.OPTIONS,
RequestMethod.TRACE
)
);

@Bean
public List< Set< String > > requestMappingUris () {
return requestMappingUris;
}

@Bean
public Map< RequestMappingInfo, HandlerMethod > handlerMethods () {
return handlerMethods;
}

@Bean
public List< Map< Set< String >, Set< RequestMethod > > > requestMappingInfos () {
return requestMappingInfos;
}

@Override
public void setApplicationContext ( ApplicationContext applicationContext ) throws BeansException {
this.applicationContext = applicationContext;
}

// Spring上下文加载完后,会执行该方法
@Override
public void onApplicationEvent ( ContextRefreshedEvent event ) {
final RequestMappingHandlerMapping requestMappingHandlerMapping =
applicationContext.getBean( RequestMappingHandlerMapping.class );
final Map< RequestMappingInfo, HandlerMethod > handlerMethods =
requestMappingHandlerMapping.getHandlerMethods();

this.handlerMethods = handlerMethods;

handlerMethods.keySet().forEach( mappingInfo -> {
Map< Set< String >, Set< RequestMethod > > mapping = new HashMap();
mapping.put(
mappingInfo.getPatternsCondition().getPatterns(),
this.getMethods( mappingInfo.getMethodsCondition().getMethods() )
);
requestMappingInfos.add( mapping );
} );

requestMappingUris.addAll(
handlerMethods.keySet()
.parallelStream()
.map( mappingInfo -> mappingInfo.getPatternsCondition().getPatterns() )
.collect( Collectors.toList() )
);

}


/**
* 因为如果支持所有方式请求的话默认是[],但是为了显示效果这里进行补全
*
* @param methods
* @return RequestMethod
*/
private Set< RequestMethod > getMethods ( Set< RequestMethod > methods ) {
if ( ! CollectionUtils.isEmpty( methods ) ) {
return methods;
}
return requestMethodsRequestConditionAll;
}
}

这样我就得到了所有RequestMapping信息

请求处理

我想在接收请求和响应请求时,对某些参数做些处理

  • WebMvcConfigurerAdapter
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

@Configuration
public class SpringConfig extends WebMvcConfigurerAdapter {

// 解析请求参数
@Override
public void addArgumentResolvers ( List< HandlerMethodArgumentResolver > argumentResolvers ) {
// 如果 Controller 方法中的参数有 PageRowBounds 则从前台获取数据组装, 如果没有传递则给设置一个默认值
argumentResolvers.add( new HandlerMethodArgumentResolver() {
@Override
public boolean supportsParameter ( MethodParameter parameter ) {
return PageRowBounds.class.isAssignableFrom( parameter.getParameterType() );
}

@Override
public Object resolveArgument ( MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest request, WebDataBinderFactory factory ) throws Exception {
int pageNum = NumberUtils.toInt( request.getParameter( GlobalConstant.PAGE_NUM_PARAM_NAME ) );
if ( pageNum <= 0 ) {
pageNum = GlobalConstant.DEFAULT_PAGE_NUM;
}
int pageSize = NumberUtils.toInt( request.getParameter( GlobalConstant.PAGE_SIZE_PARAM_NAME ) );
if ( pageSize <= 0 ) {
pageSize = GlobalConstant.DEFAULT_PAGE_SIZE;
}
return new PageRowBounds( pageNum, pageSize );
}
} );
}

}
1
2
3
4
5
6

// 这样在控制器中,就是前端不传值,我也有默认值
GetMapping
public ResponseEntity< PageInfo< UserVO > > listPage ( PageRowBounds pageRowBounds ) {
return ResponseEntity.ok().setResponseContent( userService.listPage( pageRowBounds) );
}

响应请求时如何处理配置

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

@Configuration
public class SpringConfig extends WebMvcConfigurerAdapter {


/**
* Add handlers to support custom controller method return value types.
* <p>Using this option does not override the built-in support for handling
* return values. To customize the built-in support for handling return
* values, configure RequestMappingHandlerAdapter directly.
* @param returnValueHandlers initially an empty list
*/
// 响应请求时进行处理
@Override
public void addReturnValueHandlers ( List< HandlerMethodReturnValueHandler > returnValueHandlers ) {
returnValueHandlers.add( new HandlerMethodReturnValueHandler() {
@Override
public boolean supportsReturnType ( MethodParameter returnType ) {
return false;
}

@Override
public void handleReturnValue ( Object returnValue,
MethodParameter returnType,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest ) throws Exception {

}
} );
}

}

响应请求处理方式,我喜欢用 ResponseBodyAdvice 接口处理

  • ResponseBodyAdvice
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

/**
* api {@link ResponseEntity} 返回类型处理,针对 {@link com.aidijing.domain.RolePermissionResource#resourceApiUriShowFields} 字段
*
* @author : 披荆斩棘
* @date : 2017/6/29
*/
@RestControllerAdvice
public class GlobalResponseController implements ResponseBodyAdvice< ResponseEntity > {

@Override
public boolean supports ( MethodParameter returnType, Class converterType ) {
return ResponseEntity.class.isAssignableFrom( returnType.getParameterType() );
}

// 响应之前对 ResponseEntity 进行后处理,因为我的系统中,所有返回请求都是使用 ResponseEntity 进行封装返回
// 比如我可以对响应实体中的内容进行过滤,对某些字段进行加密,或者不显示某些字段,这样就不用再具体代码中进行编码了,减少了代码量和耦合
@Override
public ResponseEntity beforeBodyWrite ( ResponseEntity body,
MethodParameter returnType,
MediaType selectedContentType,
Class selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response ) {
// 如果自行设置了,那么就以自行设置的为主
if ( body.isFieldsFilter() ) {
return body.filterFieldsFlush();
}
// 如果当前用户的这个接口权限本来就可以查看全部
if ( ResponseEntity.WILDCARD_ALL.equals( ContextUtils.getCurrentRequestApiShowFields() ) ) {
return body;
}
return body.setFilterFieldsAndFlush( ContextUtils.getCurrentRequestApiShowFields() );
}
}

异常处理

  • 这里我只喜欢使用 @RestControllerAdvice + @ExceptionHandler 进行处理
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* @author : 披荆斩棘
* @date : 2017/5/18
*/
@RestControllerAdvice
public class GlobalErrorController {

@ExceptionHandler( UsernameNotFoundException.class )
public ResponseEntity serviceErrorHandler ( UsernameNotFoundException e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.unauthorized();
}

@ExceptionHandler( AuthenticationException.class )
public ResponseEntity serviceErrorHandler ( AuthenticationException e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.unauthorized();
}

@ExceptionHandler( ForbiddenException.class )
public ResponseEntity forbiddenErrorHandler ( ForbiddenException e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.forbidden( e.getMessage() );
}

@ExceptionHandler( CaptchaException.class )
public ResponseEntity captchaErrorHandler ( CaptchaException e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.badRequest( e.getMessage() );
}

@ExceptionHandler( ServiceException.class )
public ResponseEntity serviceErrorHandler ( ServiceException e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.badRequest( e.getMessage() );
}

@ExceptionHandler( DaoException.class )
public ResponseEntity daoErrorHandler ( DaoException e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.badRequest( e.getMessage() );
}

@ExceptionHandler( { SQLException.class , DataAccessException.class } )
public ResponseEntity sqlErrorHandler ( Throwable e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "error", e );
}
return ResponseEntity.internalServerError( "服务器内部错误,EXCEPTION_CODE:" + ExceptionCode.SQL_EXCEPTION.getCode() );
}


@ExceptionHandler( Throwable.class )
public ResponseEntity globalErrorHandler ( Throwable e ) {
if ( LogUtils.getLogger().isErrorEnabled() ) {
LogUtils.getLogger().error( "internalServerError", e );
}
return ResponseEntity.internalServerError( "internalServerError : " + e.getMessage() );
}


private enum ExceptionCode {
SQL_EXCEPTION( "9001", "SQL异常" );

private String code;
private String comment;

ExceptionCode ( String code, String comment ) {
this.code = code;
this.comment = comment;
}

/**
* 判断传入的code是否是枚举中所定义的code
*
* @param code
* @return
*/
public static boolean isCode ( final String code ) {
for ( ExceptionCode value : ExceptionCode.values() ) {
if ( value.getCode().equalsIgnoreCase( code ) ) {
return true;
}
}
return false;
}

public static String codeValue ( final String code ) {
for ( ExceptionCode value : ExceptionCode.values() ) {
if ( value.getCode().equalsIgnoreCase( code ) ) {
return value.getComment();
}
}
return null;
}

public static boolean isNotCode ( final String code ) {
return ! isCode( code );
}

public String getComment () {
return comment;
}

public String getCode () {
return code;
}
}

}

流程控制

  • 和异常处理搭配使用
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
/**
* <p>
* 断言
* 使用异常流程控制,异常控制流程比if else '状态码'控制可读性更强
* </p>
* <b style="color:red">
* 注 : 异常的处理效率比条件分支低,内部系统可用异常形式控制流程,对外http/api接口则使用'错误码'.
* </b>
*
* @author : 披荆斩棘
* @date : 2017/6/3
*/
public abstract class AssertUtils {


/**
* 如果条件为<code>true</code> throw {@link CaptchaException}
*
* @param condition : 断言条件
* @param message : 错误信息
* @throws CaptchaException
*/
public static void assertCaptchaIsTrue ( boolean condition, String message ) {
if ( condition ) {
throw new CaptchaException( message );
}
}

/**
* 如果条件为<code>true</code> throw {@link ForbiddenException}
*
* @param condition : 断言条件
* @param message : 错误信息
* @throws ForbiddenException
*/
public static void assertPermissionIsTrue ( boolean condition, String message ) throws ForbiddenException {
if ( condition ) {
throw new ForbiddenException( message );
}
}

/**
* service 层断言
*
* @param condition : 断言条件
* @param message : 错误信息
* @throws ServiceException
*/
public static void isTrue ( boolean condition, String message ) {
assertServiceException( condition, message );
}


/**
* dao 层断言
*
* @param condition : 断言条件
* @param message : 错误信息
* @throws DaoException
*/
public static void assertDaoIsTrue ( boolean condition, String message ) {
daoServiceException( condition, message );
}

/**
* 断言
*
* @param condition : 断言条件
* @param message : 错误信息
* @throws DaoException 如果条件为<code>true</code>,则会抛Service异常(异常为运行时异常,在Spring中会有统一异常处理)
*/
private static void assertServiceException ( boolean condition, String message ) {
if ( condition ) {
throw new ServiceException( message );
}
}

/**
* 断言
*
* @param condition : 断言条件
* @param message : 错误信息
* @throws DaoException 如果条件为<code>true</code>,则会抛Dao异常(异常为运行时异常,在Spring中会有统一异常处理)
*/
private static void daoServiceException ( boolean condition, String message ) {
if ( condition ) {
throw new DaoException( message );
}
}


}

json 字段动态过滤

https://github.com/bohnman/squiggly-filter-jackson

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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

/**
* json工具类
*
* @author : 披荆斩棘
* @date : 2016/10/2
*/
public abstract class JsonUtils {


/**
* <p>jackson</p>
* <a href="https://github.com/FasterXML/jackson-docs">document</a>
*/
private static final ObjectMapper BASIC = new ObjectMapper();
private static final ObjectMapper CUSTOMIZATION = new CustomizationObjectMapper();
/**
* <p>gson</p>
* <a href="https://github.com/google/gson/blob/master/UserGuide.md">document</a>
*/
private static final Gson GSON = new GsonBuilder().setDateFormat( DateUtils.DATE_BASIC_STYLE )
.create();
/**
* <p>fastjson</p>
* <a href="https://github.com/alibaba/fastjson/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98">document</a>
*/
private static JSON JSON;

static {
BASIC.setDateFormat( new SimpleDateFormat( DateUtils.DATE_BASIC_STYLE ) );
}

/**
* <p>
* 转换为Json,可过滤属性
* <p>默认使用Jackson进行转换,{@link #CUSTOMIZATION}</p>
* 注意 : <b style="color:red"><code>null</code>将不会被序列化</b>
* <pre>
* <code>@Data</code>
* <code>@Accessors(chain = true)</code>
* public class User implements Serializable {
* private Long id;
* private String name;
* private String[] names;
* private String username;
* private List< String > info;
* private Date time;
* private Address address;
* private Order order;
*
* public User () {
* this.id = 1001L;
* this.name = null;
* this.names = new String[]{ "令狐冲" , "张三" , "大毛" };
* this.info = Arrays.asList( "北京", "朝阳", "密云" );
* this.time = new Date();
* this.username = "admin";
* this.address = new Address().setZip( "518000" ).setProvince( "北京" ).setName( "地址" );
* this.order = new Order().setId( 8888L ).setName( "支付宝" );
* }
* <code>@Data</code>
* <code>@Accessors(chain = true)</code>
* public class Order implements Serializable {
* private Long id;
* private String name;
* }
* <code>@Data</code>
* <code>@Accessors(chain = true)</code>
* public class Address implements Serializable {
* private String name;
* private String province;
* private String zip;
* }
* }
*
* {@link JsonUtils#toFilterJson(Object , String)}
* String filter = "表达式";
* JsonUtils.toFilterJson(user,filter);
*
* Object String Presentation Examples
* ------ ------ ------------ -------
* user "" 空字符串 {}
* user null null {"id":1001,"names":["令狐冲","张三","大毛"],"username":"admin","info":["北京","朝阳","密云"],"time":"2017-06-23 17:37:06","address":{"name":"地址","province":"北京","zip":"518000"},"order":{"id":8888,"name":"支付宝"}}
* user * '*'通配符 {"id":1001,"names":["令狐冲","张三","大毛"],"username":"admin","info":["北京","朝阳","密云"],"time":"2017-06-23 17:37:06","address":{"name":"地址","province":"北京","zip":"518000"},"order":{"id":8888,"name":"支付宝"}}
* user username,address 只显示某些字段 {"username":"admin","address":{"name":"地址","province":"北京","zip":"518000"}}
* user na*,result '*'通配符 {"names":["令狐冲","张三","大毛"]}
* user ** '*'通配符 {"id":1001,"names":["令狐冲","张三","大毛"],"username":"admin","info":["北京","朝阳","密云"],"time":"2017-06-23 17:37:06","address":{"name":"地址","province":"北京","zip":"518000"},"order":{"id":8888,"name":"支付宝"}}
* user address[province,zip] 对象字段内部过滤 {"address":{"province":"北京","zip":"518000"}}
* user (address,order)[name] 同时指定多个对象字段内部过滤 {"address":{"province":"北京","zip":"518000"}}
* user address.zip,address.name '.' 的方式 {"address":{"name":"地址","zip":"518000"}}
* user address.zip,address[name] '.' 的方式 {"address":{"name":"地址","zip":"518000"}}
* user ~na[a-z]es~ 正则表达式 {"names":["令狐冲","张三","大毛"]}
* user -names,-username '-' 排除字段 {"id":1001,"info":["北京","朝阳","密云"],"time":"2017-06-23 18:27:58","address":{"name":"地址","province":"北京","zip":"518000"},"order":{"id":8888,"name":"支付宝"}}
* user -names,username '-' 排除字段(注意) {"username":"admin"}
* user -names,-username,* '-' 排除字段 {"id":1001,"info":["北京","朝阳","密云"],"time":"2017-06-23 18:27:58","address":{"name":"地址","province":"北京","zip":"518000"},"order":{"id":8888,"name":"支付宝"}}
* user -user.names,-order.id '-' 排除字段 {"user":{"id":1001,"username":"admin","info":["北京","朝阳","密云"],"time":"2017-07-05 13:58:20","address":{"name":"地址","province":"北京","zip":"518000"},"order":{"id":8888,"name":"支付宝"}}}
*
* </pre>
*
* @param input :
* @param filter : 过滤字段
* @return 如果转换失败返回 <code>null</code> ,否则返回转换后的json
* @see <a href="https://github.com/bohnman/squiggly-filter-jackson">更多内容请看:Squiggly-document</a> <br/>
*/
public static String toFilterJson ( Object input, String filter ) {
return toJson( Squiggly.init( new CustomizationObjectMapper(), filter ), input );
}

/**
* 转换为Json
* <p>默认使用Jackson进行转换,{@link #BASIC}</p>
*
* @param input
* @return 如果转换失败返回 <code>null</code> ,否则返回转换后的json
*/
public static String toJson ( Object input ) {
return toJson( BASIC, input );
}

/**
* 转换为Json
* <p>默认使用Jackson进行转换,{@link #BASIC}</p>
*
* @param input
* @return 如果转换失败返回 <code>null</code> ,否则返回转换后的json
*/
public static String toCustomizationJson ( Object input ) {
return toJson( CUSTOMIZATION, input );
}

/**
* json转换为指定类型
* <p>默认使用Jackson进行转换,{@link #BASIC}</p>
* 注意 : 指定类型是内部类会报错 jackson can only instantiate non-static inner class by using default, no-arg
*
* @param inputJson : json
* @param targetType : 目标类型
* @param <T>
* @return 如果解析失败返回 <code>null</code> ,否则返回解析后的json
*/
public static < T > T jsonToType ( String inputJson, Class< T > targetType ) {
return jsonToType( BASIC, inputJson, targetType );
}

/**
* json转换为指定类型
* <p>默认使用Jackson进行转换,{@link #BASIC}</p>
* 注意 : 指定类型是内部类会报错 jackson can only instantiate non-static inner class by using default, no-arg
*
* @param inputJson : json
* @param targetType : 目标类型
* @param <T>
* @return 如果解析失败返回 <code>null</code> ,否则返回解析后的json
*/
public static < T > List< T > jsonToListType ( String inputJson, Class< T > targetType ) {
return jsonToListType( BASIC, inputJson, targetType );
}

/**
* json转换为指定类型(支持泛型)
* <pre class="code">
* 示例 :
* ResponseEntity< User > responseEntity = JsonUtils.jsonToType( jscksonJsonValue,new TypeReference< ResponseEntity< User > >() {} );
* </pre>
*
* @param inputJson : json
* @param targetType : 目标类型
* @param <T>
* @return
*/
public static < T > T jsonToType ( String inputJson, TypeReference targetType ) {
return jsonToType( BASIC, inputJson, targetType );
}

public static ObjectMapper getCustomizationMapper () {
return CUSTOMIZATION;
}

public static ObjectMapper getBasicMapper () {
return BASIC;
}

public static Gson getGson () {
return GSON;
}

public static JSON getFastjson () {
return JSON;
}

private static < T > T jsonToType ( ObjectMapper objectMapper, String inputJson, TypeReference targetType ) {
try {
return objectMapper.readValue( inputJson, targetType );
} catch ( Exception e ) {
LogUtils.getLogger().catching( e );
}
return null;
}

private static < T > T jsonToType ( ObjectMapper objectMapper, String inputJson, Class< T > targetType ) {
try {
return objectMapper.readValue( inputJson, targetType );
} catch ( Exception e ) {
LogUtils.getLogger().catching( e );
}
return null;
}

private static < T > List< T > jsonToListType ( ObjectMapper objectMapper,
String inputJson,
Class< T > targetType ) {
try {
return objectMapper.readValue(
inputJson,
objectMapper.getTypeFactory().constructCollectionType( List.class, targetType )
);
} catch ( Exception e ) {
LogUtils.getLogger().catching( e );
}
return null;
}

private static String toJson ( ObjectMapper objectMapper, Object input ) {
try {
return objectMapper.writeValueAsString( input );
} catch ( JsonProcessingException e ) {
LogUtils.getLogger().catching( e );
}
return null;
}

private static class CustomizationObjectMapper extends ObjectMapper {
CustomizationObjectMapper () {
super();
setDateFormat( new SimpleDateFormat( DateUtils.DATE_BASIC_STYLE ) );
setSerializationInclusion( JsonInclude.Include.NON_NULL ); // <code>null<code> 不序列化
}
}


}
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/**
* @author : 披荆斩棘
* @date : 16/6/16
*/
@ToString
@Getter
@Setter
@Accessors( chain = true )
public final class ResponseEntity < T > {
/** 通配符 **/
public static final String WILDCARD_ALL = "*";
/** 响应状态码 **/
private volatile String statusCode = StatusCode.BAD_REQUEST.getStatusCode();
/** 响应状态码对应的提示信息 **/
private volatile String statusMessage = StatusCode.BAD_REQUEST.getStatusMessage();
/** 响应内容 **/
private volatile T responseContent = null;
/**
* 可选择的过滤字段
* <p><b>为什么要有这个字段?</b></p>
* <ul>
* <li>1. 首先这个字段默认是 <code>null</code> ,json已设置 <code>null</code> 值不会被序列化,所以不用这个字段的时候它是不可见的</li>
* <li>
* 2. 是为了在添加权限时,可以给前端用,这个API到底是有哪些字段一目了然,如果单独再提供接口,这样每个API都要提供一个,会非常不优雅
* <p>
* 这里提供了2个方法,一个是方式生成(这个最简洁),还有一个是给非pojo类型用的
* {@link #setOptionalFilterFields(Set)}
* {@link #reflectionToOptionalFilterFields(Class)}
* </li>
* </ul>
* 当然一般情况下这个字段也用不到
*/
private volatile Set< String > optionalFilterFields = null;
/**
* json处理时需要过滤的字段,默认不过滤
* 具体看 {@link JsonUtils#toFilterJson}
*/
@JsonIgnore
private volatile String filterFields = WILDCARD_ALL;

public ResponseEntity () {
}

public ResponseEntity ( final String statusCode ) {
this( statusCode, null, null );
}

public ResponseEntity ( final String statusCode, final String statusMessage ) {
this( statusCode, statusMessage, null );
}

public ResponseEntity ( final String statusCode, final String statusMessage, final T responseContent ) {
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.responseContent = responseContent;
}

public static ResponseEntity empty () {
return new ResponseEntity();
}

/**
* 成功请求,成功状态码自行指定
*
* @param ok
* @param message
* @return
*/
public static ResponseEntity ok ( final StatusCode ok, final String message ) {
return new ResponseEntity( ok.getStatusCode(), message );
}

/**
* 失败请求,失败状态码自行指定
*
* @param fail
* @param message
* @return
*/
public static ResponseEntity badRequest ( final StatusCode fail, final String message ) {
return new ResponseEntity( fail.getStatusCode(), message );
}

public static ResponseEntity ok () {
return ok( StatusCode.OK.getStatusCode(), StatusCode.OK.getStatusMessage() );
}

public static ResponseEntity ok ( final String message ) {
return ok( StatusCode.OK.getStatusCode(), message );
}

public static ResponseEntity ok ( final String ok, final String message ) {
return new ResponseEntity( ok, message );
}


public static ResponseEntity internalServerError ( final String message ) {
return new ResponseEntity( StatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), message );
}

public static ResponseEntity internalServerError ( final StatusCode error, final String message ) {
return new ResponseEntity( error.getStatusCode(), message );
}


public static ResponseEntity badRequest () {
return badRequest( StatusCode.BAD_REQUEST.getStatusCode(), StatusCode.BAD_REQUEST.getStatusMessage() );
}

public static ResponseEntity badRequest ( final String message ) {
return badRequest( StatusCode.BAD_REQUEST.getStatusCode(), message );
}

public static ResponseEntity badRequest ( final String fail, final String message ) {
return new ResponseEntity( fail, message );
}


public static ResponseEntity forbidden () {
return forbidden( StatusCode.FORBIDDEN.getStatusMessage() );
}

public static ResponseEntity forbidden ( final String message ) {
return new ResponseEntity( StatusCode.FORBIDDEN.getStatusCode(), message );
}

public static ResponseEntity unauthorized () {
return unauthorized( StatusCode.UNAUTHORIZED.getStatusMessage() );
}

public static ResponseEntity unauthorized ( final String message ) {
return new ResponseEntity( StatusCode.UNAUTHORIZED.getStatusCode(), message );
}


public static ResponseEntity serviceUnavailable () {
return serviceUnavailable( StatusCode.SERVICE_UNAVAILABLE.getStatusMessage() );
}

public static ResponseEntity serviceUnavailable ( final String message ) {
return new ResponseEntity( StatusCode.SERVICE_UNAVAILABLE.getStatusCode(), message );
}


/**
* 给 responseContent 添加内容
* <pre>
* ResponseEntity.ok( "success" )
* .add( "username", "披荆斩棘" )
* .add( "password", "123456" )
* .add( "ip", "localhost" );
*
* ResponseEntity{statusCode='200', statusMessage='success', filterFields='*', responseContent={password=123456, ip=localhost, username=披荆斩棘}}
* </pre>
*
* @param key : <code>String</code>类型
* @param value : <code>Object</code>类型
* @return <code>this</code>
*/
public ResponseEntity add ( final String key, final Object value ) {
if ( null == this.responseContent ) {
this.responseContent = ( T ) new HashMap< String, Object >();
Map< String, Object > content = ( Map< String, Object > ) this.responseContent;
content.put( key, value );
return this;
}
if ( ! ( this.responseContent instanceof Map ) ) {
return this;
}
( ( Map ) this.responseContent ).put( key, value );
return this;
}


/**
* 反射获取对象字段并设置到当前对象的 {@link #optionalFilterFields} 中,忽略 serialVersionUID
*
* @param clazz {@link Class}
* @return this
*/
public ResponseEntity reflectionToOptionalFilterFields ( final Class clazz ) {
final Field[] declaredFields = clazz.getDeclaredFields();
this.optionalFilterFields = new LinkedHashSet<>( declaredFields.length );
for ( Field declaredField : declaredFields ) {
if ( "serialVersionUID".equalsIgnoreCase( declaredField.getName() ) ) {
continue;
}
optionalFilterFields.add( declaredField.getName() );
}
return this;
}

/**
* 设置过滤字段
* <p>
* <b style="color:red">注意只会过滤 responseContent 中的内容</b>
*
* @param filterFields : 过滤字段,{@link JsonUtils#toFilterJson(Object , String)}
* @return this
*/
public ResponseEntity setFilterFields ( final String filterFields ) {
if ( null == filterFields || WILDCARD_ALL.equals( filterFields ) ) {
return this;
}
StringBuilder buffer = new StringBuilder( WILDCARD_ALL ).append( ",responseContent[" );
// 如果是分页对象,则只对分页对象内的结果集进行处理
if ( this.getResponseContent() instanceof PageInfo ) {
buffer.append( "*,list[" )
.append( filterFields )
.append( "]]" );
} else {
buffer.append( filterFields )
.append( "]" );
}
this.filterFields = buffer.toString();
return this;
}

/**
* 设置过滤字段并过滤刷新
* <p>
* <b style="color:red">
* 注意该方法在controller中最后 <code>return</code> 时使用,可能会导致flush2次,因为在自定义 <p>
* {@link org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice} 的实现类中,会再次flush(因为我在返回的时候会进行flush); <p>
* 如果,你未定义 {@link org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice}
* 实现类那么就需要在 <code>return</code> 时调用 {@link #setFilterFieldsAndFlush(String)} 而不是 {@link #setFilterFields(String)} <p>
* 亦或者你自定义了实现类,但是没有进行flush,那么你还是得调用 {@link #setFilterFieldsAndFlush(String)}
* </b> <p>
* 当然我可以设置<code>boolean</code>状态值,但是我又不想这个状态值返回到外面,那么这样就需要对这个字段进行忽略,这样就不会进行序列化.<p>
* 但是有的时候在传输的时候是需要序列号和反序列化的,所以这里是使用约定而不是既定
* <p>
* 更多 {@link #setFilterFields(String)}
*/
public ResponseEntity setFilterFieldsAndFlush ( final String filterFields ) {
return this.setFilterFields( filterFields ).filterFieldsFlush();
}

/**
* 过滤字段刷新
*
* @return 刷新后的 <code>this</code>
*/
public ResponseEntity filterFieldsFlush () {
return JsonUtils.jsonToType( this.toJson(), this.getClass() );
}

/**
* 对<code>this</code>进行json序列号,如果设置了过滤字段则会进行过滤
*
* @return json
*/
public String toJson () {
if ( this.isNotFieldsFilter() ) {
return JsonUtils.toJson( this );
}
return JsonUtils.toFilterJson( this, this.getFilterFields() );
}


/**
* 是否成功
*
* @return 如果状态 <b style="color:red">是<code>StatusCode.OK</code></b> 则返回 <code>true</code>
*/
@JsonIgnore
public boolean isOk () {
return StatusCode.OK.getStatusCode().equals( this.getStatusCode() );
}

/**
* 是否不成功
*
* @return "!" {@link #isOk()}
*/
@JsonIgnore
public boolean isNotOk () {
return ! isOk();
}

/**
* 是否需要过滤字段
*
* @return "!" {@link #isNotFieldsFilter()}
*/
@JsonIgnore
public boolean isFieldsFilter () {
return ! this.isNotFieldsFilter();
}

/**
* 是否不需要过滤字段
*
* @return 如果 <b style="color:red"> null == this.getFilterFields() || {@link #filterFields} <code>equals</code> {@link #WILDCARD_ALL} </b>则返回 <code>true</code>
*/
@JsonIgnore
public boolean isNotFieldsFilter () {
return null == this.getFilterFields() || WILDCARD_ALL.equals( this.getFilterFields() );
}


public enum StatusCode {
/** [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 **/
OK( "200", "请求成功" ),
/** [POST/PUT/PATCH]:用户新建或修改数据成功。 **/
CREATED( "201", "操作成功" ),
/** 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) **/
OK_NOT_HANDLER( "202", "收到请求" ),
/** 204 NO CONTENT - [DELETE]:用户删除数据成功。 **/
NO_CONTENT( "204", "数据删除成功" ),
/** 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 **/
BAD_REQUEST( "400", "请求失败" ),
/** 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 **/
UNAUTHORIZED( "401", "身份验证失败" ),
/** 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 **/
FORBIDDEN( "403", "权限不足" ),
/** 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 **/
NOT_FOUND( "404", "记录不存在" ),
/** 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 **/
NOT_ACCEPTABLE( "404", "请求格式错误" ),
REQUEST_TIME_OUT( "408", "服务器等待客户端发送的请求时间过长,超时" ),
/** 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 **/
GONE( "410", "请求的资源被永久删除" ),
/** 422 请求格式正确,但是由于含有语义错误,无法响应 **/
UNPROCESSABLE_ENTITY( "422", "验证失败" ),
TOO_MANY_REQUESTS( "429", "太多的请求" ),
TRADE_REPETITION( "460", "重复交易" ),
/** 500 INTERNAL SERVER INTERNAL_SERVER_ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 **/
INTERNAL_SERVER_ERROR( "500", "请求出错" ),
SERVICE_UNAVAILABLE( "503", "由于临时的服务器维护或者过载,服务器当前无法处理请求" );

private final String statusCode;
private final String statusMessage;

StatusCode ( String statusCode, String statusMessage ) {
this.statusCode = statusCode;
this.statusMessage = statusMessage;
}

public String getStatusMessage () {
return statusMessage;
}

public String getStatusCode () {
return statusCode;
}

}


}
  • 这样我在控制器中返回数据时,我就可以动态指定字段了
1
2
3
4
5
6
7

@GetMapping
public ResponseEntity< PageInfo< User > > listPage ( PageRowBounds pageRowBounds, UserQuery query ) {
return ResponseEntity.ok()
.setResponseContent( userService.listPage( pageRowBounds, query ) )
.setFilterFieldsAndFlush( "-password" ); // 这里我就想过滤掉 password 这个字段
}
  • 如果在权限系统中,粒度控制到字段的话,配合 ResponseBodyAdvice 就可以很轻松的实现了

我想得到当前请求的原始uri并得到path变量的相关值,以及解析过程

如果没看过源码不知道Spring MVC执行流程,反正只要记住一个类就行,DispatcherServlet

  • DispatcherServlet

DispatcherServlet 执行处理入口方法应该时doService,doService没有直接处理,而是交给doDispatch处理.

doDispatch方法最核心的代码只有4句
大体分为2部分 : 处理请求和渲染页面.

1. 根据request找到Handler
2. 根据Handler找到对应的HandlerAdapter
3. 用HandlerAdapter处理Handler
4. 调用processDispatchResult方法处理上面处理之后的结果(包含找到View并渲染输出给用户)
1
2
3
4
mappedHandler = getHandler(processedRequest); // 1
HandlerAdapterha = getHandlerAdapter(mappedHandler.getHandler()); // 2
mv = ha.handle(processedRequest,response,mappedHandler.getHandler()); // 3
processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException) // 4

下面来看



具体就不看了,直接找到 RequestMappingInfoHandlerMapping 的 handleMatch 这个方法

既然知道Spring是设置在 request.setAttribute 中,那么在当前请求中通过request直接获取就行了

1
2
3
4
5
6
final String uriVariables = ( String ) request.getAttribute( HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE );
// /roles/{userId}/{roleId}
System.err.println( "uriVariables = " + uriVariables );
final Map< String, String > decodedUriVariables = ( Map< String, String > ) request.getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE );
// {userId=1, roleId=10000}
System.err.println( "decodedUriVariables = " + decodedUriVariables );

.