preface

Has recently been submerged by the endless business requirements, and don’t have time to breathe, finally received a comfort zone of work makes me break code, solve its process is very tortuous, once let I doubt the life, but the harvest is very big also, code is not obvious, but felt erased Java, Tomcat, Spring has been blocked in front of my eyes a layer of yarn. A new level of understanding.

I haven’t output for a long time, so I choose one aspect to summarize, hoping to learn some other things in the process of combing. Due to Java’s thriving ecosystem, each of the following modules has a large number of articles devoted to it. So I chose another Angle, starting from a practical problem, to connect these scattered pieces of knowledge, you can look at it as a summary. For the ultimate details of each module, check out the official documentation or other blogs on the web. Also pay attention to: code ape technology column, in the background reply: “interview treasure book” can be obtained, hd PDF latest edition 3625 pages of Internet big factory interview questions.

The requirement was simple and clear, not at all the sexy requirement that the product mentioned: add a common appKey whitelist verification function to our Web framework and hope that it would be more scalable.

This Web framework is implemented by the predecessor of the department based on Spring-boot. It is between the service and the Spring framework and performs some general functions biased to the service, such as log output, function switch, and general parameter analysis. Normally transparent to the business, recently so busy getting the requirements right and the code right that it never even noticed it existed.

Traditional AOP


The first thing that comes to mind for this requirement is, of course, the AOP interface provided by Spring-Boot; you just need to add pointcuts in front of the Controller method and then work with them.

implementation

The steps are as follows:

  1. use@AspectDeclare the section classWhitelistAspect;
  2. Adds a pointcut to the section classwhitelistPointcut()In order to achieve the ability of this pointcut to be flexible and assemblable, it is not used hereexecutionIntercept them all, but add an annotation@WhitelistAnnotated methods are whitelisted.
  3. Use Spring’s AOP annotations in the faceted classes@BeforeDeclare a notification methodcheckWhitelist()Verify the whitelist before the Controller method is executed.

The pseudocode of the section class is as follows:

 @Aspect
  public class WhitelistAspect {

    @Before(value = "whitelistPointcut() && @annotation(whitelist)")
    public void checkAppkeyWhitelist(JoinPoint joinPoint, Whitelist whitelist) {
        checkWhitelist();
        // You can use joinPoint.getargs () to get the Controller method parameters
        // You can use the whitelist variable to get annotation parameters
    }

    @Pointcut("@annotation(com.zhenbianshu.Whitelist)")
    public void whitelistPointCut(a) {}}Copy the code
  1. Add it to the Controller method@WhitelistAnnotations implement functionality.

extension

In this example, annotations are used to declare pointcuts, and I have implemented the annotation parameter to declare whitelists to be verified. If you need to add other whitelists later, such as by UID, you can add methods such as UID () to this annotation to implement custom validation.

In addition, Spring’s AOP supports pointcut declaration methods such as execution, bean (the execution method of a bean object that matches a particular name), and notification methods such as @around (executed within the execution of the target function) and @After (After the execution of the method).

So, the functionality has been implemented, but the leader is not satisfied with =_=, because there is too much AOP in the project, it is overused, and suggested that I take a different approach. Well, we have to. Also pay attention to: code ape technology column, in the background reply: “interview treasure book” can be obtained, hd PDF latest edition 3625 pages of Internet big factory interview questions.

Interceptor


Spring’s Interceptor is also a great way to do this. As the name suggests, interceptors are used to determine whether or not an Action should be executed in a Controller with some parameters before it is executed. To implement an interceptor, you can implement Spring’s HandlerInterceptor interface.

implementation

The implementation steps are as follows:

  1. Define the interceptor classAppkeyInterceptorClass and implement the HandlerInterceptor interface.
  2. To achieve itspreHandle()Methods;
  3. The preHandle method uses annotations and parameters to determine whether a request needs to be interceptedfalse;
  4. In customWebMvcConfigurerAdapterClass to register the interceptor;

The AppkeyInterceptor class is as follows:

@Component
public class WhitelistInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Whitelist whitelist = ((HandlerMethod) handler).getMethodAnnotation(Whitelist.class);
        // whitelist.values(); Get the request parameters through request and the annotation parameters through the Whitelist variable
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  // the method is executed after the Controller method
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  // Execute after view rendering is complete}}Copy the code

extension

To enable the interceptor, you also need to explicitly configure it to be enabled, which is configured using the WebMvcConfigurerAdapter. Note that the MvcConfiguration that inherits it needs to be in the ComponentScan path.

@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WhitelistInterceptor()).addPathPatterns("/ *").order(1);
        // The order in which paths are enabled by interceptors can be configured. If multiple interceptors exist, any interceptor returning false will cause subsequent request methods to stop executing}}Copy the code

Note also that the response code is 200 after successful execution of the interceptor, but the response data is empty.

We already have an Auth parameter, appKey can be retrieved from the Auth parameter, we can whitelist Auth as a way, why not check Auth? Emmm… Vomiting blood.

ArgumentResolver


The parameter parser is a spring-provided tool for parsing custom parameters. The @RequestParam annotation is a shadow of this and allows you to combine the parameters into the desired shape before entering the Controller Action. Spring maintains a ResolverList. When a request arrives, Spring finds parameters of a custom type (non-basic type) and tries these in turn until one Resolver resolves the required parameters. To implement a parser parameters, need to implement the interface HandlerMethodArgumentResolver.

implementation

  1. Define a custom parameter typeAuthParam, class has appKey related fields;
  2. defineAuthParamResolverAnd implement HandlerMethodArgumentResolver interface;
  3. implementationsupportsParameter()The interface method ADAPTS AuthParam to AuthParamResolver.
  4. implementationresolveArgument()The interface method parses the Reqest object to generate an AuthParam object and validates the AuthParam object to verify whether appKey is in the whitelist.
  5. Add the AuthParam parameter to the signature on the Controller Action method to enable this Resolver;

The AuthParamResolver class is implemented as follows:

@Component
public class AuthParamResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(AuthParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Whitelist whitelist = parameter.getMethodAnnotation(Whitelist.class);
        // Check the whitelist with webRequest and whitelist
        return newAuthParam(); }}Copy the code

extension

Of course, using the parameter parser also requires a separate configuration, which we also configure in the WebMvcConfigurerAdapter:

@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(newAuthParamResolver()); }}Copy the code

After the implementation this time, I was still a little worried, so I searched on the Internet to see if there are other ways to realize this function, and FOUND that Filter is also common.

Filter


Filters are not provided by Spring; they are defined in the Servlet specification and are supported by the Servlet container. Requests that are filtered by Filter are not dispatched to the Spring container. Its implementation is also relatively simple, javax.servlet.filter interface can be implemented.

Since the Filter is not in the Spring container, it does not get the resources of the Spring container, so you have to use the native Java ServletRequest and ServletResponse to get the request parameters.

In addition, the doFilter method calling FilterChain must be displayed in a Filter, otherwise the request is considered to be blocked. The implementation is similar to:

public class WhitelistFilter implements javax.servlet.Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
  // it is called once after initialization
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     // Determine whether interception is required
       chain.doFilter(request, response); // The request passes to display the call
    }

    @Override
    public void destroy(a) {
     // called once when destroyed}}Copy the code

extension

Filter also needs to display the configuration:

@Configuration
public class FilterConfiguration {

    @Bean
    public FilterRegistrationBean someFilterRegistration(a) {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new WhitelistFilter());
        registration.addUrlPatterns("/ *");
        registration.setName("whitelistFilter");
        registration.setOrder(1); // Sets the order in which filters are invoked
        returnregistration; }}Copy the code

summary


Each of the four implementations has its own appropriate scenarios, so what is the order of calls between them?

The Filter is implemented by Servlet, so it is called first, followed by the Interceptor, which does not need further processing, followed by the parameter parser, and finally the cutting point of the section. After I implemented all four approaches in one project, the output logs proved this conclusion.

In addition, the author has completed two columns of the article Mybatis advanced, Spring Boot advanced, has organized the column into a book, there is a need for public number reply keywords Mybatis advanced, Spring Boot advanced free access.

When you step out of the concrete implementation and turn around and look at the implementation, there are some aspects to it. Because before their own programming way more inclined to process oriented programming, in the use of Java object-oriented contrast AOP and process oriented hook child, some feeling, write a day to sort it out.