aop 通过注解实现业务的日志记录

在咱们开发的项目中,有人操作系统的时候需要记录操作人在该系统做了哪些操作,最原始的方式就是在每个需要记录的方法最后写插入日志的方法。首先他是可以实现我们的需求。但是有两个劣势:

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()方法来获取代理对象



猜你喜欢

转载自blog.csdn.net/qq_29897369/article/details/79517863