您当前的位置: 首页 >  spring

衣舞晨风

暂无认证

  • 0浏览

    0关注

    1156博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring MVC AOP通过自定义注解方式拦截Controller等实现日志管理

衣舞晨风 发布时间:2016-08-18 20:53:40 ,浏览量:0

之前一直写.net,没玩过spring,一直没用过aop(面向切面编程)这类功能,当然不是说.net里面没有这类框架,企业库就可以微软企业库官网

开始上代码:

注解定义
package com.jiankunking.common;

import java.lang.annotation.*;

/**
 * @author jiankunking
 * @Date: 2016/8/15
 * @Time: 11:09
 * @annotation OperationLogger
 */
@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)//目标是方法
@Documented//文档生成时,该注解将被包含在javadoc中,可去掉
public @interface OperationLogger
{
    /**
     * 模块名字
     */
    String modelName() default "";

    /**
     * 操作类型
     */
    String option();

}

@interface是用来自定义注释类型的。 注释的声明用@符号后面跟上这个注释类型的名字,再后面跟上括号,括号中列出这个注释中元 素/方法的key-value对。值必须是常量。

AOP拦截部分
package com.jiankunking.common;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author jiankunking
 * @Date: 2016/8/15
 * @Time: 11:11
 * @annotation SysLogAspect
 */
@Aspect
@Component
public class SysLogAspect
{
    private static final Logger logger = Logger.getLogger(SysLogAspect.class);

    /**
     * 定义Pointcut,Pointcut的名称,此方法不能有返回值,该方法只是一个标示
     */
    @Pointcut("@annotation(com.jiankunking.common.OperationLogger)")
    public void controllerAspect()
    {
        System.out.println("我是一个切入点");
    }

    /**
     * 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。
     * @param joinPoint
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint)
    {
        System.out.println("=====SysLogAspect前置通知开始=====");
        //handleLog(joinPoint, null);
    }

    /**
     * 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
     * @param joinPoint
     */
    @AfterReturning(pointcut = "controllerAspect()")
    public void doAfter(JoinPoint joinPoint)
    {
        System.out.println("=====SysLogAspect后置通知开始=====");
        //handleLog(joinPoint, null);
    }

    /**
     * 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "controllerAspect()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e)
    {
        System.out.println("=====SysLogAspect异常通知开始=====");
        //handleLog(joinPoint, e);
    }

    /**
     * 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。
     * @param joinPoint
     */
    @Around("controllerAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
    {
        System.out.println("=====SysLogAspect 环绕通知开始=====");
        //handleLog(joinPoint, null);
        Object obj= joinPoint.proceed();
        System.out.println("=====SysLogAspect 环绕通知结束=====");
        return  obj;
    }

    /**
     * 日志处理
     *
     * @param joinPoint
     * @param e
     */
    private void handleLog(JoinPoint joinPoint, Exception e)
    {
        try
        {
            //获得注解
            OperationLogger logger = giveController(joinPoint);
            if (logger == null)
            {
                return;
            }

            String signature = joinPoint.getSignature().toString(); // 获取目标方法签名
            String methodName = signature.substring(signature.lastIndexOf(".") + 1,
                    signature.indexOf("("));

            String longTemp = joinPoint.getStaticPart().toLongString();
            String classType = joinPoint.getTarget().getClass().getName();

            Class clazz = Class.forName(classType);

            Method[] methods = clazz.getDeclaredMethods();
            System.out.println("methodName: " + methodName);

            for (Method method : methods)
            {
                if (method.isAnnotationPresent(OperationLogger.class)
                        && method.getName().equals(methodName))
                {
                    //OpLogger logger = method.getAnnotation(OpLogger.class);
                    String clazzName = clazz.getName();
                    System.out.println("clazzName: " + clazzName + ", methodName: "
                            + methodName);
                }
            }

        } catch (Exception exp)
        {
            logger.error("异常信息:{}", exp);
            exp.printStackTrace();
        }
    }

    /**
     * 获得注解
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private static OperationLogger giveController(JoinPoint joinPoint) throws Exception
    {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null)
        {
            return method.getAnnotation(OperationLogger.class);
        }
        return null;
    }

}

Aspect通常用于将必要的但和业务无关的逻辑和业务逻辑分离。

Spring使用的AOP注解分为三个层次: 前提条件是在xml中放开了


  1. @Aspect放在类头上,把这个类作为一个切面。
  2. @Pointcut放在方法头上,定义一个可被别的方法引用的切入点表达式。
  3. 5种通知。
    1. @Before,前置通知,放在方法头上。
    2. @After,后置【finally】通知,放在方法头上。
    3. @AfterReturning,后置【try】通知,放在方法头上,使用returning来引用方法返回值。
    4. @AfterThrowing,后置【catch】通知,放在方法头上,使用throwing来引用抛出的异常。
    5. @Around,环绕通知,放在方法头上,这个方法要决定真实的方法是否执行,而且必须有返回值。
在Maven中加入以下以依赖

    4.0.0
    com.mkyong.common
    spring-mvc-log4j
    war
    1.0-SNAPSHOT
    SpringMVC + Log4j

    
        1.7
        4.3.2.RELEASE
        2.6.2
        1.2
        3.1.0
        1.7.4
        3.1
    

    

        
            org.springframework
            spring-webmvc
            ${spring.version}
        
        
            org.springframework
            spring-aop
            ${spring.version}
        
        
            org.springframework
            spring-aspects
            ${spring.version}
        

        
        
            org.apache.logging.log4j
            log4j-api
            ${log4j.version}
        
        
            org.apache.logging.log4j
            log4j-core
            ${log4j.version}
        
        

        
        
            jstl
            jstl
            ${jstl.version}
        

        
            javax.servlet
            javax.servlet-api
            ${servletapi.version}
            provided
        
        
            log4j
            log4j
            1.2.17
        

        
        
            org.aspectj
            aspectjrt
            ${org.aspectj-version}
        

        
        
            javax.inject
            javax.inject
            1
        

        
            cglib
            cglib
            ${cglib.version}
        

    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.3
                
                    ${jdk.version}
                    ${jdk.version}
                
            
        
    

在spring-*.xml中加入spring支持,打开aop功能



    
    

    
    
        
            /WEB-INF/pages/
        
        
            .jsp
        
    
    
    

    
    

    
    
        
    
    
    

注解也写好了,spring也配置好了,在controller里面怎么用呢?

Controller应用
package com.jiankunking.controller;

import com.jiankunking.common.OperationLogger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "/Welcome", produces = "text/html;charset=UTF-8")
public class WelcomeController
{
    @OperationLogger(modelName = "WelcomeController", option = "getWelcome")
    @RequestMapping(value = "/getWelcome", method = RequestMethod.POST)
    public void getWelcome()
    {
        //异常拦截测试
        //int i = 9 / 0;
        System.out.println("controller方法执行!");
    }

}

如何测试呢? 从前端发起ajax请求controller,即可:

$.ajax({
                type: "POST",
                url: "/Welcome/getWelcome",
                contentType: "application/json",
                data: null,
                success: function ()
                {
//                    alert("22");
                },
                error: function ()
                {
//                    alert("失败!");
                }
            });

效果如下: 这里写图片描述

由于这里是通过Spring的@Aspect注解实现的AOP,所以同一个类中的某个方法A(该方法没有注解标识)调用另一个有注解标识的方法B时,方法B上的注解是不会起作用的。

这里写图片描述

演示demo: http://download.csdn.net/detail/xunzaosiyecao/9609918

xml方式实现aop拦截及aop基础知识参考: xml实现aop拦截

Spring Boot 自定义注解方式拦截Controller等实现日志管理: Spring Boot 自定义注解

个人微信公众号: 这里写图片描述

作者:jiankunking 出处:http://blog.csdn.net/jiankunking

关注
打赏
1647422595
查看更多评论
立即登录/注册

微信扫码登录

0.0421s