Spring-AOP日志记录及使用注解解释

一、注解

1.@RequestMapping:

    1.1、@RequestMapping是什么?

          用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。用来处理请求地址映射的注解,可用于方法上。

       @RequestMapping 修饰类 :

              类定义处: 提供初步的请求映射信息。相对于 WEB 应用的根目录;

             方法处: 提供进一步的细分映射信息。 相对于类定义处的 URL。

             若类定义处未标注 @RequestMapping,则方法处标记的 URL相对于 WEB 应用的根目录

             返回ModelAndView时的url会根据你的 @RequestMapping实际情况组成。

             如果类上没有映射,那么url直接就是方法的映射;否则url为类上+方法上映射路径组合。

             对应项目jsp位置则是一级路径对应一级文件目录。

             如url为/default/index对应项目中webapp/default/index.jsp

  1.2、@RequestMapping注解的属性:

             1.2.1、@RequestMapping都有哪些属性?

             

              value:指定请求的实际地址,指定的地址可以是URI Template 模式;

              method: 指定请求的method类型, GET、POST、PUT、DELETE等;

              consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;

              produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;

              params: 指定request中必须包含某些参数值时,才让该方法处理。

              headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。

              name:

              path:

2.

@ArchivesLog:自定义注解类

fileQuery为controller层的方法

@RequestMapping(value="/fileQuery")
@ArchivesLog(operationType="操作类型",operationName="方法作用")
public ModelAndView fileQuery(HttpServletRequest request, HttpServletResponse response,String ip){
    return new ModelAndView("archives/fileQuery.ftl");
}

@Target({ElementType.TYPE}) 注解

ElementType 这个枚举类型的常量提供了一个简单的分类:注释可能出现在Java程序中的语法位置(这些常量与元注释类型(@Target)一起指定在何处写入注释的合法位置)

@Retention({RetentionPolicy.Runtime}) 注解

RetentionPolicy这个枚举类型的常量描述保留注释的各种策略,它们与元注释(@Retention)一起指定注释要保留多长时间

@Documented注解

Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

ArchivesLog:自定义注解类

import java.lang.annotation.*;

/**
 * @author CB
 * @Title: ArchivesLog
 * @Package 
 * @Description: <日志打印注解类>
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ArchivesLog {

    /** 要执行的操作类型比如:add操作 **/
    public String operationType() default "";

    /** 要执行的具体操作比如:添加用户 **/
    public String operationName() default "";
}

Spring默认不支持@Aspect风格的切面声明,通过如下配置开启@Aspect支持:

<aop:aspectj-autoproxy/>  

通过以上配置,Spring就能发现用@Aspect注解的切面内并把它应用到目标对象上。 

@component (把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)

ArchivesLogAspect:切面日志类

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;

@Aspect
@Component
public class ArchivesLogAspect {

    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private HttpServletRequest request = null;

    /**
     * @param joinPoint
     * @Description: 方法调用后触发   记录结束时间
     * @author CB
     */
    @Around("within(自己的项目路径..*) && @annotation(ArchivesLog)")
    public void after(JoinPoint joinPoint) {
        startTimeMillis = System.currentTimeMillis();
        request = getHttpServletRequest();
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = null;
        try {
            targetClass = Class.forName(targetName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method[] methods = targetClass.getMethods();
        String operationName = "";
        String operationType = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs != null && clazzs.length == arguments.length && method.getAnnotation(ArchivesLog.class) != null) {
                    operationName = method.getAnnotation(ArchivesLog.class).operationName();
                    operationType = method.getAnnotation(ArchivesLog.class).operationType();
                    break;
                }
            }
        }
        endTimeMillis = System.currentTimeMillis();
        //操作时间
        String startTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis);
        String ip = getIpAddress(request);
        String uri = request.getRequestURI();//返回请求行中的资源名称
        String url = request.getRequestURL().toString();//获得客户端发送请求的完整url
        String params = request.getQueryString();//返回请求行中的参数部分
        String host = request.getRemoteHost();//返回发出请求的客户机的主机名
        int port = request.getRemotePort();//返回发出请求的客户机的端口号。
        System.out.println(ip);
        System.out.println(url);
        System.out.println(uri);
        System.out.println(params);
        System.out.println(host);
        System.out.println(port);
        System.out.println(" 操作人: " + request.getRemoteUser() + " 操作方法: " + operationName + " 操作时间: " + startTime
                + "operationType" + operationType + methodName + "用户ip:" + ip);
    }

    /**
     * @param
     * @return HttpServletRequest
     * @Description: 获取request
     * @author CB
     */
    public HttpServletRequest getHttpServletRequest() {
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest request = sra.getRequest();
        return request;
    }

    /**
     * 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
     * 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,
     * 192.168.1.100
     * 用户真实IP为: 192.168.1.110
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

Sring配置文件

<!--AOP操作日志打印-->
<aop:aspectj-autoproxy proxy-target-class="true" />

通过配置织入@Aspectj切面

虽然可以通过编程的方式织入切面,但是一般情况下,我们还是使用spring的配置自动完成创建代理织入切面的工作。

通过aop命名空间的<aop:aspectj-autoproxy />声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring

在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被<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动态代理。

@AspectJ语法基础

@AspectJ使用jdk5.0注解和正规则的AspectJ 5的切面表达式语言描述切面,由于spring只支持方法的连接点,所以spring仅支持部分aspectJ的切面语言,在这节时,我们将对AspectJ切点表达式语言进行必要的学习。

切点表达式函数

AspectJ 5的切点表达式由关键字和操作参数组成。如execution(*greeTo(..))的切点表达式,"execute"为关键字,而"*greeTo(..)"为操作参数。在这里,execution代表目标类执行某一方法,而"*greeTo(..)"是描述目标方法的匹配模式串,两者联合起来所表示的切点匹配目标类greeTo(..)方法的连接点。为了描述方便,我们将execution()称作函数,而将匹配串"*greeTo(..)"称作函数的入参。

             

个人微信:

猜你喜欢

转载自blog.csdn.net/trggrthy/article/details/81407010
今日推荐