本步骤只记录完成切面所需的必要代码
本人开发中遇到的问题:
切面一直切不进去,最后发现需要在springMVC的核心配置文件中中开启注解驱动才可以,只在spring的核心配置文件中开启是不会在web项目中生效的。
之后按照下面的代码进行配置,然后前端在访问controller层中的路径时即可观察到日志已经被正常记录到数据库,代码中有部分注释,看不懂的可以参照注释。接下来进入正题
1、导入maven坐标
<!--切面-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- AspectJ Runtime -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
2、核心文件配置,在springMVC.xml文件中开启AOP切面注解驱动
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- 开启注解驱动-->
<context:annotation-config/>
<!-- 开启组件扫描-->
<context:component-scan base-package="com.xszx"></context:component-scan>
<!-- 开启切面注解驱动-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
3、配置web.xml文件,指定spring容器的配置文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置一个全局的参数:指定spring容器的配置文件 ServletContext-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!--解决中文乱码的filter一定要放在最前面 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 配置encoding,告诉我们指定的编码格式 -->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<!--是否强制设置request的编码为encoding,默认false,不建议更改-->
<param-name>forceRequestEncoding</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<!--是否强制设置response的编码为encoding,建议设置为true,下面有关于这个参数的解释-->
<param-name>forceResponseEncoding</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置监听器:创建spring容器对象-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--定义一个request监听器-->
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--指定配置文件的路径-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:SpringMVC.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
4、切面类
@Component
@Aspect
public class LogAop {
@Resource
private LoginUserCountService loginUserCountService;
@Autowired
private HttpServletRequest request;
@Autowired
private ISysLogService sysLogService;
private Date visitTime; //开始时间
private Class clazz; //访问的类
private Method method;//访问的方法
//前置通知 主要是获取开始时间,执行的类是哪一个,执行的是哪一个方法
@Before("execution(public * com.xszx.controller.*.*(..))")
public void doBefore(JoinPoint jp) {
try {
visitTime = new Date(); // 当前时间就是开始访问的时间
clazz = jp.getTarget().getClass(); // 具体要访问的类
String methodName = jp.getSignature().getName(); // 获取访问的方法的名称
Object[] args = jp.getArgs(); // 获取访问的方法的参数
// 获取具体执行的方法的Method对象
if (args == null || args.length == 0) {
method = clazz.getMethod(methodName); // 只能获取无参数的方法
} else {
Class[] classArgs = new Class[args.length];
for (int i = 0; i < args.length; i++) {
classArgs[i] = args[i].getClass();
}
method = clazz.getMethod(methodName, classArgs);
}
} catch (NoSuchMethodException e) {
// e.printStackTrace();
} catch (Exception e) {
// e.printStackTrace();
}
}
//后置通知
@After("execution(* com.xszx.controller.*.*(..))")
public void doAfter(JoinPoint jp) {
try {
long time = new Date().getTime() - visitTime.getTime(); // 获取访问的时长
String url = "";
// 获取url
if (clazz != null && method != null && clazz != LogAop.class) {
RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
if (methodAnnotation != null) {
String[] methodValue = methodAnnotation.value();
url = methodValue[0];
// 获取访问的ip
String ip = request.getRemoteAddr();
// 登录时将用户信息存入session,现在从session中获取当前操作的用户
HttpSession httpSession = request.getSession();
User user = (User)httpSession.getAttribute("loginUser");
String userEmail = "";
if (user == null){
userEmail = "无登录用户";
}else {
userEmail = user.getEmail();
}
// 将日志相关信息封装到SysLog对象
SysLog sysLog = new SysLog();
sysLog.setExecutionTime(time); // 执行时长
sysLog.setIp(ip);
sysLog.setMethod("[类名] " + clazz.getName() + "[方法名] " + method.getName());
sysLog.setUrl(url);
sysLog.setUsername(userEmail);
sysLog.setVisitTime(visitTime);
// 调用Service完成操作
sysLogService.save(sysLog);
}
}
} catch (Exception e) {
// e.printStackTrace();
}
}
}
5、beans层
public class SysLog {
private Integer id;
private Date visitTime;
private String visitTimeStr;
private String username;
private String ip;
private String url;
private Long executionTime;
private String method;
//记得写getter,setter方法,构造方法,toString方法
}
6、dao层
public interface ISysLogMapper {
void save(SysLog sysLog) throws Exception;
List<SysLog> findAll() throws Exception;
}
7、service层
@Service
public class SysLogServiceImpl implements ISysLogService {
private SqlSessionTemplate sqlSessionTemplate;
@Autowired
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public List<SysLog> findAll() throws Exception {
ISysLogMapper iSysLogMapper = sqlSessionTemplate.getMapper(ISysLogMapper.class);
return iSysLogMapper.findAll();
}
@Override
public void save(SysLog sysLog) throws Exception {
ISysLogMapper iSysLogMapper = sqlSessionTemplate.getMapper(ISysLogMapper.class);
iSysLogMapper.save(sysLog);
}
}
8、mapper层
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xszx.dao.ISysLogMapper">
<insert id="save">
insert into syslog(visitTime,username,ip,url,executionTime,method)
values(#{visitTime},#{username},#{ip},#{url},#{executionTime},#{method})
</insert>
<select id="findAll" resultType="com.xszx.beans.SysLog">
select * from sysLog
</select>
</mapper>
9、数据库表结构
执行完后进行测试即可,当前扫描的路径是controller下的所有方法,所以随便进入一个即可打印出对于的日志记录。