AOP的相关术语:
1. 通知(Advice): 通知定义了切面是什么以及何时使用。描述了切面要完成的工作和何时需要执行这个工作。
2. 连接点(Joinpoint): 程序能够应用通知的一个“时机”,这些“时机”就是连接点,例如方法被调用时、异常被抛出时等等。
3. 切入点(Pointcut) 通知定义了切面要发生的“故事”和时间,那么切入点就定义了“故事”发生的地点,例如某个类或方法的名称,Spring中允许我们
方便的用正则表达式来指定
4. 切面(Aspect) 通知和切入点共同组成了切面:时间、地点和要发生的“故事”
5. 引入(Introduction) 引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)
6. 目标(Target) 即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做
的事)
7. 代理(proxy) 应用通知的对象,详细内容参见设计模式里面的代理模式
8. 织入(Weaving)
把切面应用到目标对象来创建新的代理对象的过程,织入一般发生在如下几个时机:
1. 编译时:当一个类文件被编译时进行织入,这需要特殊的编译器才可以做的到,例如AspectJ的织入编译器
2. 类加载时:使用特殊的ClassLoader在目标类被加载到程序之前增强类的字节代码
3. 运行时:切面在运行的某个时刻被织入,SpringAOP就是以这种方式织入切面的,原理应该是使用了JDK的动态代理技术
Spring提供了4种实现AOP的方式:
1. 经典的基于代理的AOP
2. @AspectJ注解驱动的切面
3. 纯POJO切面
4. 注入式AspectJ切面
经典的基于代理的AOP:
Spring支持五种类型的通知:
1. Before(前) org.apringframework.aop.MethodBeforeAdvice
2. After-returning(返回后) org.springframework.aop.AfterReturningAdvice
3. After-throwing(抛出后) org.springframework.aop.ThrowsAdvice
4. Arround(周围) org.aopaliance.intercept.MethodInterceptor
5. Introduction(引入) org.springframework.aop.IntroductionInterceptor
具体实现
第一步自定义注解类
/**
*自定义注解 拦截Controller
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface SystemControllerLog {
String description() default “”;
String className() default “”;//用于判断操作哪个实体类
String actionType() default “”;//1 添加 2 修改 3 删除
}
/**
*自定义注解 拦截service
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SystemServiceLog {
String description() default “”;
}
第二步创建数据库表和切入类
1. 操作日志记录表
create table ELACTIONLOG ( id NUMBER(18),
requestip VARCHAR2(50),– 操作IP
type VARCHAR2(3),– 操作类型 1 操作记录2异常记录
userid NUMBER(18),– 操作人ID
description VARCHAR2(4000),– 操作描述
actiondate DATE,– 操作时间
exceptioncode VARCHAR2(50),– 异常code
exceptiondetail VARCHAR2(4000),– 异常详情
actionmethod VARCHAR2(500),– 请求方法
params CLOB – 请求参数
)
2. 切入类
@Aspect
@Component
public class SystemLogAspect {
//注入Service用于把日志保存数据库
@Resource
private ActionLogService actionLogService;
//本地异常日志记录对象
private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);
//Service层切点
@Pointcut(“@annotation(com.beok.elife.admin.struts2.action.SystemServiceLog)”)
public void serviceAspect() {
}
//Controller层切点
@Pointcut(“@annotation(com.beok.elife.admin.struts2.action.SystemControllerLog)”)
public void controllerAspect() {
}
/**
* 前置通知 用于拦截Controller层记录用户的操作
*
* @param joinPoint 切点
*/
@Before(“controllerAspect()”)
public void doBefore(JoinPoint joinPoint) {
System.out.println(“controllerAspect———-doBefore”);
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
//读取session中的用户
SysAdmin admin = (SysAdmin) session.getAttribute(Constants.SESSION_ADMIN);
//请求的IP
String ip = request.getRemoteAddr();
String params = “”;
try {
String methodDescription = getControllerMethodDescription(joinPoint);
String [] am = methodDescription.split(“;”);
String actionType = am[2];
if(!actionType.equals(“1”)){
//记录修改或删除前数据
if(am[1].equals(“Bluetooth”)){
Long bluId=null;
if(actionType.equals(“2”)){//修改
bluId=Long.valueOf(request.getParameter(“bluetooth.id”));
}else if(actionType.equals(“3”)){//删除
bluId=Long.valueOf(request.getParameter(“id”));
}
Bluetooth blu = (Bluetooth) actionLogService.load(Bluetooth.class, bluId);
if(blu!=null){
params += “修改或删除前数据:”+JSONObject.toJSONString(blu) + “;”;
}
}
}
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for ( int i = 0; i < joinPoint.getArgs().length; i++) {
params += JSONObject.toJSONString(joinPoint.getArgs()[i]) + “;”;
}
}
/========控制台输出=========/
System.out.println(“=====前置通知开始=====”);
System.out.println(“请求方法:” + (joinPoint.getTarget().getClass().getName() + “.” + joinPoint.getSignature().getName() + “()”));
System.out.println(“方法描述:” + methodDescription);
System.out.println(“请求人:” + admin.getLoginName());
System.out.println(“请求IP:” + ip);
/========数据库日志=========/
ActionLog actionLog = new ActionLog();
actionLog.setDescription(methodDescription);
actionLog.setActionMethod((joinPoint.getTarget().getClass().getName() + “.” + joinPoint.getSignature().getName() + “()”));
actionLog.setType(“1”);
actionLog.setRequestIp(ip);
actionLog.setExceptionCode( null);
actionLog.setExceptionDetail( null);
actionLog.setParams(params);
actionLog.setUserId(admin.getId());
actionLog.setActionDate(new Date());
/保存数据库,这里不能直接使用actionLogService.create(actionLog);需要在service类新建方法actionLogService.addActionLog(actionLog);/
actionLogService.addActionLog(actionLog);
System.out.println(“=====前置通知结束=====”);
} catch (Exception e) {
//记录本地异常日志
logger.error(“==前置通知异常==”);
logger.error(“异常信息:{}”, e.getMessage());
}
}
@AfterReturning(“serviceAspect()”)
public void doAfterReturning(JoinPoint joinPoint){
System.out.println(“doAfterReturning——-“);
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
//读取session中的用户
SysAdmin admin = (SysAdmin) session.getAttribute(Constants.SESSION_ADMIN);
//请求的IP
String ip = request.getRemoteAddr();
String params = “”;
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for ( int i = 0; i < joinPoint.getArgs().length; i++) {
params += JSONObject.toJSONString(joinPoint.getArgs()[i]) + “;”;
}
}
System.out.println(params);
try {
String mthodDescription = getServiceMthodDescription(joinPoint);
/========控制台输出=========/
System.out.println(“=====后置返回通知开始=====”);
System.out.println(“请求方法:” + (joinPoint.getTarget().getClass().getName() + “.” + joinPoint.getSignature().getName() + “()”));
System.out.println(“方法描述:” + mthodDescription);
System.out.println(“请求人:” + admin.getLoginName());
System.out.println(“请求IP:” + ip);
/========数据库日志=========/
ActionLog actionLog = new ActionLog();
actionLog.setDescription(mthodDescription);
actionLog.setActionMethod((joinPoint.getTarget().getClass().getName() + “.” + joinPoint.getSignature().getName() + “()”));
actionLog.setType(“1”);
actionLog.setRequestIp(ip);
actionLog.setExceptionCode( null);
actionLog.setExceptionDetail( null);
actionLog.setParams(params);
actionLog.setUserId(admin.getId());
actionLog.setActionDate(new Date());
/保存数据库,这里不能直接使用actionLogService.create(actionLog);需要在service类新建方法actionLogService.addActionLog(actionLog);/
actionLogService.addActionLog(actionLog);
System.out.println(“=====后置返回通知结束=====”);
} catch (Exception e) {
//记录本地异常日志
logger.error(“==后置返回通知异常==”);
logger.error(“异常信息:{}”, e.getMessage());
}
}
/**
* 异常通知 用于拦截service层记录异常日志
*
* @param joinPoint
* @param e
*/
@AfterThrowing(pointcut = “serviceAspect()”, throwing = “e”)
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println(“serviceAspect———-doAfterThrowing”);
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
//读取session中的用户
SysAdmin admin = (SysAdmin) session.getAttribute(Constants.SESSION_ADMIN);
//获取请求ip
String ip = request.getRemoteAddr();
//获取用户请求方法的参数并序列化为JSON格式字符串
String params = “”;
if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
for ( int i = 0; i < joinPoint.getArgs().length; i++) {
params += JSONObject.toJSONString(joinPoint.getArgs()[i]) + “;”;
}
}
try {
/========控制台输出=========/
System.out.println(“=====异常通知开始=====”);
System.out.println(“异常代码:” + e.getClass().getName());
System.out.println(“异常信息:” + e.getMessage());
System.out.println(“异常方法:” + (joinPoint.getTarget().getClass().getName() + “.” + joinPoint.getSignature().getName() + “()”));
System.out.println(“方法描述:” + getServiceMthodDescription(joinPoint));
System.out.println(“请求人:” + admin.getLoginName());
System.out.println(“请求IP:” + ip);
System.out.println(“请求参数:” + params);
/==========数据库日志=========/
ActionLog log = new ActionLog();
log.setDescription(getServiceMthodDescription(joinPoint));
log.setExceptionCode(e.getClass().getName());
log.setType(“2”);
log.setExceptionDetail(e.getMessage());
log.setActionMethod((joinPoint.getTarget().getClass().getName() + “.” + joinPoint.getSignature().getName() + “()”));
log.setParams(params);
log.setUserId(admin.getId());
log.setActionDate(new Date());
log.setRequestIp(ip);
/保存数据库,这里不能直接使用actionLogService.create(actionLog);需要在service类新建方法actionLogService.addActionLog(actionLog);/
actionLogService.addActionLog(log);
System.out.println(“=====异常通知结束=====”);
} catch (Exception ex) {
//记录本地异常日志
logger.error(“==异常通知异常==”);
logger.error(“异常信息:{}”, ex.getMessage());
}
/==========记录本地异常日志==========/
//logger.error(“异常方法:{}异常代码:{}异常信息:{}参数:{}”, joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), }
/**
* 获取注解中对方法的描述信息 用于service层注解
*
* @param joinPoint 切点
* @return 方法描述
* @throws Exception
*/
public static String getServiceMthodDescription(JoinPoint joinPoint)
throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = “”;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(SystemServiceLog. class).description();
break;
}
}
}
return description;
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param joinPoint 切点
* @return 方法描述
* @throws Exception
*/
public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
Class targetClass = Class.forName(targetName);
Method[] methods = targetClass.getMethods();
String description = “”;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
Class[] clazzs = method.getParameterTypes();
if (clazzs.length == arguments.length) {
description = method.getAnnotation(SystemControllerLog. class).description()+”;”;
description += method.getAnnotation(SystemControllerLog. class).className()+”;”;
description += method.getAnnotation(SystemControllerLog. class).actionType();
break;
}
}
}
return description;
}
}
第三步把Controller的代理权交给cglib
在实例化ApplicationContext的时候需要加上
Xml代码
在调用Controller的时候AOP发挥作用所以在SpringMVC的配置文件里加上
Xml代码
Spring AOP 实现系统操作日志记录
最新推荐文章于 2024-07-16 11:12:33 发布