Directory
- Brief introduction
- Posture one, using the Filter interface
- 1. Register Filterregistrationbean
- 2. @WebFilter annotations
- Posture Two, Hanlderinterceptor
- Posture III, @ExceptionHandler annotations
- Posture Four, Requestbodyadvice/responsebodyadvice
- The use of Requestbodyadvice
- Responsebodyadvice usage
- Posture Five, @Aspect annotations
- Thinking
- Summary
Brief introduction
AOP (aspect-oriented programming) is often used to solve some coupling problems in the system, which is a programming mode
By extracting some common logic into a public module, it is called by the container to achieve the effect of inter-module isolation.
It also has an alias called Point-of-interest programming, which calls the core business logic in the system a core concern , while some common non-core logic is divided into crosscutting concerns.
AOP is commonly used in ...
Log records
You need to implement access logging for your Web application, but you don't want to do it all in one interface.
Safety control
Access control is implemented for URLs, and some illegal accesses are automatically intercepted.
Transaction
Some business processes need to be serialized in one transaction
Exception handling
The system handles exceptions and returns a custom message body based on different exceptions.
When I first began to touch programming, AOP was a new thing, and it was thought that AOP would be a great way to go.
As sure as it is, AOP has become a key core competency in the popular spring framework.
Next, we'll look at how to implement some of the commonly used interception operations in the Springboot framework.
Let's look at one of the following controller methods:
Example
@RestController@RequestMapping("/intercept")public class InterceptController { @PostMapping(value = "/body", consumes = { MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE }) public String body(@RequestBody MsgBody msg) { return msg == null ? "<EMPTY>" : msg.getContent(); } public static class MsgBody { private String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
In the body method of the above code, a MSGBODY request message body is accepted, and a simple output content field is eventually used.
Below, we will show you how to implement interception actions for this method. In total, there are five different postures.
Posture one, using the Filter interface
The Filter interface is defined by the Java EE and is called by the container before the servlet executes.
In Springboot, there are two ways to declare Filter:
1. Register Filterregistrationbean
Declare a Filterregistrationbean instance and make a series of definitions of filter, as follows:
@Bean public FilterRegistrationBean customerFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); // 设置过滤器 registration.setFilter(new CustomerFilter()); // 拦截路由规则 registration.addUrlPatterns("/intercept/*"); // 设置初始化参数 registration.addInitParameter("name", "customFilter"); registration.setName("CustomerFilter"); registration.setOrder(1); return registration; }
Where Customerfilter implements the filter interface, as follows:
public class CustomerFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(CustomerFilter.class); private String name; @Override public void init(FilterConfig filterConfig) throws ServletException { name = filterConfig.getInitParameter("name"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Filter {} handle before", name); chain.doFilter(request, response); logger.info("Filter {} handle after", name); }}
2. @WebFilter annotations
Adds @WebFilter annotations for the filter's implementation class, which is injected after scanning by the Springboot framework
@WebFilter is enabled with @servletcomponentscan to take effect
@Component@ServletComponentScan@WebFilter(urlPatterns = "/intercept/*", filterName = "annotateFilter")public class AnnotateFilter implements Filter { private static final Logger logger = LoggerFactory.getLogger(AnnotateFilter.class); private final String name = "annotateFilter"; @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { logger.info("Filter {} handle before", name); chain.doFilter(request, response); logger.info("Filter {} handle after", name); }}
Using annotations is the simplest, but the disadvantage is that the order property is still not supported (to control the sorting of the filter).
The usual @Order annotations can only be used to define the order in which the beans are loaded, but it really does not control the filter ordering.
This is a known issue, refer to Here
Recommended index
3 stars, the Filter definition belongs to the Java EE specification, and is executed by the servlet container scheduler.
Because it is independent of the framework, it is not possible to use the Spring framework's handy features
Some third-party components are now integrated with this approach.
Posture Two, Hanlderinterceptor
The handlerinterceptor is used to intercept the execution of the Controller method, which declares several methods:
| method | Description |
|-----|-----|
|prehandle | Call before the Controller method executes |
|prehandle | After the controller method is called before the view is rendered |
|aftercompletion| After the entire method executes (including exception-throwing captures) |
Examples based on the Handlerinterceptor interface implementation:
public class Customhandlerinterceptor implements Handlerinterceptor {private static final Logger Logger = Loggerfactor Y.getlogger (Customhandlerinterceptor.class); /* Before the Controller method call, returns TRUE to continue processing */@Override public boolean prehandle (HttpServletRequest request, Httpserv Letresponse response, Object handler) throws Exception {Handlermethod method = (Handlermethod) handler; Logger.info ("Customerhandlerinterceptor prehandle, {}", Method.getmethod (). GetName ()); return true; }/* Controller method call, the view renders before/@Override public void Posthandle (HttpServletRequest request, HTTPSERVLETR Esponse response, Object handler, Modelandview Modelandview) throws Exception {Handlermethod method = ( Handlermethod) handler; Logger.info ("Customerhandlerinterceptor posthandle, {}", Method.getmethod (). GetName ()); Response.getoutputstream (). Write ("Append content". GetBytes ()); }/* * The entire request is processed and the view is rendered. If there isException exception not empty */@Override public void aftercompletion (HttpServletRequest request, HttpServletResponse respons E, Object handler, Exception ex) throws Exception {Handlermethod method = (Handlermethod) handler; Logger.info ("Customerhandlerinterceptor aftercompletion, {}", Method.getmethod (). GetName ()); }}
In addition to the above code implementation, do not forget to register the Interceptor implementation:
@Configurationpublic class InterceptConfig extends WebMvcConfigurerAdapter { // 注册拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new CustomHandlerInterceptor()).addPathPatterns("/intercept/**"); super.addInterceptors(registry); }
Recommended index
4 stars, Handlerinterceptor from the SPRINGMVC frame, basically can replace the Filter interface to use;
In addition to the convenience of exception handling, through the interface parameters to obtain the Controller method instance, but also to achieve more flexible customization.
Posture III, @ExceptionHandler annotations
The purpose of the @ExceptionHandler is to catch exceptions thrown when the method executes,
It is commonly used to catch global exceptions and to lose the results of the customizations.
As in the following example:
@ControllerAdvice(assignableTypes = InterceptController.class)public class CustomInterceptAdvice { private static final Logger logger = LoggerFactory.getLogger(CustomInterceptAdvice.class); /** * 拦截异常 * * @param e * @param m * @return */ @ExceptionHandler(value = { Exception.class }) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public String handle(Exception e, HandlerMethod m) { logger.info("CustomInterceptAdvice handle exception {}, method: {}", e.getMessage(), m.getMethod().getName()); return e.getMessage(); }}
It is important to note that @ExceptionHandler need to be used in conjunctionwith @ControllerAdvice
The Assignabletypes property in which @ControllerAdvice specifies the name of the class being intercepted.
In addition to this, the annotations also support specifying package scan scopes, annotation scopes, and so on.
Recommended index
5 stars, @ExceptionHandler is very convenient to use, in the mechanism of exception handling is preferred;
It is also the most recommended method for the Springboot framework.
Posture Four, Requestbodyadvice/responsebodyadvice
Requestbodyadvice and Responsebodyadvice may be unfamiliar to the reader,
And these two interfaces are also Spring 4.x only began to appear.
The use of Requestbodyadvice
As we all know, springboot can use annotations such as @RequestBody to complete the conversion of the request content body to the object.
Requestbodyadvice , however, can be used to intercept processing before and after a request for a Content Object transformation , which defines several methods:
Method |
Description |
Supports |
Determine if support |
Handleemptybody |
Called when the request body is empty |
Beforebodyread |
Called when the request body is not read (converted) |
Afterbodyread |
Called after the request body has finished reading |
The implementation code is as follows:
@ControllerAdvice (assignabletypes = interceptcontroller.class) public class Customrequestadvice extends Requestbodyadviceadapter {private static final Logger Logger = Loggerfactory.getlogger (Customrequestadvice.class); @Override public Boolean supports (Methodparameter methodparameter, Type targetType, class<? extends Http Messageconverter<?>> convertertype) {//returns TRUE, which means to start intercept return MsgBody.class.getTypeName (). Equals (Ta Rgettype.gettypename ()); } @Override public Object Handleemptybody (object body, Httpinputmessage inputmessage, methodparameter parameter, Type TargetType, class<? Extends Httpmessageconverter<?>> convertertype) {logger.info ("Customrequestadvice handleEmptyBody"); For the empty request body, returns the object return body; } @Override Public Httpinputmessage beforebodyread (httpinputmessage inputmessage, methodparameter parameter, Type ta Rgettype, class<? Extends httpmessageconverter<? >> Convertertype) throws IOException {Logger.info ("Customrequestadvice beforebodyread"); Customizable message serialization return new Bodyinputmessage (inputmessage); } @Override public Object Afterbodyread (object body, Httpinputmessage inputmessage, methodparameter parameter, Type TargetType, class<? Extends Httpmessageconverter<?>> convertertype) {logger.info ("Customrequestadvice afterBodyRead"); Can be converted to the object after reading, do not handle return body here; }
In the above code implementation, the above mentioned msgbody object type is intercepted.
In Beforebodyread, a bodyinputmessage object is returned, and this object is responsible for the source data stream parsing transformation
public static class BodyInputMessage implements HttpInputMessage { private HttpHeaders headers; private InputStream body; public BodyInputMessage(HttpInputMessage inputMessage) throws IOException { this.headers = inputMessage.getHeaders(); // 读取原字符串 String content = IOUtils.toString(inputMessage.getBody(), "UTF-8"); MsgBody msg = new MsgBody(); msg.setContent(content); this.body = new ByteArrayInputStream(JsonUtil.toJson(msg).getBytes()); } @Override public InputStream getBody() throws IOException { return body; } @Override public HttpHeaders getHeaders() { return headers; } }
Code description
Complete the conversion of the data stream, including the following steps:
- Gets the request content string;
- Constructs the Msgbody object and takes the content string as its contents field;
- Serializes the Msgbody object Json into a stream of bytes for subsequent use.
Responsebodyadvice usage
The purpose of Responsebodyadvice is to intercept the returned content, as in the following example:
@ControllerAdvice(assignableTypes = InterceptController.class) public static class CustomResponseAdvice implements ResponseBodyAdvice<String> { private static final Logger logger = LoggerFactory.getLogger(CustomRequestAdvice.class); @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 返回true,表示启动拦截 return true; } @Override public String beforeBodyWrite(String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { logger.info("CustomResponseAdvice beforeBodyWrite"); // 添加前缀 String raw = String.valueOf(body); return "PREFIX:" + raw; } }
Look, it's still easy to understand, we've added a prefix to the returned string!
Recommended index
2 stars, this is two very unpopular interface, the current use of the scene is relatively limited;
This is typically used in scenarios where the input and output streams need to be specially processed (such as encryption and decryption).
Posture Five, @Aspect annotations
This is currently the most flexible approach, the direct use of annotations can achieve arbitrary object, method interception.
On the class of a bean * * @Aspect * * Annotations You can declare a bean to be an AOP-capable object.
@Aspect@Componentpublic class InterceptControllerAspect { private static final Logger logger = LoggerFactory.getLogger(InterceptControllerAspect.class); @Pointcut("target(org.zales.dmo.boot.controllers.InterceptController)") public void interceptController() { } @Around("interceptController()") public Object handle(ProceedingJoinPoint joinPoint) throws Throwable { logger.info("aspect before."); try { return joinPoint.proceed(); } finally { logger.info("aspect after."); } }}
Simple description
The @Pointcut is used to define the facet points, and the target keyword can be used to navigate to the specific class.
@Around defines a slice processing method, which is controlled by injecting Proceedingjoinpoint object.
Some common facets annotations:
Annotations |
Description |
@Before |
Before the method executes |
@After |
After the method executes |
@Around |
Before and after method execution |
@AfterThrowing |
After throwing an exception |
@AfterReturing |
After normal return |
Go a little deeper
The ability of AOP comes from Spring-boot-starter-aop, and further relies on aspectjweaver components.
Interested to learn more.
Recommended index
5 stars,AspectJ and springboot can be seamlessly integrated, which is a classic AOP framework,
Can implement any of the functions you want, the author has previously used in several projects, the effect is very good.
Support for annotations and automatic packet scanning greatly simplifies development, however, you still need to have a good understanding of Pointcut's definition first.
Thinking
Here, the reader may wonder, what is the relationship between these implementations of the Interceptor interface?
The answer is, it doesn't matter! Each interface is called at a different time, and we do the log output based on the code example above:
- Filter customFilter handle before - Filter annotateFilter handle before - CustomerHandlerInterceptor preHandle, body - CustomRequestAdvice beforeBodyRead - CustomRequestAdvice afterBodyRead - aspect before. - aspect after. - CustomResponseAdvice beforeBodyWrite - CustomerHandlerInterceptor postHandle, body - CustomerHandlerInterceptor afterCompletion, body - Filter annotateFilter handle after - Filter customFilter handle after
As you can see, the execution order of the various interceptor interfaces is as follows:
Summary
AOP is the basic idea for implementing interceptors, and this article describes the five common poses for intercepting functions in the Springboot project .
for each method, a real code sample is given, and the reader can choose the scenario that he or she applies to.
Finally, you are welcome to continue to follow the "tutorial series-springboot", looking forward to more exciting content ^-^