springAOP面向切面编程之日志记录功能
2016-12-22 10:15
926 查看
关于AOP:
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。
但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。
也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。
AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。
就指是把逻辑代码和处理琐碎事务的代码分离开,以便能够分离复杂度。
让人在同一时间只用思考代码逻辑,或者琐碎事务。
代码逻辑比如是插入一条数据,那么琐碎事务就包括获取连接和关闭连接,事务开始,事务提交。
切面就是指在大堆繁琐事务中的逻辑代码。
把用户对数据的增删改操作及异常信息记录下来。
jdk1.6添加jar包:aspectjweaver_1.6.0.jar,aspectjrt_1.6.0.jar
jdk1.7添加jar包:aspectjweaver_1.7.4.jar,aspectjrt_1.7.4.jar
spring mvc-servlet.xml添加:
在…service中新建LogService.java
在…action中新建afterMethod.java,onMethod.java,booleanMethod.java
数据库中添加一张表用来记录日志信息:
T_login_log 登陆操作记录表
LOG_ID VARCHAR2(32) N 主键
USER_ID VARCHAR2(32) Y 用户ID
USER_NAME VARCHAR2(32) Y 操作人
ACCOUNT VARCHAR2(30) Y 账号
ORG_CODE VARCHAR2(10) Y 机构代码
LOGIN_DATE TIMESTAMP(6) Y 操作日期
EXIT_DATE DATE Y 备用
LOGIN_TYPE VARCHAR2(10) Y 状态
LOGIN_IP VARCHAR2(20) Y 操作人IP
LOGIN_STATUS VARCHAR2(40) Y 操作功能
LOGIN_VOID VARCHAR2(100) Y 操作方法
LOGIN_ERROR VARCHAR2(500) Y 错误信息
LOGIN_DATAID VARCHAR2(200) Y 数据信息
LogService.java:
在需要的方法上面添加注解:
logService
增加了一个@AfterThrowing 方法,针对系统目前有返回值的方法,已可以正常抓取到异常
针对无void的方法需要加入 try ctach 加入
onMethod.java:
这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。
但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。
也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。
AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。
就指是把逻辑代码和处理琐碎事务的代码分离开,以便能够分离复杂度。
让人在同一时间只用思考代码逻辑,或者琐碎事务。
代码逻辑比如是插入一条数据,那么琐碎事务就包括获取连接和关闭连接,事务开始,事务提交。
切面就是指在大堆繁琐事务中的逻辑代码。
把用户对数据的增删改操作及异常信息记录下来。
jdk1.6添加jar包:aspectjweaver_1.6.0.jar,aspectjrt_1.6.0.jar
jdk1.7添加jar包:aspectjweaver_1.7.4.jar,aspectjrt_1.7.4.jar
spring mvc-servlet.xml添加:
<!-- aop --> <bean id="logService" class="...service.LogService"></bean> <!-- 启动对@AspectJ注解的支持 --> <aop:aspectj-autoproxy proxy-target-class="true"/>
在…service中新建LogService.java
在…action中新建afterMethod.java,onMethod.java,booleanMethod.java
数据库中添加一张表用来记录日志信息:
T_login_log 登陆操作记录表
LOG_ID VARCHAR2(32) N 主键
USER_ID VARCHAR2(32) Y 用户ID
USER_NAME VARCHAR2(32) Y 操作人
ACCOUNT VARCHAR2(30) Y 账号
ORG_CODE VARCHAR2(10) Y 机构代码
LOGIN_DATE TIMESTAMP(6) Y 操作日期
EXIT_DATE DATE Y 备用
LOGIN_TYPE VARCHAR2(10) Y 状态
LOGIN_IP VARCHAR2(20) Y 操作人IP
LOGIN_STATUS VARCHAR2(40) Y 操作功能
LOGIN_VOID VARCHAR2(100) Y 操作方法
LOGIN_ERROR VARCHAR2(500) Y 错误信息
LOGIN_DATAID VARCHAR2(200) Y 数据信息
LogService.java:
/** * * @Aspect 实现spring aop 切面(Aspect): * 一个关注点的模块化,这个关注点可能会横切多个对象。事务管理是J2EE应用中一个关于横切关注点的很好的例子。 * 在SpringAOP中,切面可以使用通用类(基于模式的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。 * */ @Component @Aspect public class LogService { @Autowired @Qualifier("genericHibernateDao") private GenericDao genericHibernateDao; public LogService() { //System.out.println("Aop"); } /** * 方法执行时 */ @Pointcut("@annotation(com.atsoft.log.action.onMethod)") public void onMethod() { } /** * 方法执行后 */ @Pointcut("@annotation(com.atsoft.log.action.afterMethod)") public void afterMethod() { } /** * 方法指定参第一个参数为boolean */ @Pointcut("@annotation(com.atsoft.log.action.booleanMethod)") public void booleanMethod() { } /** * 方法执行后 * @param point * @throws Throwable */ @After("afterMethod()") public void after(JoinPoint point) throws Throwable{ String monthRemark = getafterMethod(point); if(!Pub.outofnull(monthRemark).equals("")){ String monthName = point.getSignature().getDeclaringTypeName(); String voidName = point.getSignature().getName(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String remark = Pub.outofnull((String) request.getAttribute("reMark")); if(remark.equals("")){ remark=monthRemark; } String loginDataid = (String) request.getAttribute("dataId"); if(!Pub.outofnull(loginDataid).equals("")){//传回值为空,说明操作未成功,不记录 TBaseUser user =(TBaseUser)request.getSession().getAttribute("user"); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); Calendar ca = Calendar.getInstance(); String operDate = df.format(ca.getTime()); TLoginLog log = new TLoginLog(); log.setLogId(DateUtil.getPKid()); if(user!=null){ log.setUserId(user.getUserid()); log.setUserName(user.getUsername()); log.setAccount(user.getAccount()); log.setOrgCode(Pub.outofnull(user.getOrgCode())); log.setLoginStatus(remark); log.setLoginVoid(monthName+"."+voidName); log.setLoginDataid(Pub.outofnull(loginDataid)); log.setLoginDate(DateUtil.parseDate(operDate,"yyyy-MM-dd HH:mm:ss SSS")); log.setLoginIp(Pub.getIpAddr(request)); this.genericHibernateDao.save(log); } } } } /** * 方法执行时 * @param point * @return * @throws Throwable */ @Around("onMethod()") public Object around(ProceedingJoinPoint point) throws Throwable { String monthRemark = getonMethod(point); if(Pub.outofnull(monthRemark).equals("")){ return ""; } HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String loginDataid = (String) request.getAttribute("dataId"); TBaseUser user =(TBaseUser)request.getSession().getAttribute("user"); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); Calendar ca = Calendar.getInstance(); String operDate = df.format(ca.getTime()); String monthName = point.getSignature().getDeclaringTypeName(); String voidName = point.getSignature().getName(); TLoginLog log = new TLoginLog(); log.setLogId(DateUtil.getPKid()); if(user!=null){ log.setUserId(user.getUserid()); log.setUserName(user.getUsername()); log.setAccount(user.getAccount()); log.setOrgCode(Pub.outofnull(user.getOrgCode())); log.setLoginStatus(monthRemark); log.setLoginVoid(monthName+"."+voidName); log.setLoginDate(DateUtil.parseDate(operDate,"yyyy-MM-dd HH:mm:ss SSS")); log.setLoginIp(Pub.getIpAddr(request)); this.genericHibernateDao.save(log); } /* String packages = point.getThis().getClass().getName(); if (packages.indexOf("$$EnhancerByCGLIB$$") > -1) { // 如果是CGLIB动态生成的类 try { packages = packages.substring(0, packages.indexOf("$$")); } catch (Exception ex) { ex.printStackTrace(); } }*/ String operatingcontent = ""; Object[] method_param = null; Object object; try { method_param = point.getArgs(); //获取方法参数 //String param=(String) point.proceed(point.getArgs()); object = point.proceed(); } catch (Exception e) { // 异常处理记录日志..log.error(e); throw e; } return object; } /** * 抛异常 * @param point * @param ex */ @AfterThrowing(pointcut="execution(* com.atsoft..*.*.*.*(..))",throwing = "ex") //* org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(..) public void afterThrowing(JoinPoint point,Exception ex) { try{ String monthRemark = getafterMethod(point); if(Pub.outofnull(monthRemark).equals("")){ monthRemark=getonMethod(point); } String monthName = point.getSignature().getDeclaringTypeName(); String voidName = point.getSignature().getName(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); TBaseUser user =(TBaseUser)request.getSession().getAttribute("user"); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); Calendar ca = Calendar.getInstance(); String operDate = df.format(ca.getTime()); TLoginLog log = new TLoginLog(); if(user!=null){ log.setUserId(user.getUserid()); log.setUserName(user.getUsername()); log.setAccount(user.getAccount()); log.setOrgCode(Pub.outofnull(user.getOrgCode())); } log.setLogId(DateUtil.getPKid()); log.setLoginType("404"); log.setLoginStatus(monthRemark); log.setLoginVoid(monthName+"."+voidName); log.setLoginDate(DateUtil.parseDate(operDate,"yyyy-MM-dd HH:mm:ss SSS")); log.setLoginIp(Pub.getIpAddr(request)); String message = ex.toString(); if(message.length()>800){ message=message.substring(0,800); } log.setLoginError(message); this.genericHibernateDao.save(log); }catch (Exception e) { e.printStackTrace(); } } /** * 第一个参数为boolean 的所有方法执行,返回结果 * @param point * @param at * @param returnValue * @throws Exception */ @AfterReturning(pointcut="execution(* com.atsoft.*.*.*.*(..)) && args(at,..)",returning="returnValue") public void smsInfo(JoinPoint point,boolean at,Object returnValue) throws Exception{ //JoinPoint point, String monthRemark = getbooleanMethod(point); String monthName = point.getSignature().getDeclaringTypeName(); String voidName = point.getSignature().getName(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String dataId = (String) request.getAttribute("dataId"); TBaseUser user =(TBaseUser)request.getSession().getAttribute("user"); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); Calendar ca = Calendar.getInstance(); String operDate = df.format(ca.getTime()); TLoginLog log = new TLoginLog(); log.setLogId(DateUtil.getPKid()); if(user!=null){ log.setUserId(user.getUserid()); log.setUserName(user.getUsername()); log.setAccount(user.getAccount()); log.setOrgCode(Pub.outofnull(user.getOrgCode())); log.setLoginStatus(monthRemark); log.setLoginVoid(monthName+"."+voidName); log.setLoginDataid(returnValue==null?dataId:returnValue.toString()); log.setLoginDate(DateUtil.parseDate(operDate,"yyyy-MM-dd HH:mm:ss SSS")); log.setLoginIp(Pub.getIpAddr(request)); this.genericHibernateDao.save(log); } } /** * 获取方法名称 * @param joinPoint * @return * @throws Exception */ public static String getonMethod(JoinPoint joinPoint) throws Exception { Class<?> clazz = joinPoint.getTarget().getClass(); String name = joinPoint.getSignature().getName(); Object[] parameterTypes = joinPoint.getArgs(); for (Method method : clazz.getDeclaredMethods()) { if (method.getName().equals(name) && method.getParameterTypes().length == parameterTypes.length) { onMethod methodLog = method.getAnnotation(onMethod.class); if (methodLog != null) { return methodLog.remark(); } break; } } return ""; } /** * 获取方法名称 * @param joinPoint * @return * @throws Exception */ public static String getafterMethod(JoinPoint joinPoint) throws Exception { Class<?> clazz = joinPoint.getTarget().getClass(); String name = joinPoint.getSignature().getName(); Object[] parameterTypes = joinPoint.getArgs(); for (Method method : clazz.getDeclaredMethods()) { if (method.getName().equals(name) && method.getParameterTypes().length == parameterTypes.length) { afterMethod methodLogInfo = method.getAnnotation(afterMethod.class); if (methodLogInfo != null) { return methodLogInfo.remark(); } break; } } return ""; } /** * 获取方法名称 * @param joinPoint * @return * @throws Exception */ public static String getbooleanMethod(JoinPoint joinPoint) throws Exception { Class<?> clazz = joinPoint.getTarget().getClass(); String name = joinPoint.getSignature().getName(); Object[] parameterTypes = joinPoint.getArgs(); for (Method method : clazz.getDeclaredMethods()) { if (method.getName().equals(name) && method.getParameterTypes().length == parameterTypes.length) { booleanMethod methodLogInfo = method.getAnnotation(booleanMethod.class); if (methodLogInfo != null) { return methodLogInfo.remark(); } break; } } return ""; } }
在需要的方法上面添加注解:
@RequestMapping(value="/addform.do",method = RequestMethod.POST) @ResponseBody @afterMethod(remark="立案申请") public void addform(PrintWriter out, HttpServletRequest request,HttpSession session){ ... }
logService
增加了一个@AfterThrowing 方法,针对系统目前有返回值的方法,已可以正常抓取到异常
针对无void的方法需要加入 try ctach 加入
try{ }catch(Exception e){ throw e; }
onMethod.java:
/** * 表示对标记有xxx注解的类,做代理 注解@Retention可以用来修饰注解,是注解的注解,称为元注解。 * Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型, * 这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 * RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE * 用@Retention(RetentionPolicy * .CLASS)修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,但不会被虚拟机读取在运行的时候; * 用@Retention(RetentionPolicy.SOURCE * )修饰的注解,表示注解的信息会被编译器抛弃,不会留在class文件中,注解的信息只会留在源文件中; * 用@Retention(RetentionPolicy.RUNTIME * )修饰的注解,表示注解的信息被保留在class文件(字节码文件)中当程序编译时,会被虚拟机保留在运行时, * 所以他们可以用反射的方式读取。RetentionPolicy.RUNTIME * 可以让你从JVM中读取Annotation注解的信息,以便在分析程序的时候使用. * * 类和方法的annotation缺省情况下是不出现在javadoc中的,为了加入这个性质我们用@Documented * java用 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类。 * @interface是一个关键字,在设计annotations的时候必须把一个类型定义为@interface,而不能用class或interface关键字 * * @author * */ @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface onMethod { String remark() default ""; }
相关文章推荐
- spring aop 面向切面编程 如何来做一个强大的日志记录功能
- 使用Aop面向切面技术实现记录详细操作日志功能
- spring aop 面向切面编程 如何来做一个强大的日志记录功能.原创
- 从壹开始前后端分离【 .NET Core2.0 Api + Vue 2.0 + AOP + 分布式】框架之十 || AOP面向切面编程浅解析:简单日志记录 + 服务切面缓存
- Spring3.0 学习-AOP面向切面编程_Spring AOP的XML配置模式
- 从头认识Spring-1.2 什么是AOP?为什么需要面向切面编程?
- spring aop 切面记录log4j日志
- Spring核心机制(面向切面编程AOP)详解
- IT SpringAOP:足迹第十八步了解SpringAOP(如何使用面向切面开发权限认证、日志、事物)
- 【spring-boot】spring aop 面向切面编程初接触
- Spring 面向切面编程AOP
- Spring AOP 实现日志记录功能
- Spring---AOP面向切面编程
- Spring 面向切面编程AOP
- 基于SSM利用SpringAOP切面及自定义注解 记录每次操作记录(操作日志 同理)
- Spring面向切面编程——Spring实现AOP方式——通过Spring API实现
- Spring面向切面编程——Spring实现AOP方式——通过注解实现
- Spring的AOP-面向切面编程
- Spring AOP 面向切面编程 常见通知实现(前置,后置,环绕,异常)
- Spring之面向切面编程AOP(三)