spring的aop注解(java自定义注解)

这个AOP使用分享里,结合用到的还有自定义注解做切点、环绕增强拦截方法、请求参的截取。我们先了解下AOP,已经知道的,直接往后拖。AOP,面向切面编程。OOP,面向对象编程。个人鄙见:在OOP模式编程的时候,有时候很多的对象都需要添加一些公共的行为的时候,也许你会想到继承啊、提抽象啊、实现接口啊等等。没错,这是给很多对象添加公共行为的一个表现,其实就是多态嘛。但是这种…

大家好,又见面了,我是你们的朋友全栈君。

前言:

该篇以记录接口调用的传入参数日志为场景,来介绍下使用自定义注解作为切点,AOP切面方式去记录每个接口的传入参数以及可扩展的业务处理。

 

正文:

项目目录:

spring的aop注解(java自定义注解)

 

先是创建自定义注解, LogTrack:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author : JCccc
 * @CreateTime : 2020/4/13
 * @Description :
 **/

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogTrack {
    String value() default "logTracking";
}

上面的自定义注解,我只用了一个默认参,所以不用命名,写value就可以。

 

然后写切点对应的代码,LogTrackAspect:

用到了fastjson,导入依赖:
 

        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>

 

/**
 * @Author : JCccc
 * @CreateTime : 2020/4/13
 * @Description :
 **/

@Component
@Aspect
public class LogTrackAspect  {
    private static final Logger log = LoggerFactory.getLogger(LogTrackAspect.class);



    //这里需要注意了,这个是将自己自定义注解作为切点的根据,路径一定要写正确了
    @Pointcut(value = "@annotation(com.jc.mytest.aop.logRecord.LogTrack)")
    public void access() {

    }

    //进来切点世界,先经过的第一个站
    @Before("access()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {


        System.out.println("-aop 日志记录启动-" + new Date());


    }


    //环绕增强,是在before前就会触发
    @Around("@annotation(logTrack)")
    public Object around(ProceedingJoinPoint pjp, LogTrack logTrack) throws Throwable {
        System.out.println("-aop 日志环绕阶段-" + new Date());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

//        GET 请求其实可以从request里获取出参数
//       Map<String,String[]> map=request.getParameterMap();
//        System.out.println("获取参数:"+map.get("username")[0])

        String url = request.getRequestURL().toString();
        String ip = IpUtil.getIpAddr(request);
        String logTrackValue = logTrack.value();
        Object[] pipArrary = pjp.getArgs();
        if (pipArrary.length>1){ //多参,不是Map/JsonObject方式
            List<Object> argList = new ArrayList<>();
            for (Object arg : pjp.getArgs()) {
          // request/response无法使用toJSON
            if (arg instanceof HttpServletRequest) {
                       argList.add("request");
                 } else if (arg instanceof HttpServletResponse) {
                  argList.add("response");
                 } else {
                    argList.add(JSON.toJSON(arg));
            }
            }
         Signature signature = pjp.getSignature();
         MethodSignature methodSignature = (MethodSignature) signature;
        // 参数名数组
        String[] parameterNames = ((MethodSignature) signature).getParameterNames();
            System.out.println("参数名数组:"+new ArrayList(Arrays.asList(parameterNames)));
            System.out.println("参数是:"+argList.toString());
            System.out.println("logTrackValue:"+logTrackValue);
            System.out.println("url:"+url);
            System.out.println("ip:"+ip);
            return pjp.proceed();
            
        }

        Object param =  pipArrary[0];
        System.out.println("logTrackValue:"+logTrackValue);
        System.out.println("url:"+url);
        System.out.println("ip:"+ip);
        System.out.println("param:"+param.toString());
        return pjp.proceed();

    }


    //进来切点这,最后经过的一个站,也是方法正常运行结束后
    @After("access()")
    public void after(JoinPoint joinPoint) {
        System.out.println("-aop 日志记录结束-" + new Date());
    }

}

ps:return pjp.proceed(); 这个是从切点的环绕增强里面脱离出来,接下来会进入before阶段 ,然后回到接口,再回来after阶段。

所以扩展业务逻辑处理的话,可以放在return pjp.proceed();这行代码之前,例如判断用户密码是否正确;判断用户权限等等。

 

接下来是在Controller编写接口,并用上自定义注解,MyTestController:

/**
 * @Author : JCccc
 * @CreateTime : 2020/3/27
 * @Description :
 **/

@Controller
@RequestMapping("/test")
public class MyTestController {

    @ResponseBody
    @GetMapping("/testGet1")
    @LogTrack("testGet1 接口")
    public void testGet1(@RequestParam("userId") String userId, @RequestParam("toUserId") String toUserId) {

        System.out.println("已经进入GET测试接口,参数userId:" + userId+  "参数toUserId:"+toUserId);
    }


    @ResponseBody
    @GetMapping("/testGet2")
    @LogTrack("testGet2 接口")
    public void testGet2(@RequestParam Map map) {

        System.out.println("已经进入GET测试接口,参数:" + map.get("userId"));
    }

    @ResponseBody
    @PostMapping("/testPost1")
    @LogTrack("testPost1 接口")
    public void testPost1(@RequestBody Map map) {

        System.out.println("已经进入POST测试接口,参数:" + map.toString());
    }

    @ResponseBody
    @PostMapping("/testPost2")
    @LogTrack("testPost2 接口")
    public void testPost2(@RequestBody JSONObject jsonObject) {

        System.out.println("已经进入POST测试接口,参数:" + jsonObject.toString());
    }

}

然后我们来运行一下,看看控制台,就知道整个切点以及环绕的流程了: 

 

首先是测试GET方式的接口,通过@RequestParam单个参数获取的情况:
spring的aop注解(java自定义注解)

调用接口:

spring的aop注解(java自定义注解)

运行结果:

 spring的aop注解(java自定义注解)

 

接下来还是GET方式 ,通过Map去接收多参:

spring的aop注解(java自定义注解)

调用接口:

spring的aop注解(java自定义注解)

运行结果:

spring的aop注解(java自定义注解)

 

然后是调用Post请求:

spring的aop注解(java自定义注解)

 

spring的aop注解(java自定义注解)

 

ps: 如果发现按照上面配置了,但是aop切点的方法好像没触发,
那么可以试试

1.检查jar包是否有导入正确

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

2.在启动类上加上注解@EnableAspectJAutoProxy  这个其实springboot是默认帮我们已经开启为true状态的。

 

 

 

 

本篇使用到的一些工具类:

IpUtil:

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @Author : JCccc
 * @CreateTime : 2018-11-23
 * @Description :
 * @Point: Keep a good mood
 **/
public class IpUtil {
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals("127.0.0.1")) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress="";
        }
        // ipAddress = this.getRequest().getRemoteAddr();

        return ipAddress;
    }
}

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/129170.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • PHP执行系统外部命令函数:exec()、passthru()、system()、shell_exec()

    PHP执行系统外部命令函数:exec()、passthru()、system()、shell_exec()

    2021年11月10日
    46
  • android studio SDK 安装不成功

    android studio SDK 安装不成功第一次安装androidstudio出现的问题问题1:无法访问androidstudio官网解决:1:https://developer.android.google.cn/studio/install?hl=zh-cner.android.google.cn/studio/install?hl=zh-cn一定是要带google的地址才行,这是google给中国用的;2:访问到了还不一定行,有可能点击下载时页面还是报错了,这时候需要检查自己网络的原因,我就是因为连接了公司的网络导致下载不了

    2022年7月21日
    39
  • 启动apache服务_cannot resolve org.apache

    启动apache服务_cannot resolve org.apache最近在使用PHP5.6+Apache2.4,搭建开发环境;在实际的开发过程中发现http请求一致处于挂起状态,于是重启(netstart/stop)Apache2.4服务,然后再访问问题得到了解决,但是过一会,平凡的打开页面又出现问题,通过查看web服务器的日志,发现报很多"指定的网络名不再可用"的错误:12345[Thu Nov 20 14…

    2022年9月26日
    3
  • Vue 后台管理系统 Demo

    Vue 后台管理系统 DemoGITHUB 原文地址 https github com lin xin vue manage system 前言该方案作为一套多功能的后台框架模板 适用于绝大部分的后台管理系统 WebManagemen 开发 基于 vue js 使用 vue cli3 脚手架 引用 ElementUI 组件库 方便开发快速简洁好看的组件 分离颜色样式 支持手动切换主题色 而且很方便

    2026年2月1日
    0
  • linux配置环境变量jdk_java配置环境变量详解

    linux配置环境变量jdk_java配置环境变量详解在配置Linux系统服务器的时候,我们常常需要设置系统环境变量,这篇文章就是总结几种常见的配置环境变量的方式。

    2025年8月23日
    3
  • Android常用对话框大全——Dialog「建议收藏」

    Android常用对话框大全——Dialog「建议收藏」唉!最近一直忙碌着写项目以至于都没有空出点时间来总结近期的学习,记录学习到的东西…现在正好有时间了就该好好记录一下学习的过程了。今天就来谈谈开发中经常用的到的一个控件——Dialog,对话框一般我们就用来提示一些信息给用户,让用户自主选择,或者在一些操作不可逆的情况下我们提示用户是否继续操作,下面就让我们一起来学习吧。老司机发车啦…一:最简单的对话框AlertDialogdialog=new

    2022年5月5日
    55

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号