场景:在用户每次调用Service接口的时候,要求系统自动保存一条操作记录到数据库。记录的内容为:用户名 操作内容 操作时间内
现在,我们就来实现这个场景,步骤如下
1、数据库创建日志记录表
CREATE TABLE t_log (
id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
username varchar(100) comment '用户名',
createdate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建日期',
operation varchar(1000) NOT NULL DEFAULT '' COMMENT '用户所做的操作'
) comment '日志';
2、SSH框架中配置该日志记录表实体映射关系
实体类TLog.java:
package edu.po;
import java.sql.Timestamp;
/**
* TLog entity. @author MyEclipse Persistence Tools
*/
public class TLog implements java.io.Serializable {
// Fields
private Integer id;
private String username;
private Timestamp createdate;
private String operation;
// Constructors
/** default constructor */
public TLog() {
}
/** full constructor */
public TLog(String username, Timestamp createdate, String operation) {
this.username = username;
this.createdate = createdate;
this.operation = operation;
}
// Property accessors
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public Timestamp getCreatedate() {
return this.createdate;
}
public void setCreatedate(Timestamp createdate) {
this.createdate = createdate;
}
public String getOperation() {
return this.operation;
}
public void setOperation(String operation) {
this.operation = operation;
}
}
实体类和数据库表映射关系TLog.hbm.xml:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
<class name="edu.po.TLog" table="t_log" catalog="test">
<id name="id" type="java.lang.Integer">
<column name="id" />
<generator class="native" />
</id>
<property name="username" type="java.lang.String">
<column name="username" length="100" >
<comment>用户名</comment>
</column>
</property>
<property name="createdate" type="java.sql.Timestamp">
<column name="createdate" length="0" not-null="true">
<comment>创建日期</comment>
</column>
</property>
<property name="operation" type="java.lang.String">
<column name="operation" length="400" not-null="true">
<comment>用户所做的操作</comment>
</column>
</property>
</class>
</hibernate-mapping>
在applicationContext.xml配置文件的SessionFactory bean中添加TLog.hbm.xml文件的路径配置,如下:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!--
添加hibernate配置
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
-->
<property name="dataSource" ref="dataSource">
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
<!--
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
-->
<!--
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
-->
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>edu/po/TLog.hbm.xml</value>
</list>
</property>
</bean>
3、保存日志实体的Dao
ILogDao.java:
package edu.dao;
import edu.po.TLog;
public interface ILogDao {
public void save(TLog log) throws Exception;
}
LogDaoImpl.java:
package edu.dao.impl;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import edu.dao.ILogDao;
import edu.po.TLog;
public class LogDaoImpl extends HibernateDaoSupport implements ILogDao {
@Override
public void save(TLog log) throws Exception {
this.getHibernateTemplate().save(log);
}
}
4、写Aspect切面类
ILogAspect.java:
package edu.log;
import org.aspectj.lang.JoinPoint;
public interface ILogAspect {
/**
* 有参无返回值的方法
* @param point
*/
public void logArg(JoinPoint point);
/**
*有参并有返回值的方法
* @param point
* @param returnObj
*/
public void logArgAndReturn(JoinPoint point, Object returnObj);
}
LogAspectImpl.java:
package edu.log.impl;
import java.sql.Timestamp;
import java.util.Date;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import utils.JsonUtils;
import utils.StringUtils;
import edu.dao.ILogDao;
import edu.log.ILogAspect;
import edu.po.TLog;
import edu.threadlocal.LocalRequestContextHolder;
import edu.vo.UserVO;
public class LogAspectImpl implements ILogAspect {
private Logger logger = Logger.getLogger(LogAspectImpl.class);
private ILogDao logDao;
private TLog parseJoinPoint(JoinPoint point) {
StringBuilder operator = new StringBuilder();
TLog logEntity = new TLog();
//当前用户信息
UserVO currentUser = LocalRequestContextHolder.getLocalRequestContext().getCurrentUser();
if(currentUser != null){
operator.append("用户:" + currentUser.getUsername() + ",");
logEntity.setUsername(currentUser.getUsername());
}
//操作
operator.append("操作:");
operator.append(point.getTarget().getClass().getName())
.append(".")
.append(point.getSignature().getName())
.append("方法,");
operator.append("参数:");
Object[] args = point.getArgs();
if (args != null) {
operator.append("(");
int i = 1;
for (Object obj : args) {
operator.append(obj.getClass()).append(" : ").append(JsonUtils.converToJsonStr(obj));
if(i == args.length){
break;
}
operator.append(",");
i++;
}
operator.append(")");
}
logEntity.setOperation(operator.toString());
logEntity.setCreatedate(new Timestamp(new Date().getTime()));
return logEntity;
}
/**
* 有参无返回值的方法
*
* @param point
*/
@Override
public void logArg(JoinPoint point) {
TLog logEntity = parseJoinPoint(point);
save(logEntity);
logger.info(logEntity.getOperation());
}
/**
* 有参并有返回值的方法
*
* @param point
* @param returnObj
*/
@Override
public void logArgAndReturn(JoinPoint point, Object returnObj) {
TLog logEntity = parseJoinPoint(point);
StringBuilder opeartor = new StringBuilder();
opeartor.append(logEntity.getOperation())
.append("执行结果是:")
.append(returnObj);
logEntity.setOperation(opeartor.toString());
save(logEntity);
logger.info(logEntity.getOperation());
}
private boolean save(TLog log) {
// TODO Auto-generated method stub
boolean success = true;
try{
this.logDao.save(log);
}catch(Exception e){
success = false;
logger.info(StringUtils.getExceptionMessage(e));
}
return success;
}
public ILogDao getLogDao() {
return logDao;
}
public void setLogDao(ILogDao logDao) {
this.logDao = logDao;
}
}
LocalRequestContextHolder的实现见博客:通过ThreadLocal和Filter实现请求上下文【更新】
5、Spring的applicationContext.xml中进行AOP配置
在applicationContext.xml文件中添加以下配置:
<bean id="logDao" class="edu.dao.impl.LogDaoImpl" scope="singleton">
<property name="sessionFactory" ref="sessionFactory">
</property>
</bean>
<!--利用AOP添加日志记录 -->
<bean id="logAspectClass" class="edu.log.impl.LogAspectImpl">
<property name="logDao" ref="logDao"></property>
</bean>
<aop:config>
<aop:pointcut id="logPointcut" expression="execution(* edu.service.impl.*.*(..))"/>
<aop:aspect id="logAspect" ref="logAspectClass">
<aop:after method="logArg" pointcut-ref="logPointcut"/>
<!--
<aop:after-returning method="logArgAndReturn" arg-names="point,returnObj" returning="returnObj" pointcut-ref="logPointcut"/>
-->
</aop:aspect>
</aop:config>
项目demo: https://github.com/zengyh/SSHWebProject.git