Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

preface

This section describes the SysLogininforController of the Ruoyi-Admin module of Ruoyi-Vue. This interface is used to display user login logs and explain the RequestContextHolder. SysOperlogController is similar, so I won’t go into details here.

List page


    @GetMapping("/list")

    public TableDataInfo list(SysLogininfor logininfor) {

        startPage();

        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);

        return getDataTable(list);

    }

Copy the code

This is a common implementation of a list page in the system. The parameter is SysLogininfor and the object returned is

TableDataInfo, which contains List< sysLoginFor >. Let’s look at the implementation

startPage

This is the method you get when you inherit from BaseController, using PageHelper for paging.

/** * set request paging data */

    protected void startPage(a) {

        PageDomain pageDomain = TableSupport.buildPageRequest();

        Integer pageNum = pageDomain.getPageNum();

        Integer pageSize = pageDomain.getPageSize();

        if(StringUtils.isNotNull(pageNum) && StringUtils.isNotNull(pageSize)) { String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); Boolean reasonable = pageDomain.getReasonable(); PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); }}Copy the code

So how does TableSupport in the Common module get the parameters that were passed in? After all, there’s no Request coming in, right? Actually very simple, TableSupport. BuildPageRequest here by RequestContextHolder for the current request and response, RequestContextHolder is usually used to help get a request and response from the Service layer. After all, passing a request and response from the Controller to the Service layer is a bit too simplistic


    public static ServletRequestAttributes getRequestAttributes(a)
    {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }
    /** * get request */
    public static HttpServletRequest getRequest(a)
    {
        return getRequestAttributes().getRequest();
    }
    /** * get response */
    public static HttpServletResponse getResponse(a)
    {
        return getRequestAttributes().getResponse();
    }
Copy the code

With its help, we can easily retrieve the corresponding request and response.

RequestContextHolder questions

How do I know what the request and response of the current request are?

In the RequestContextHolder class, we see two subclasses that inherit from ThreadLocal to hold the request from the current thread

    private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
    private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context");
Copy the code

NamedThreadLocal and NamedInheritableThreadLocal inherited from ThreadLocal, NamedThreadLocal is more a name, InheritableRequestAttributesHolder is more interesting, however, its corresponding NamedInheritableThreadLocal can represent its succession quilt thread.

    public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {
        if (attributes == null) {
            resetRequestAttributes();
        } else if (inheritable) {
            inheritableRequestAttributesHolder.set(attributes);
            requestAttributesHolder.remove();
        } else{ requestAttributesHolder.set(attributes); inheritableRequestAttributesHolder.remove(); }}@Nullable
    public static RequestAttributes getRequestAttributes(a) {
        RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
        if (attributes == null) {
            attributes = (RequestAttributes)inheritableRequestAttributesHolder.get();
        }
        return attributes;
    }
Copy the code

Getthread getthread getthread getthread getthread getthread getthread getthread getthread getthread

When did you set the current request and response?

Is implemented in the FrameworkServlet file of the WebMVC package, which overrides doGet and other methods using processRequest in those methods, In this method you can see that the current request and response Settings are made.

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
        // Get the LocaleContext saved from the previous request
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        // The current LocaleContext
        LocaleContext localeContext = this.buildLocaleContext(request);
        // The RequestAttributes saved for the previous request
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        // Create new RequestAttributes
        ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new FrameworkServlet.RequestBindingInterceptor());
        // Specify the Settings here
        this.initContextHolders(request, localeContext, requestAttributes);
        try {
            this.doService(request, response);
        } catch (IOException | ServletException var16) {
            failureCause = var16;
            throw var16;
        } catch (Throwable var17) {
            failureCause = var17;
            throw new NestedServletException("Request processing failed", var17);
        } finally {
            // Restore
            this.resetContextHolders(request, previousLocaleContext, previousAttributes);
            if(requestAttributes ! =null) {
                requestAttributes.requestCompleted();
            }
            this.logResult(request, response, (Throwable)failureCause, asyncManager);
            this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause); }}Copy the code

In the setup method, we can see that the setRequestAttributes method we showed above is called, where the request context is set.

     private void initContextHolders(HttpServletRequest request, @Nullable LocaleContext localeContext, @Nullable RequestAttributes requestAttributes) {
        if(localeContext ! =null) {
            LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
        }
        if(requestAttributes ! =null) {
            RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); }}Copy the code

Database design

You can see the system login log table

drop table if exists sys_logininfor;

create table sys_logininfor (
  info_id        bigint(20)     not null auto_increment   comment 'access ID',
  user_name      varchar(50)    default ' '                comment 'User account',
  ipaddr         varchar(128)   default ' '                comment 'Login IP address',
  login_location varchar(255)   default ' '                comment 'Login location',
  browser        varchar(50)    default ' '                comment 'Browser Type',
  os             varchar(50)    default ' '                comment 'Operating system',
  status         char(1)        default '0'               comment 'Login status (0 successful, 1 failed)',
  msg            varchar(255)   default ' '                comment 'Prompt message',
  login_time     datetime                                 comment 'Access Time'.primary key (info_id)

) engine=innodb auto_increment=100 comment = 'System Access Record';

Copy the code

There is no other index except for a primary key, in fact, I think we can add a time index, because the usual search log will add time to the amount of data.

In addition, we can see the last data clearing interface, which uses truncate

    <update id="cleanLogininfor">
        truncate table sys_logininfor
    </update>
Copy the code

Compared with delete, truncate is faster (after all, create after drop) and can reset the primary key of the table. Therefore, it is obviously a good choice. If there are too many login logs, the query of the table will definitely be slower and slower.