在咱们开发的项目中,有人操作系统的时候需要记录操作人在该系统做了哪些操作,最原始的方式就是在每个需要记录的方法最后写插入日志的方法。首先他是可以实现我们的需求。但是有两个劣势:
1、书写的代码比较多。每个方法都需要写插入日志的方法。都需要我们去new 对象。
2、写上的代码维护起来比较麻烦。修改一个日志类属性,每个方法都需要修改。
现在我们使用Spring 他的一个特性有一个是aop。下面来自百度引用:
什么是AOP
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
总之:aop 这个思想可以应用到我们的记录日志中。当我们执行完一个添加用户操作后,我们可以将操作相关信息记录下来。通过aop 动态绑定。由代理对象来实现我们的操作。
1、自定义注解userLog
package com.dairuijie.core.aoplog; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * * <p>Title: UserLog</p> * <p>Description:日志注解在对应的方法上写上注解就可以实现日志的记录 </p> * @author dairuijie * @date 2018年3月11日 */ @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface UserLog { String oprName() default ""; Class remarkClass() default UserLog.class; }
2、编写切面类也就是代理类
package com.dairuijie.core.aoplog; import java.util.Date; import javax.ejb.SessionSynchronization; import javax.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON; import com.yunjie.core.util.SessionUtils; import com.yunjie.core.util.TimeToFormat; import com.yunjie.person.pojo.LogRem; import com.yunjie.person.service.LogRemService; /** * * <p>Title: UserLogAspect</p> * <p>Description:日志切面类 </p> * @author dairuijie * @date 2018年3月11日 */ @Component @Aspect public class UserLogAspect { @Autowired private LogRemService logRemSerice; private static final Logger log = LoggerFactory.getLogger(UserLogAspect.class); @After(value = "@annotation(userLog)") public void saveUserLog(JoinPoint joinPoit, UserLog userLog) throws Exception { LogRem logrem = new LogRem(); for (Object object : joinPoit.getArgs()) {//获取操作方法中的参数 if (object.getClass().equals(userLog.remarkClass())) { logrem.setRemark(JSON.toJSONString(object) + "|" + userLog.remarkClass());//备注信息(操作对象json数据,以及该方法所在类全路径名称class com.yunjie.person.controller.AuthController) }else{ logrem.setRemark(userLog.remarkClass().toString());//该方法所在类全路径名称class com.yunjie.person.controller.AuthController) } if (object instanceof HttpServletRequest) { logrem.setLoginIp(((HttpServletRequest) object).getRemoteAddr());//获取请求的ip地址 } } logrem.setLoginName(SessionUtils.getUser().getLoginname());//登录名 logrem.setUserName(SessionUtils.getUser().getUsername());//用户名 logrem.setOperateType(userLog.oprName());//操作的方法名称 logrem.setRemdate(TimeToFormat.getNowTime());//操作的时间 logRemSerice.insert(logrem);//插入到log 日志表 log.info(userLog.remarkClass()+userLog.oprName());//输出到tomcat日志,后续有问题,查找方便 } }
3、Spring配置文件开启切面编程
<!-- 开启切面编程(通过配置织入@Aspectj切面 ) --> <aop:aspectj-autoproxy/>
<aop:aspectj-autoproxy />有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为<aop:aspectj-autoproxy poxy-target-class="true"/>时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。
4、测试类(这个是一个修改模块权限的方法)
@RequestMapping("/edit") @ResponseBody @UserLog(oprName="修改权限",remarkClass=AuthController.class) public Map<String, Object> editSysAuth(@Valid final Auth vo, HttpServletResponse response, final HttpServletRequest request) { OperateTemplete templete = new HttpTemplete(request) { protected void doSomething() throws BaseException { authService.update(vo); } }; return templete.operate(); }
由于目标类没有实现接口所以使用的是CGLib动态代理。
5、数据库日志记录截图
总结:实现Spring aop切面的方法通过查阅资料发现有:
配置可以通过xml文件来进行,大概有四种方式:
1. 配置ProxyFactoryBean,显式地设置advisors, advice, target等
2. 配置AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象
3. 通过<aop:config>来配置
4. 通过<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点
也可以直接使用ProxyFactory来以编程的方式使用Spring AOP,通过ProxyFactory提供的方法可以设置target对象, advisor等相关配置,最终通过 getProxy()方法来获取代理对象