spring boot使用filter(过滤器)和listener(监听器)和多线程 实现系统的功能访问后日志保存操作

1.使用背景:

项目需要实现用户访问的功能菜单进行日志记录登记。

2.整体思路

1.获取到所有的请求(这里使用过滤器获取用户请求)。例如:请求:http://127.0.0.1/xxxx.yyyyyy.do?xxx=xxx  
2.配置需要记录的功能和请求map[格式:请求=功能名称](这里使用监听器监听应用启动完后加载配置文件内容放入全局MAP中)。
例如:yyyyyy.do =系统配置
3.通过请求和配置的记录信息判断是否为需要记录的功能,符合则将其保存入库(这里开启多线程,保存记录)。

3.基础知识

参考:添加链接描述

4.功能实现

  • 定义过滤器
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.Date;

public class FunctionLogFilter implements Filter {
    /**
     * 初始化
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    /**
     * 过滤逻辑
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 过滤用户请求,判断是否登录
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;//转为HttpServletRequest 
        HttpServletResponse httpServletResponse = (HttpServletResponse)response;//转为HttpServletResponse
        httpServletResponse.setContentType("text/html;charset=utf-8");
        httpServletRequest.setCharacterEncoding("utf-8");
        httpServletResponse.setCharacterEncoding("utf-8");
        //1.获取当前的登录用户
        OpuOmUser loginUser = SecurityContext.getCurrentUser();
        //2.获取访问请求方法
        String functionUrl = httpServletRequest.getServletPath();
        //3.处理时间
        Timestamp dealTime = new Timestamp(new Date().getTime());
        //4.逻辑判断保存
        ThreadPoolUtil.startCallable1(dealTime,functionUrl,loginUser,httpServletRequest);
        chain.doFilter(request, response);
    }

    /**
     * 銷毀
     */
    @Override
    public void destroy() {

    }
}

  • 将过虑器添加至springMVC配置文件中
/**
     * 注册过滤器
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public FilterRegistrationBean filterRegist() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new FunctionLogFilter());
        filterRegistrationBean.addUrlPatterns("/*");
        return filterRegistrationBean;
    }
  • 定义监听器
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

/**
 * ApplicationStartedEvent:在刷新上下文之后,但在调用任何应用程序和命令行运行程序之前.
 * 加载logurls.properties文件
 */
@Component /*这个要加上 不然spring不认识 就不能使用配置文件中发布的方式*/
public class FunctionLogListener implements ApplicationListener<ApplicationStartedEvent> {
	public FunctionLogListener() {
	}

	@SuppressWarnings("unchecked")
	public void contextInitialized() {

		Properties properties = new Properties();
		InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("logurls.properties");
			if (in == null) {
				System.out.println("没有找到logurls.properties文件!");
			} else {
				try {
					properties.load(new InputStreamReader(in, "utf-8"));
					Set propertiesSet = properties.keySet();
					Iterator ite = propertiesSet.iterator();
					while (ite.hasNext()) {
						String key = ite.next().toString();
						ToolUtils.LOG_URLS_MAP.put(key.replaceAll("logUrls_key_", ""), properties.getProperty(key));//LOG_URLS_MAP 全局变量MAP
					}
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					try {
						if (in != null) {
							in.close();
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		}

	@Override
	public void onApplicationEvent(ApplicationStartedEvent event) {
		contextInitialized();
	}
}

  • 发布监听器(在spring boot配置文件中添加的方式)
#启动功能使用日志监听
#格式:org.springframework.context.ApplicationListener=监听器全类路径
org.springframework.context.ApplicationListener=com.xxx.xxxx.listener.FunctionLogListener

  • 定义线程类
package com.xxx.xxx.thread;

import com.xxx.xxx.framework.security.user.OpuOmUser;
import com.xxx.xxx.domain.XmjgFunctionLog;
import com.xxx.xxx.service.FunctionLogService;
import com.xxx.xxx.service.impl.FunctionLogServiceImpl;
import com.xxx.xxx.util.SpringContextUtil;
import org.apache.commons.lang3.StringUtils;

import java.sql.Timestamp;
import java.util.concurrent.Callable;

/**
 *功能模块操作日志信息保存线程
 */
public class FunctionLogDataCallable implements Callable<Long> {
	private Timestamp dealTime; //操作时间
	private String functionName;// 功能模块名称
	private String functionUrl;// 功能模块访问url
	private String clientIp; //客户端ip
	private OpuOmUser opuOmUser;//用户对象
	//多线程下 无法使用注解方式注入,解决方案为手动获取
	private FunctionLogService functionLogService= SpringContextUtil.getBean("functionLogServiceImpl", FunctionLogServiceImpl.class);

	public FunctionLogDataCallable(Timestamp dealTime, String functionUrl, String functionName, OpuOmUser opuOmUser, String clientIp ) {
        this.dealTime = dealTime;
        this.functionUrl = functionUrl;
        this.functionName = functionName;
        this.clientIp = clientIp;
        this.opuOmUser = opuOmUser;
    }
	@Override
	public Long call() throws Exception {
		XmjgFunctionLog form=new XmjgFunctionLog();
		if(null !=opuOmUser){
			form.setUserLoginName(opuOmUser.getLoginName());
			form.setUserCode(opuOmUser.getUserId());
			form.setUserName(opuOmUser.getUserName());
		}
		form.setDealTime(dealTime);
		if(StringUtils.isNotEmpty(clientIp)) {
			form.setClientIp(clientIp);
		}
		if(StringUtils.isNotEmpty(functionUrl)) {
			form.setFunctionUrl(functionUrl);
		}
		if(StringUtils.isNotEmpty(functionName)) {
			form.setFunctionName(functionName);
		}
		functionLogService.saveFunctionLog(form);
		return 1L;
	}


}

  • 定义线程池和业务逻辑判断工具类
package com.xxx.xxx.util;

import com.xxxx.xxxx.framework.security.user.OpuOmUser;
import com.xxx.xxx.thread.FunctionLogDataCallable;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolUtil {
	private static ExecutorService executorService=null;
	public static void startCallable1(Timestamp dealTime, String functionUrl, OpuOmUser loginUser, HttpServletRequest request) {
		if(loginUser!=null) {
			String clientIp=request.getRemoteAddr();
			startCallable(dealTime,functionUrl,null,loginUser,clientIp);
		}

	}
	
	public static void startCallable(Timestamp dealTime, String functionUrl, String functionName, OpuOmUser loginUser, String clientIp) {
		if(StringUtils.isEmpty(functionName) && StringUtils.isNotEmpty(functionUrl)) {
			functionName=getFunctionNameByUrl(functionUrl);
		}
		if(StringUtils.isNotEmpty(functionName)) {
			getInstance();//初始化线程池
			executorService.submit(new FunctionLogDataCallable(dealTime, functionUrl, functionName,loginUser,clientIp));//线程执行
		}
	}
	
	/**
	 * 初始化线程池
	 */
	private synchronized static void getInstance() {
		if (executorService == null) {
			synchronized(ThreadPoolUtil.class) {
				if (executorService == null) {
					executorService= Executors.newFixedThreadPool(10);
				}
			}
		}
	}
	private static String getFunctionNameByUrl(String functionUrl) {
		if(StringUtils.isNotEmpty(functionUrl) && ToolUtils.LOG_URLS_MAP.containsKey(functionUrl)) {
			return ToolUtils.LOG_URLS_MAP.get(functionUrl);
		}
		return null;
	}


}

解决多线程下无法使用自动注解(@Autowired)问题

  • 原因:每一个新的线程开始,不在spring容器内,所以无法使用

  • 解决方式:手动获取bean,进行注入。

  • 具体实现:

定义一个类去实现ApplicationContextAware 接口获取应用的上下文环境信息,在该类中编写获取应用环境中的bean的方法,并将该类交给spring管理。



import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext applicationContext = null;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(SpringContextUtil.applicationContext == null){
            SpringContextUtil.applicationContext  = applicationContext;
        }
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static Object getBean(String name){
        return getApplicationContext().getBean(name);
    }

    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

    public static <T> T getBean(String name,Class<T> clazz){
        return getApplicationContext().getBean(name, clazz);
    }
}

发布了18 篇原创文章 · 获赞 2 · 访问量 651

猜你喜欢

转载自blog.csdn.net/qq_34699995/article/details/100975751