如何创建一个与Servlet-api完全解耦和的管理员后台操作日志监控

Posted JAVA技术博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何创建一个与Servlet-api完全解耦和的管理员后台操作日志监控相关的知识,希望对你有一定的参考价值。

技术框架MyBatis+Spring+SpringMVC

邮箱:huangfusuper@163.com欢迎交流


在日常开发系统后台时,需要针对管理员操作进行监控,如果使用Spring这一套技术体系,使用AOP切面编程+自定义注解不妨是一个好办法,但是在使用这一套体系的同时也会出现一些坑。比如这一套体系是完全依赖于WEB环境,脱离WEB环境就会出现出现ServletRequestAttributes为null的情况。那么如何解决这个问题。

首先快速搭建一个Spring 的运行环境具体jar参照下图:


<dependency>

      <groupId>javax.servlet</groupId>

      <artifactId>servlet-api</artifactId>

      <version>2.5</version>

      <scope>provided</scope>

    </dependency>


    <!--jstl-->

    <dependency>

      <groupId>javax.servlet</groupId>

      <artifactId>jstl</artifactId>

      <version>1.2</version>

    </dependency>


    <!--spring-->

    <!--spring相关依赖-->

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-core</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-context</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-context-support</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-beans</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-aop</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-expression</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-aspects</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-tx</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-web</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>


    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-jdbc</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>


    <!--springmvc-->

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-webmvc</artifactId>

      <version>4.3.2.RELEASE</version>

    </dependency>


    <!--mybatis-->

    <dependency>

      <groupId>org.mybatis</groupId>

      <artifactId>mybatis</artifactId>

      <version>3.2.5</version>

    </dependency>


    <!--mybatis-spring-->


    <dependency>

      <groupId>org.mybatis</groupId>

      <artifactId>mybatis-spring</artifactId>

      <version>1.3.2</version>

    </dependency>


    <!--mysql-->

    <dependency>

      <groupId>mysql</groupId>

      <artifactId>mysql-connector-java</artifactId>

      <version>5.1.38</version>

    </dependency>


    <!--druid-->

    <dependency>

      <groupId>com.alibaba</groupId>

      <artifactId>druid</artifactId>

      <version>1.1.10</version>

    </dependency>

    <dependency>

      <groupId>com.alibaba</groupId>

      <artifactId>fastjson</artifactId>

      <version>1.2.47</version>

    </dependency>

    <!--commons-fileupload-->

    <dependency>

      <groupId>commons-fileupload</groupId>

      <artifactId>commons-fileupload</artifactId>

      <version>1.3</version>

    </dependency>


    <!--jackson-->

    <dependency>

      <groupId>com.fasterxml.jackson.core</groupId>

      <artifactId>jackson-databind</artifactId>

      <version>2.9.5</version>

    </dependency>


    <!--log4j-->

    <dependency>

      <groupId>log4j</groupId>

      <artifactId>log4j</artifactId>

      <version>1.2.16</version>

    </dependency>

    <!--junit测试-->

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.12</version>

      <scope>test</scope>

    </dependency>



首先对前面的开发步骤做一个简单的讲解,首先开发一个自定义注解:

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

//在方法上使用

@Target({ElementType.METHOD})

//类一旦运行  起作用

@Retention(RetentionPolicy.RUNTIME)

public @interface LogAnnotation {

public String value();

}


开发环绕通知(常规做法,下面做修改)

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import org.springframework.web.context.request.RequestContextHolder;

import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpSession;

import java.lang.reflect.Method;

import java.util.Date;

/*

*

* 这是一个环绕通知

* 需要实现

* */

public class Around implements MethodInterceptor {

    @Override

    public Object invoke(MethodInvocation mi) throws Throwable {

        /*

        * 1.什么人--------

        * 2.什么时间 new Date()

        * 3.什么事情----方法名

        * 4.是否成功-----能

        * */

        ServletRequestAttributes s = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        //获取HttpRequst对象,于Servlet强耦合

        HttpSession session = s.getRequest().getSession();

        //拿到存放在Session的数据

        String adminName = (String)session.getAttribute("admin");

        //时间

        Date date = new Date();

        //什么事情

        Method method = mi.getMethod();

        //拿到类对象  反射

        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);

        //通过反射拿到的实例  调用方法

        String name = annotation.value();

        //是否成功

        boolean flag = false;

        Object proceed = null;

        try {

            proceed = mi.proceed();

            flag=true;

        }catch (Exception a){

            a.printStackTrace();

        }

        //这里不做插入数据库的操作

        System.out.println(adminName+"管理员在"+date+"执行了"+name+"成功了么?"+flag);

        return proceed;

    }

}




上述实现的修改(于Servlet弱耦合),利用Spring的DI特性,实现对操作对象的自动注入。

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.Method;

import java.util.Date;

/*

* 这是一个环绕通知

* 需要实现

* */

public class Around implements MethodInterceptor {

    //将操作对象声明为成员变量

    private String adminName;

    //设置公开访问方法,实现未来的数据注入

    public void setAdminName(String adminName) {

        this.adminName = adminName;

    }


    @Override

    public Object invoke(MethodInvocation mi) throws Throwable {

        Date date = new Date();

        //什么事情

        Method method = mi.getMethod();

        //拿到类对象  反射

        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);

        //通过反射拿到的实例  调用方法

        String name = annotation.value();

        //是否成功

        boolean flag = false;

        Object proceed = null;

        try {

            proceed = mi.proceed();

            flag=true;

        }catch (Exception a){

            a.printStackTrace();

        }

        System.out.println(adminName+"管理员在"+date+"执行了"+name+"成功了么?"+flag);

        return proceed;

    }

}



以上将操作数据设定为成员变量,未来我可以在controller层和业务层增加一个过滤器,实现对操作数据的注入。

3. 对通知类使用Spring进行管理


<!--配置组装切面-->

    <aop:config proxy-target-class="true">

        <!--配置切入点-->

        <aop:pointcut id="pc" expression="@annotation(com.baizhi.aop.LogAnnotation)"/>

        <!--组装切面-->

        <aop:advisor advice-ref="around" pointcut-ref="pc"/>

    </aop:config>


过滤器的开发

import com.baizhi.aop.Around;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.context.support.WebApplicationContextUtils;


import javax.servlet.*;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import java.io.IOException;


public class LogFilter implements Filter {

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {


    }


    @Override

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);

        HttpServletRequest res = (HttpServletRequest)servletRequest;

        HttpSession session = res.getSession();

        String admin = (String) session.getAttribute("admin");


        ServletContext servletContext = session.getServletContext();

        //使用这个web工具类拿到WebApplicationContext对象

        WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);

        //能通过WebApplicationContext类拿到已经初始化的Bean

        Around around = (Around) webApplicationContext.getBean("around");

        //通过set方法注入数据

        around.setAdminName(admin);

    }

    @Override

    public void destroy() {


    }

}


5.最后在web.xml中进行配置


 <!--配置过滤器位置-->

<filter>

  <filter-name>log</filter-name>

  <filter-class>com.baizhi.filter.LogFilter</filter-class>

</filter>

  <!--过滤器拦截位置-->

<filter-mapping>

  <filter-name>log</filter-name>

  <url-pattern>/*</url-pattern>

</filter-mapping>

搞定测试,这样做的好处是可以在测试某个模块时,只关注于String类型的name就行了,不必去考虑web的session的获取问题和null值问题


以上是关于如何创建一个与Servlet-api完全解耦和的管理员后台操作日志监控的主要内容,如果未能解决你的问题,请参考以下文章

工厂模式

工厂模式

立创EDA学习笔记——创建元件符号

java 完全解耦

Struts的学习

Altium Designer 20 ——原理图库元件库介绍以及电阻电容模型创建