1. spring 配置
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task" xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p" xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.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.cloudtech.web" />
<!-- 开启aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:property-placeholder location="classpath*:/config.properties"
ignore-resource-not-found="true" ignore-unresolvable="true" />
<!-- <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"
/> <property name="locations"> <list> <value>file:${CONF_DOCTOR}/config/jdbc-125.properties</value>
<value>file:${CONF_DOCTOR}/config/config.properties</value> </list> </property>
</bean> -->
<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!-- 数据库驱动 config.decrypt=true -->
<property name="driverClassName" value="${jdbc.driverClassName}" />
<!-- 数据库地址 -->
<property name="url" value="${jdbc.url}" />
<!-- 数据库用户名 -->
<property name="username" value="${jdbc.username}" />
<!-- 数据库密码 -->
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="1" />
<!-- 连接池最大使用连接数量 -->
<property name="maxActive" value="100" />
<!-- 连接池最大空闲 -->
<!-- <property name="maxIdle" value="20" /> -->
<!-- 连接池最小空闲 -->
<property name="minIdle" value="5" />
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="60000" />
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize"
value="33" />
<!-- 用来检测连接是否有效的sql,要求是一个查询语句 -->
<!-- <property name="validationQuery" value="${validationQuery}" /> -->
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="testWhileIdle" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="25200000" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="true" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="1800" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="true" />
<!-- 监控数据库 -->
<property name="filters" value="stat,log4j" />
</bean>
<!-- myBatis文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
<property name="mapperLocations" value="classpath*:/com/cloudtech/web/mapper/*.xml" />
<!-- 要映射类的包路径 -->
<property name="typeAliasesPackage" value="com.cloudtech.web.entity" />
<!-- 通用mapper配置 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<!-- 属性一行一个,具体属性参考mybatis-config.xml中的属性 -->
<value>
mapper=tk.mybatis.mapper.common.Mapper
dialect=mysql
<!-- 主键自增回写方法,默认值MYSQL, -->
identity=mysql
<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
<!-- 和startPage中的pageNum效果一样 -->
offsetAsPageNum=true
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
rowBoundsWithCount=true
</value>
</property>
</bean>
<bean class="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<property name="properties">
<!-- 属性一行一个,具体属性参考mybatis-config.xml中的属性 -->
<value>
mappers=tk.mybatis.mapper.common.Mapper
identity=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
<!-- 以下是Redis连接池配置 -->
<!-- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
最大连接数 <property name="maxActive" value="${redis.pool.maxActive}" /> 最大空闲连接数
<property name="maxIdle" value="${redis.pool.maxIdle}" /> 最大等待时间 <property
name="maxWait" value="${redis.pool.maxWait}" /> 对拿到的connection进行validateObject校验
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> 在进行returnObject对返回的connection进行validateObject校验
<property name="testOnReturn" value="${redis.pool.testOnReturn}" /> </bean>
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool"
scope="singleton"> <constructor-arg index="0" ref="jedisPoolConfig" /> <constructor-arg
index="1"> <list> <bean class="redis.clients.jedis.JedisShardInfo"> <constructor-arg
name="host" value="${redis.host}" /> <constructor-arg name="port" value="${redis.port}"
/> <constructor-arg name="timeout" value="${redis.timeout}" /> <constructor-arg
name="weight" value="1" /> <property name="password" value="${redis.password}"/>
</bean> </list> </constructor-arg> </bean> -->
<!-- 扫描指定包下的Mapper,自动创建mapper bean实例 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.cloudtech.web.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 注解方式配置事物 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<aop:aspectj-autoproxy />
<!-- 注解方式配置定时任务 -->
<task:annotation-driven />
<!-- <task:scheduled-tasks scheduler="myScheduler"> 对账定时任务,每天十点半 cron="0
30 10 ? * *" <task:scheduled ref="taskHandler" method="recontTask" cron="${recont.cron}"/>
</task:scheduled-tasks> <task:scheduler id="myScheduler" pool-size="10"/> -->
<!-- 注解方式配置缓存 -->
<!-- <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="false"
/> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="WXpayConfig" /> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="AlipayConfig" /> </set> </property> </bean> -->
<!-- <import resource="applicationContext-quartz.xml"/> -->
<import resource="dubbo-provide.xml"/>
</beans>
导入aop命令空间和aop注解
package com.cloudtech.web.aop;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.cloudtech.web.dao.AdminMapper;
import com.cloudtech.web.dao.AuthorityMapper;
import com.cloudtech.web.dao.OperationDetailLogsMapper;
import com.cloudtech.web.dao.OperationLogsMapper;
import com.cloudtech.web.dao.OperatorMapper;
import com.cloudtech.web.dao.RoleMapper;
import com.cloudtech.web.entity.Admin;
import com.cloudtech.web.entity.Authority;
import com.cloudtech.web.entity.OperationDetailLogs;
import com.cloudtech.web.entity.OperationLogs;
import com.cloudtech.web.entity.Operator;
import com.cloudtech.web.entity.Role;
import com.cloudtech.web.enums.ChildModuleType;
import com.cloudtech.web.enums.OperationType;
import com.cloudtech.web.enums.ParentModuleType;
import com.cloudtech.web.vo.Principal;
/**
*
* @ClassName: LogAspect
* @Description: 日志操作记录
* @author wude
* @date 2018年7月19日
*
*/
@Aspect
@Component
public class LogAspect {
/**
* @serial 日志.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
@Autowired
private OperationLogsMapper logsMapper;
@Autowired
private OperationDetailLogsMapper detailLogsMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private AuthorityMapper authorityMapper;
@Autowired
private OperatorMapper operatorMapper;
@Autowired
private AdminMapper adminMapper;
/**
* 角色日志修改和新增操作日志记录
* @param point
* @throws Throwable
*/
@SuppressWarnings("null")
@Around("execution(* com.cloudtech.web.service.impl.RoleServiceImpl.insertOrUpdate(..))")
public Object processInsertRoleConfig(ProceedingJoinPoint point) throws Throwable {
Object rvt = null;
Role role = null;
Principal principal = null; //新增
OperationType type = null; //操作类型
String title = ""; //标题
// 访问目标方法的参数:
Object[] args = point.getArgs(); // point.getArgs() 获得执行参数
//父模块id
Integer module = ParentModuleType.ADMIN_MANGGER.getCode();
//子模块id
Integer subModule = ChildModuleType.ROLE_LIST.getCode();
if (args != null && args.length > 0) {
role = (Role) args[0];
principal = (Principal) args[1];
type = (OperationType) args[2];
if(type == OperationType.ADD) { //新增
rvt = point.proceed();
LOGGER.debug("新增角色操作日志记录: " + role.toString());
if(role.getId() != null){
//插入日志表
title = "角色信息新增";
OperationLogs logs = insertLog(title, principal, type,module,subModule);
//插入日志详情表-----角色名称
insertRoleDetailLog(logs, null, role, 1, type);
//插入日志详情表-----角色描述
insertRoleDetailLog(logs, null, role, 2, type);
}
}
Role beforeRole = null; //修改之前的role
if(type == OperationType.UPDATE) { //修改
beforeRole = roleMapper.selectByPrimaryKey(role.getId());
LOGGER.debug("修改角色操作日志记录: " + role.toString());
rvt = point.proceed();
boolean flag = false;
boolean nameUpdate = false; //名字修改
boolean descUpdate = false; //描述 修改
if (!beforeRole.getName().equals(role.getName())) {
flag = true;
nameUpdate = true;
}
if (!beforeRole.getDescription().equals(role.getDescription())) {
flag = true;
descUpdate = true;
}
if(flag){ //修改过
title = "角色信息修改";
OperationLogs logs = insertLog(title, principal, type,module,subModule);
if(nameUpdate){ //名字修改
//插入日志详情表-----角色名称
insertRoleDetailLog(logs, beforeRole, role, 1, type);
}
if(descUpdate){ //描述 修改
//插入日志详情表-----角色描述
insertRoleDetailLog(logs, beforeRole, role, 2, type);
}
}
}
}
return rvt;
}
/**
* 角色模块删除操作日志
*
* @param point
* @return
* @throws Throwable
*/
@Around("execution(* com.cloudtech.web.service.impl.RoleServiceImpl.delete(..))")
public Object processDeleteRoleLog(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
Integer id = null;
Object rvt = null;
Principal principal = null; // 新增
OperationType type = null; // 操作类型
String title = ""; // 标题
Role beforeRole = null;
// 父模块id
Integer module = ParentModuleType.ADMIN_MANGGER.getCode();
// 子模块id
Integer subModule = ChildModuleType.ROLE_LIST.getCode();
if (args != null && args.length > 0) {
id = (Integer) args[0];
principal = (Principal) args[1];
type = (OperationType) args[2];
beforeRole = roleMapper.selectByPrimaryKey(id);
rvt = point.proceed();
Role role = roleMapper.selectByPrimaryKey(id);
if(role == null){
LOGGER.debug("删除角色操作日志记录: 角色id为" + id);
// 插入日志表
title = "角色信息删除";
OperationLogs logs = insertLog(title, principal, type, module, subModule);
// 角色名称
insertRoleDetailLog(logs, beforeRole, null, 1, type);
// 角色描述
insertRoleDetailLog(logs, beforeRole, null, 2, type);
}
}
// 用改变后的参数执行目标方法
return rvt;
}
}
注意:因为我这里新增和修改使用的是同一个方法,所以,这里有一个难点,修改需要区别修改前和修改后的值,新增的时候,需要操作成功后才插入对应的值。通过 rvt = point.proceed();实现。通过调用该方法,通知配置切面的对应的方法先运行,假设为新增操作,因为是环绕增强,方法前方法后都会运行,所以,通过该代码,确保添加成功后,才记录对应的日志,也就是上文中的判断id不为空时,才新增。
删除场景:考虑到删除有报错的情况,所以需要判断是否删除成功后,才能记录日志,而,判断删除是否成功的前提,就是环绕增强,先走切面,记录修改前的角色对象,和id,通过 rvt = point.proceed();通过调用该方法,通知配置切面的对应的方法先运行,删除方法跑完后,会继续回调切面方法,这是再更加id查,看是否能查到数据,如果查不到数据,说明删除成功