在基于Spring的项目中,我们通常可以使用 @RequestMapping 、@PostMapping 这类注解来设置某个处理逻辑的请求URL。但是这都在代码中写死了,我们如何实现在项目启动时根据某个配置来动态设置这个请求的URL呢?
其实要实现也很简单,只需要以下几个简单步骤就可以达成:
(1)添加一个用于动态注入请求URL的注解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package
cn
.
zifangsky
.
annotations
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
Mapping
;
import
java
.
lang
.
annotation
.
Documented
;
import
java
.
lang
.
annotation
.
ElementType
;
import
java
.
lang
.
annotation
.
Retention
;
import
java
.
lang
.
annotation
.
RetentionPolicy
;
import
java
.
lang
.
annotation
.
Target
;
@Target
(
{
ElementType
.
METHOD
,
ElementType
.
TYPE
}
)
@Retention
(
RetentionPolicy
.
RUNTIME
)
@Documented
@Mapping
public
@interface
PropertySourcedMapping
{
String
propertyKey
(
)
;
}
|
(2)添加一个自定义的 HandlerMapping,用于在Bean初始化的时候修改请求URL:
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
|
package
cn
.
zifangsky
.
plugins
;
import
cn
.
zifangsky
.
annotations
.
PropertySourcedMapping
;
import
org
.
apache
.
commons
.
beanutils
.
BeanUtils
;
import
org
.
apache
.
commons
.
lang3
.
StringUtils
;
import
org
.
springframework
.
core
.
Ordered
;
import
org
.
springframework
.
core
.
annotation
.
AnnotationUtils
;
import
org
.
springframework
.
stereotype
.
Controller
;
import
org
.
springframework
.
web
.
bind
.
annotation
.
RequestMapping
;
import
org
.
springframework
.
web
.
method
.
HandlerMethod
;
import
org
.
springframework
.
web
.
servlet
.
HandlerMapping
;
import
org
.
springframework
.
web
.
servlet
.
mvc
.
method
.
RequestMappingInfo
;
import
org
.
springframework
.
web
.
servlet
.
mvc
.
method
.
annotation
.
RequestMappingHandlerMapping
;
import
org
.
springframework
.
web
.
util
.
UriTemplate
;
import
javax
.
servlet
.
http
.
HttpServletRequest
;
import
java
.
lang
.
reflect
.
Method
;
import
java
.
text
.
MessageFormat
;
import
java
.
util
.
LinkedHashMap
;
import
java
.
util
.
Map
;
/**
* 用于在{@link Controller}的{@link RequestMapping}中动态注入请求URL
* @author zifangsky
* @date 2018/11/7 11:00
* @since 1.0.0
*/
public
class
PropertySourcedRequestMappingHandlerMapping
extends
RequestMappingHandlerMapping
{
private
final
Map
<
String
,
HandlerMethod
>
handlerMethods
=
new
LinkedHashMap
<>
(
)
;
private
final
WebUserInfo
webUserInfo
;
private
final
Object
handler
;
public
PropertySourcedRequestMappingHandlerMapping
(
WebUserInfo
webUserInfo
,
Object
handler
)
{
this
.
webUserInfo
=
webUserInfo
;
this
.
handler
=
handler
;
}
@Override
protected
void
initHandlerMethods
(
)
{
logger
.
debug
(
"initialising the handler methods"
)
;
setOrder
(
Ordered
.
HIGHEST_PRECEDENCE
+
1000
)
;
Class
<?
>
clazz
=
handler
.
getClass
(
)
;
if
(
isHandler
(
clazz
)
)
{
for
(
Method
method
:
clazz
.
getMethods
(
)
)
{
//获取参数注解
PropertySourcedMapping
mapper
=
AnnotationUtils
.
getAnnotation
(
method
,
PropertySourcedMapping
.
class
)
;
if
(
mapper
!=
null
)
{
RequestMappingInfo
mapping
=
getMappingForMethod
(
method
,
clazz
)
;
HandlerMethod
handlerMethod
=
createHandlerMethod
(
handler
,
method
)
;
String
mappingPath
=
mappingPath
(
mapper
)
;
if
(
mappingPath
!=
null
)
{
logger
.
info
(
String
.
format
(
"Mapped URL path [%s] onto method [%s]"
,
mappingPath
,
handlerMethod
.
toString
(
)
)
)
;
//将urlPath和对应的处理HandlerMethod放到LinkedHashMap
handlerMethods
.
put
(
mappingPath
,
handlerMethod
)
;
}
else
{
for
(
String
path
:
mapping
.
getPatternsCondition
(
)
.
getPatterns
(
)
)
{
logger
.
info
(
String
.
format
(
"Mapped URL path [%s] onto method [%s]"
,
path
,
handlerMethod
.
toString
(
)
)
)
;
handlerMethods
.
put
(
path
,
handlerMethod
)
;
}
}
}
}
}
}
/**
* 根据参数注解获取请求URL
* @author zifangsky
* @date 2018/11/7 10:50
* @since 1.0.0
* @param mapper 参数注解
* @return java.lang.String
*/
private
String
mappingPath
(
final
PropertySourcedMapping
mapper
)
{
final
String
key
=
mapper
.
propertyKey
(
)
;
if
(
StringUtils
.
isNoneBlank
(
key
)
)
{
try
{
return
BeanUtils
.
getProperty
(
webUserInfo
,
key
)
;
}
catch
(
Exception
e
)
{
logger
.
error
(
MessageFormat
.
format
(
"获取参数[{0}]对应的值失败:"
,
key
)
,
e
)
;
}
}
return
null
;
}
@
Override
protected
boolean
isHandler
(
Class
<?
>
beanType
)
{
return
(
(
AnnotationUtils
.
findAnnotation
(
beanType
,
Controller
.
class
)
!=
null
)
||
(
AnnotationUtils
.
findAnnotation
(
beanType
,
RequestMapping
.
class
)
!=
null
)
)
;
}
/**
* 通过请求的urlPath查找处理的{@link HandlerMethod}
* @author zifangsky
* @date 2018/11/7 10:35
* @since 1.0.0
* @param urlPath 请求URL
* @return org.springframework.web.method.HandlerMethod
*/
@Override
protected
HandlerMethod
lookupHandlerMethod
(
String
urlPath
,
HttpServletRequest
request
)
{
logger
.
debug
(
"looking up handler for path: "
+
urlPath
)
;
HandlerMethod
handlerMethod
=
handlerMethods
.
get
(
urlPath
)
;
if
(
handlerMethod
!=
null
)
{
return
handlerMethod
;
}
for
(
String
path
:
handlerMethods
.
keySet
(
)
)
{
UriTemplate
template
=
new
UriTemplate
(
path
)
;
if
(
template
.
matches
(
urlPath
)
)
{
request
.
setAttribute
(
HandlerMapping
.
URI_TEMPLATE_VARIABLES_ATTRIBUTE
,
template
.
match
(
urlPath
)
)
;
return
handlerMethods
.
get
(
path
)
;
}
}
return
null
;
}
}
|
(3)在JavaConfig中配置指定的controller使用上述的HandlerMapping:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* 添加一个自定义的{@link HandlerMapping},用于修改请求路径
*/
@Bean
public
HandlerMapping
webLoginControllerMapping
(
WebUserInfo
webUserInfo
,
UserService
userService
)
{
return
new
PropertySourcedRequestMappingHandlerMapping
(
webUserInfo
,
new
WebLoginController
(
userService
)
)
;
}
/**
* 添加一个自定义的{@link HandlerMapping},用于修改请求路径
*/
@Bean
public
HandlerMapping
webUserControllerMapping
(
WebUserInfo
webUserInfo
,
UserService
userService
)
{
return
new
PropertySourcedRequestMappingHandlerMapping
(
webUserInfo
,
new
WebUserController
(
userService
)
)
;
}
|
(4)测试: