Spring AOP 自定義註解實現統一日誌管理

1、AOP的基本概念:html

AOP,面向切面編程,經常使用於日誌,事務,權限等業務處理。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容(Spring核心之一),是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。spring

2、AOP的幾個特徵:express

(1)Aspect(切面):一般是一個類,裏面能夠定義切入點和通知編程

(2)JointPoint(鏈接點):程序執行過程當中明確的點,通常是方法的調用app

(3)Advice(通知):AOP在特定的切入點上執行的加強處理,有before,after,afterReturning,afterThrowing,around框架

(4)Pointcut(切入點):就是帶有通知的鏈接點,在程序中主要體現爲書寫切入點表達式函數式編程

(5)AOP代理:AOP框架建立的對象,代理就是目標對象的增強。Spring中的AOP代理可使JDK動態代理,也能夠是CGLIB代理,前者基於接口,後者基於子類函數

 

3、具體功能實例:測試

(1)首先引入spring AOP所需的jar包依賴:網站

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.3</version>
</dependency>
        
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.3</version>
</dependency>

 

 (2)自定義註解類:

/**
 * 自動日誌監聽註解類
 * @author AoXiang
 *
 */

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysAutoLog {

    String module() default "";
    String methods() default "";
    String description() default "";
    
}

 

 (3)新建切面類:

/**
 * 日誌切面類
 * @author AoXiang
 *
 */
@Aspect
@Component
public class SysLogAopControl{

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
    
    @Autowired
    private SysHandLogStub SysHandLogStub;
    

    @Pointcut("@annotation(cn.tisson.tc.annotation.SysAutoLog)")
    public void LogAspect() {}

    @AfterThrowing(pointcut = "LogAspect()", throwing = "e")
    public void doAfterThrowing(JoinPoint point, Throwable e) throws Exception {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        SysHandLogEntity sysLog = new SysHandLogEntity();
        Map<String, Object> map = this.getMethodDescription(point);
        sysLog.setModel(map.get("module").toString());
        sysLog.setMethod("執行方法異常:-->" + map.get("methods").toString());
        sysLog.setStatusDesc("執行方法異常:-->" + e);
        sysLog.setArgs(map.get("args").toString());
        sysLog.setIp(GetRemoteIpUtil.getRemoteIp(request));
        sysLog.setCreateDate(new Date());
        SysHandLogStub.update(sysLog);
    }

    @Around("LogAspect()")
    public Object doAround(ProceedingJoinPoint point) {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        Object result = null;
        try {
            result = point.proceed();
            SysHandLogEntity sysLog = new SysHandLogEntity();
            Map<String, Object> map = this.getMethodDescription(point);
            sysLog.setModel(map.get("module").toString());
            sysLog.setMethod(map.get("methods").toString());
            sysLog.setStatusDesc(map.get("description").toString());
            sysLog.setArgs(map.get("args").toString());
            sysLog.setIp(GetRemoteIpUtil.getRemoteIp(request));
            sysLog.setCreateDate(new Date());
            //用戶信息
            Subject subject = SecurityUtils.getSubject();
            UserVo userVo = (UserVo)subject.getPrincipal();
            if (userVo == null) {
                userVo = (UserVo)ShiroSubject.getSessionVo();
            }
            if(userVo != null) {
                sysLog.setUserRealName(userVo.getRealName());
            }
            SysHandLogStub.update(sysLog);
        } catch (Throwable e) {
            logger.error("異常信息:{}", e.getMessage());
            throw new RuntimeException(e);
        }
        return result;
    }

    @SuppressWarnings("rawtypes")
    public Map<String, Object> getMethodDescription(JoinPoint joinPoint) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    map.put("module", targetName.substring(targetName.lastIndexOf(".")+1,targetName.length()));
                    String methodStr = method.toString().substring(0,method.toString().lastIndexOf("(") );
                    methodStr = methodStr.substring(methodStr.lastIndexOf(".")+1,methodStr.length() );
                    map.put("methods", methodStr);
                    map.put("args", this.getArgs(method, arguments));
                    String desc = method.getAnnotation(SysAutoLog.class).description();
                    if (StringUtils.isEmpty(desc))
                        desc = "執行成功!";
                    map.put("description", desc);
                    break;
                }
            }
        }
        return map;
    }
    
    private String getArgs(Method method, Object[] arguments) {
        StringBuilder builder = new StringBuilder("{");
        String params[] = parameterNameDiscoverer.getParameterNames(method);
        if(params.length==0) {
            return "無參數";
        }
        for (int i = 0; i < params.length; i++) {
            if(!"password".equals(params[i])) {
                builder.append(params[i]).append(" : ").append(arguments[i]).append(";");
            }
        }
        return builder.append("}").toString(); 
    }
}

 

 (4)配置springMVC.xml文件,啓動AOP支持

<!-- 該文件只注入Controller 類 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 設置使用註解的類所在的jar包 -->
<context:component-scan base-package="cn.test" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

 

 注意,關於springMVC.xml配置文件的寫法,有幾點須要注意:

引用crawl+的博客http://www.cnblogs.com/crawl/p/7940755.html中的描述以下:

use-default-filters 屬性的默認值爲 true,即使用默認的 Filter 進行包掃描,而默認的 Filter 對標有 @Service,@Controller和@Repository 的註解的類進行掃描,由於前面說過,咱們但願 SpringMVC 只來控制網站的跳轉邏輯,因此咱們只但願 SpringMVC 的配置掃描 @Controllerce 註解標註的類,不但願它掃描其他註解標註的類,因此設置了 use-default-filters 爲 false,並使用 context:include-filter 子標籤設置其只掃描帶有 @Controller 註解標註的類。

在使用 use-default-filters 屬性時要分清楚須要掃描哪些包,是否是須要使用默認的 Filter 進行掃描。樓主稍微總結一下,即 use-default-filters="false" 須要和 context:include-filter 一塊兒使用,而不能和 context:exclude-filter 屬性一塊兒使用。

 

(5)使用方式

@ResponseBody
@SysAutoLog(description="測試方法")
@RequestMapping("testr")
public String test(HttpServletRequest request) throws Exception {}

 

只須要在方法上添加@SysAutoLog(description="")即咱們自定義的註解便可,description是方法描述,這樣在記錄日誌的時候能夠一併記錄下日誌描述。

 

4、總結:

關於Spring AOP處理日誌的實現比較簡單,固然對於日誌的統一處理也不止於這一種方式,還可使用攔截器的方式,能夠根據項目具體的應用環境選擇合適的方式,有什麼不當之處歡迎你們批評指正。

相關文章
相關標籤/搜索