SSM (14) annotation-based HTTP anti-reset plugin
Objective
For what we often use today, RESTful API
we usually need to uniquely identify the request, that is, each time with a request number, such as reqNO
.
For the request of this kind of operation database in warehousing we generally want to guarantee his uniqueness, a request number is usually only used once, so we need to add a verification mechanism to this request.
The idea for this requirement is to customize annotation
and annotate only the interfaces that need to be verified. Then, by using the interface of the annotation, each request number is stored in Redis
, and each time it is judged whether there is a request number.
Take a look at the actual effect of adding this plugin:
Custom annotations
First we want to customize an annotation:
1234567 |
@Target (Elementtype.method) @Retention (retentionpolicy.runtime) @Documented public @interface checkreqno { String desc() default "";} |
(PS: This is not too much to explain the relevant knowledge of annotations).
First use @interface
to declare an annotation. We then use Java
the three meta annotations we provide to define CheckReqNo
annotations.
It @Target
shows where this annotation is used, the use ElementType.METHOD
indicates that it is applied to the method, and there are other values to see java.lang.annotation.ElementType
the enumeration type.
@Retention
The annotations indicate the extent to which our annotations are valid, and the configuration here RetentionPolicy.RUNTIME
indicates that they can be obtained by reflection at run time.
@Documented
The literal meaning should also be guessed to be used to generate JavaDoc
the document.
The method that defines one is desc()
not actually used, but if you need to customize some of the requirements when using annotations, you can write them in filed(域)
this way, and you can get specific values by reflection.
such as: @CheckReqNo(desc = "abc")
the value that can be obtained "abc"
.
Facet annotations
The previous idea was to slice through all the methods that used the annotation:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364 |
@Aspect@Componentpublic class Reqnodrcaspect { private static Logger Logger = Loggerfactory.getlogger (reqnodrcaspect.class); @Value ("${redis.prefixreq:reqno}") private String prefixreq;@Value ("${redis.day:1}") private long day; @Autowiredprivate redistemplate<string, string> redistemplate;@PostConstructpublic void init() throws Exception { Logger.info ("Ssm-request-check init ...");}@Pointcut ("@annotation (com.crossoverJie.request.anotation.CheckReqNo)") public void checkrepeat() { }@Before ("checkrepeat ()") public void before(Joinpoint joinpoint) throws Exception { Baserequest request;request = getbaserequest (joinpoint);if (Request! = null) { final String reqno = Request.getreqno ();if (Stringutil.isempty (reqno)) {throw New RuntimeException ("reqno cannot be Empty"); }else{try {String tempreqno = Redistemplate.opsforvalue (). Get (Prefixreq +reqno); Logger.debug ("tempreqno=" +tempreqno);if ((Stringutil.isempty (tempreqno))) {Redistemplate.opsforvalue (). Set (Prefixreq + reqno, Reqno, Day, timeunit.days);}else{throw New RuntimeException ("Duplicate request number, reqno=" +reqno); } }catch (Redisconnectionfailureexception e) {Logger.error ("Redis operation exception", e);throw New RuntimeException ("Need Redisservice"); }}} }public static baserequest getbaserequest(joinpoint joinpoint) throws Exception { Baserequest returnrequest =null;object[] arguments = Joinpoint.getargs ();if (arguments! = null && arguments.length > 0) { Returnrequest = (baserequest) arguments[0];}return returnrequest;}} |
Use @Aspect
to define a slice.
Where prefixReq,day
the domain can customize the prefix of the cache request number key
and the time of the cache.
The key point is to use
@Pointcut("@annotation(com.crossoverJie.request.anotation.CheckReqNo)")
A pointcut is defined so that all @CheckReqNo
annotations used are intercepted.
The next logic is simpler, and is intercepted before each request.
Go first to Redis
see if this request number ( ps:反射获取
) exists, and if it does not exist, then pass and cache the request number for this time. Throws an exception if it exists.
Using annotations
You can jdbc.properties
customize the prefix and cache time in the configuration file
1234 |
#redis前缀redis. Prefixreq=reqno#redis Cache Time Default unit is day Redis.day=1 |
Not defined or Yes, default values are used.
Since this annotation needs to be added to the controller
layer, we have to use the CGLIB
proxy.
Here is a pit where you need to CGLIB
configure the open configuration to our web.xml
1234567891011 |
<!--Spring MVC servlet --- <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class> <init-param> <param-name>contextconfiglocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> |
In the file defined here spring-mvc.xml
, the springMVC
child container that is otherwise located cannot be loaded by the parent container.
Usage examples:
1234567891011121314151617181920212223 |
@CheckReqNo@RequestMapping (value = "/createrediscontent", method = Requestmethod.post) @ResponseBodyPublic baseresponse<nullbody> createrediscontent(@RequestBody rediscontentreq rediscontentreq) { baseresponse<nullbody> response =new Baseresponse<nullbody> ();Rediscontent rediscontent =new Rediscontent (); try {Commonutil.setlogvaluemodeltomodel (rediscontentreq,rediscontent); Rediscontentmapper.insertselective (rediscontent); Response.setreqno (Rediscontentreq.getreqno ()); Response.setcode (StatusEnum.SUCCESS.getCode ()); Response.setmessage (StatusEnum.SUCCESS.getMessage ()); }catch (Exception e) {Logger.error ("System error", E);Response.setreqno (Response.getreqno ()); Response.setcode (StatusEnum.FAIL.getCode ()); Response.setmessage (StatusEnum.FAIL.getMessage ()); }return response;} |
Unified Exception Controller
1234567891011121314151617181920212223242526272829 |
/*** * Classname:errorcontroller <br/> * Function: Error exception unified processing. <br/> *@author Crossoverjie*@version*@since JDK 1.7*/@ControllerAdvicepublic class Errorcontroller { private Logger Logger = Loggerfactory.getlogger (this.getclass ()); @ExceptionHandler (Exception.class) @ResponseStatus (Httpstatus.ok) @ResponseBody Public Object processunauthenticatedexception(nativewebrequest request, Exception e) { Logger.error ("Request exception:", e);baseresponse<nullbody> response =new Baseresponse<nullbody> ();Response.setcode (StatusEnum.FAIL.getCode ());if (e instanceof runtimeexception) { Response.setmessage (E.getmessage ()); }else {Response.setmessage (StatusEnum.FAIL.getMessage ()); }return response;}} |
This way, when the controller layer is abnormal, it will enter here for a unified return.
Summarize
At this point the entire plug-in process has been all OK, from which you can see the Spring AOP
various benefits in the actual development.
Previous articles have also been applied to:
- Using Redis in Javaweb applications
- Dynamically Switching data sources
Unknowingly this small white Primer SSM
series has been updated 14, in GitHub
also has more than 500 stars, during and many friends have had exchanges, discussion, thank you for your support.
Next may not be very likely to update the series, because the blogger now in the project group is currently more popular SpringBoot+SpringCloud
and Docker
the way to carry out the structure, so the center of gravity will certainly be moved to this aspect, after the use of the SpringBoot
trust that you will certainly not go back.
So after that I will continue to update SpringBoot+SpringCloud
the relevant articles, welcome continuous attention, continuous shooting bricks ( ps:这个插件也会用springBoot重写一遍
)
SSM (14) annotation-based HTTP anti-reset plugin