1.自定义注释
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Log {
LogType type() default LogType.SPACE;
}
2.type值的枚举
public enum LogType {
SPACE(""),
INSERT("增加"),
DELETE("删除"),
UPDATE("修改"),
QUERY("查询");
private String description;
private LogType( String string) {
description=string;
}
public String GetDescription()
{
return description;
}
}
3.注释的使用
public abstract class AbstractService<T, M> implements IService<T, M>
{
@Override
@Log(type = LogType.INSERT)
public int add(T t)
{
return getMapper().insert(t);
}}
4.自定义切面(注意@Aspect 和@Component注解)
@Aspect
@Component
public class OperateLogAspect {
@Autowired
private IOperationLogService operationLogService;
/**
* 后置增强
*/
@After("@annotation(cn.com.reformer.annotation.Log) && @annotation(log)")
private void after(JoinPoint joinPoint, Log log) {
//读取中的用户
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Admin user = (Admin) request.getAttribute(Constants.CURRENT_ADMIN);
//未登录或登录操作
if (null == user) {
return;
}
//没有参数
if (joinPoint.getArgs() == null) {
return;
}
OperationLog operationLog = new OperationLog();
operationLog.setUserId(user.getId());
operationLog.setIpAddr(getIP(request));
operationLog.setOpearteTime(new Date());
Object[] os = joinPoint.getArgs();
//获取类名
String className = joinPoint.getTarget().getClass().getSimpleName();
//获取方法名
String methodName = joinPoint.getSignature().getName();
String param = className + "." + methodName + ":";
for (int i = 0; i < os.length; i++) {
param += "参数[" + i + "]:" + os[i].toString();
}
operationLog.setOperation(log.type().GetDescription());
operationLog.setOperateContent(param);
//写入数据库
operationLogService.insertSelective(operationLog);
return;
}
//获取请求IP
public String getIP(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
</pre><p></p><pre>
5.配置Spring-mvc.xml
proxy-target-class="true"为开启CGLIB动态代理
<!--自定义切面所在包-->
<context:component-scan base-package="cn.com.reformer.service"/>
<!--识别切面,开启CGLIB动态代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
6.此过程中遇到的麻烦
在数据库写操作日志时报read-only的错误,原因事务配置了read-only,如下:
<!-- 事务管理 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 对insert,update,delete 开头的方法进行事务管理,只要有异常就回滚 -->
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="create*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Throwable"/>
<!-- select,count开头的方法,开启只读,提高数据库访问性能 -->
<tx:method name="select*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="count*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<!-- 对其他方法 使用默认的事务管理 -->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
解决方法:
1.此处设置read-only的地方全部都是查询,可以不记录查询日志,及查询方法前不加自定义注解@Log
2.去掉事务的read-only属性