AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等等。

Spring AOP模块提供截取拦截应用程序的拦截器,例如,当执行方法时,可以在执行方法之前或之后添加额外的功能.

一 AOP的基本概念

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知

(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

二 AOP常用注解

  • @JoinPoint

    切入点(PointCut)是一组一个或多个连接点,在其中应该执行的通知。 您可以使用表达式或模式指定切入点,我们将在AOP示例中看到。 在Spring中切入点有助于使用特定的连接点来应用通知。请考虑以下示例:

    @Pointcut("execution(* com.demo.controller.*.*(..))")
    @Pointcut("execution(* com.demo.StudentServiceImpl.getName(..))")

    语法

    @Aspectpublic class Logging {   
    
    //切所有的controller包下表所有方法 
    @Pointcut("execution(* com.demo.controller.*.*(..))")
    private void controllerPoint(){}

        @Aspect - 将类标记为包含通知方法的类。

@Pointcut - 将函数标记为切入点

execution( expression ) - 涵盖应用通知的方法的表达式。

  • @Before Advice

    @Before是一种通知类型(前置),可以确保在方法执行之前运行通知。 以下是@Before通知(advice)的语法:

    @Pointcut("execution(* com.demo.controller.*.*(..))")
    private void controllerPoint(){}
    @Before("controllerPoint()")
    public void beforeAdvice(){
    System.out.println("在方法执行之前输出");
    }
  • @After Advice

    @After是一种通知类型(后置),可确保在方法执行后运行通知。 以下是@After通知类的语法:

    @Pointcut("execution(* com.demo.controller.*.*(..))")p
    rivate void controllerPoint(){}
    @After("controllerPoint()") public void afterAdvice(){
    System.out.println("方法执行后输出。");
    }
  • @After Returning Advice

    @AfterReturning是一种通知类型,可确保方法执行成功后运行通知。 以下是@AfterReturning通知的语法:

    @AfterReturning(pointcut="execution(* com.demo.controller.*.*(..))", returning="retVal")
    public void afterReturningAdvice(JoinPoint jp, Object retVal){
    System.out.println("Method Signature: " + jp.getSignature());
    System.out.println("Returning:" + retVal.toString() );
    }
    ps:returning - 要返回的变量的名称。
  • @AfterThrowing

    @AfterThrowing是一种通知类型,可以确保在方法抛出异常时运行一个通知

    @AfterThrowing(pointcut="execution(* com.demo.controller.*.*(..))", throwing= "error")
    public void afterThrowingAdvice(JoinPoint jp, Throwable error){
    System.out.println("Method Signature: " + jp.getSignature());
    System.out.println("Exception: "+error);
    }
    ps:throwing - 返回的异常名称
  • @Around

    @Around是一种环绕通知,通过环绕通知,我们可以在一个方法内完成前置、后置、异常(@AfterThrowing)等通知所实现的功能。

    @Around("controllerPoint()")
    
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ 
    System.out.println("方法开始");
    //执行方法
    Object result=jp.proceed(args);
    System.out.println("方法结束");
    return result.toString();
    } 

三 使用AOP记录每个servie方法执行日志

package com.example.dubbo.demo.service.aop;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo; @Component
@Aspect
public class LogAspect { private final Logger logger = LoggerFactory.getLogger(LogAspect.class); //定义切点
@Pointcut("execution(public * com.example.dubbo.demo.service.impl..*.*(..))")
public void serviceLog(){} @Around("serviceLog()")
public Object logBefore(ProceedingJoinPoint pj) throws Throwable{
//接口请求的开始时间
Long startTimeMillis = System.currentTimeMillis();
JSONObject paramJson = this.printMethodParams(pj,String.valueOf(startTimeMillis));
logger.info("请求前:{}",paramJson.toString());
Object retVal = pj.proceed(); JSONObject returnJson = new JSONObject();
returnJson.put("class_name",paramJson.get("class_name"));
returnJson.put("method_name",paramJson.get("method_name"));
returnJson.put("class_name_method",paramJson.get("class_name_method"));
returnJson.put("return_name",retVal);
Long endTimeMillis = System.currentTimeMillis();
returnJson.put("endTimeMillis",endTimeMillis);
returnJson.put("times",endTimeMillis - startTimeMillis);
logger.info("请求后:"+returnJson.toString()); return retVal; } @AfterThrowing(pointcut = "serviceLog()", throwing = "e")//切点在webpointCut()
public void handleThrowing(JoinPoint joinPoint, Exception e) throws IOException {
Long startTimeMillis = System.currentTimeMillis();
JSONObject paramJson = this.printMethodParams(joinPoint,String.valueOf(startTimeMillis));
//获取错误详细信息
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
paramJson.put("errorMsg", e.getMessage());
paramJson.put("StackTrace", sw.toString());
logger.info("请求错误:"+paramJson.toString());
pw.flush();
sw.flush();
} /**
* 打印类method的名称以及参数
* @param point 切面
*/
public JSONObject printMethodParams(JoinPoint point,String startTimeMillis){
if(point == null){
return new JSONObject();
}
/**
* Signature 包含了方法名、申明类型以及地址等信息
*/
String class_name = point.getTarget().getClass().getName();
String method_name = point.getSignature().getName(); logger.info("class_name = {},startTimeMillis:"+startTimeMillis,class_name);
logger.info("method_name = {},startTimeMillis:"+startTimeMillis,method_name); JSONObject paramJson = new JSONObject();
paramJson.put("class_name",class_name);
paramJson.put("method_name",method_name);
paramJson.put("startTimeMillis",startTimeMillis);
paramJson.put("class_name_method", String.format("%s.%s", class_name,method_name));
/**
* 获取方法的参数值数组。
*/
Object[] method_args = point.getArgs(); try {
//获取方法参数名称
String[] paramNames = getFieldsName(class_name, method_name); //打印方法的参数名和参数值
String param_name = logParam(paramNames,method_args);
paramJson.put("param_name",JSONObject.parse(param_name));
} catch (Exception e) {
e.printStackTrace();
}
return paramJson;
} /**
* 使用javassist来获取方法参数名称
* @param class_name 类名
* @param method_name 方法名
* @return
* @throws Exception
*/
private String[] getFieldsName(String class_name, String method_name) throws Exception {
Class<?> clazz = Class.forName(class_name);
String clazz_name = clazz.getName();
ClassPool pool = ClassPool.getDefault();
ClassClassPath classPath = new ClassClassPath(clazz);
pool.insertClassPath(classPath); CtClass ctClass = pool.get(clazz_name);
CtMethod ctMethod = ctClass.getDeclaredMethod(method_name);
MethodInfo methodInfo = ctMethod.getMethodInfo();
CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
if(attr == null){
return null;
}
String[] paramsArgsName = new String[ctMethod.getParameterTypes().length];
// 如果是静态方法,则第一就是参数
// 如果不是静态方法,则第一个是"this",然后才是方法的参数
// 我接口中没有写public修饰词,导致我的数组少一位参数,所以再往后一位,原本应该是 XX ? 0 : 1
int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
for (int i=0;i<paramsArgsName.length;i++){
paramsArgsName[i] = attr.variableName(i+pos);
}
return paramsArgsName;
} /**
* 判断是否为基本类型:包括String
* @param clazz clazz
* @return true:是; false:不是
*/
private boolean isPrimite(Class<?> clazz){
if (clazz.isPrimitive() || clazz == String.class){
return true;
}else {
return false;
}
} /**
* 打印方法参数值 基本类型直接打印,非基本类型需要重写toString方法
* @param paramsArgsName 方法参数名数组
* @param paramsArgsValue 方法参数值数组
*/
private String logParam(String[] paramsArgsName,Object[] paramsArgsValue){
StringBuffer buffer = new StringBuffer();
if(ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)){
buffer.append("{\"noargs\":\"该方法没有参数\"}");
return buffer.toString();
}
for (int i=0;i<paramsArgsName.length;i++){
//参数名
String name = paramsArgsName[i];
//参数值
Object value = paramsArgsValue[i];
buffer.append("\""+name+"\":");
if(isPrimite(value.getClass())){
buffer.append("\""+value+"\",");
}else {
buffer.append(JSON.toJSONString(value)+",");
}
}
return "{"+buffer.toString().substring(0,buffer.toString().length()-1)+"}";
} }

最后需要再 application.properties文件中添加开启aop的配资

spring.aop.auto=true

作者:Eric.Chen
出处:https://www.cnblogs.com/lc-chenlong

如果喜欢作者的文章,请关注“写代码的猿”订阅号以便第一时间获得最新内容。本文版权归作者所有,欢迎转载

springboot+mybatis+dubbo+aop日志第三篇的更多相关文章

  1. springboot+mybatis+dubbo+aop日志第一篇

    本篇文章主要讲述项目搭建过程,不会涉及过多的基础知识,本项目是作者对前段时间学习的一个总结,主要使用到技术有:maven父子工程.springboot.mybatis.dubbo.zookeeper. ...

  2. springboot+mybatis+dubbo+aop日志第二篇

    本篇主要介绍dubbo-demo-api接口层和dubbo-demo-service层,以及如何通过dubbo把服务发布出去,介绍代码前,咱们先来回顾一下整个demo工程的结构,如下图所示: 1.du ...

  3. springboot+mybatis+dubbo+aop日志终结篇

    之前的几篇文章把dubbo服务层都介绍完毕,本篇文章咱们主要写web层如何调用服务层的方法.文章底部附带源码. 启动服务 服务启动时,会向zk注册自己提供的服务,zk则会记录服务提供者的IP地址以及暴 ...

  4. DB数据源之SpringBoot+MyBatis踏坑过程(三)手工+半自动注解配置数据源与加载Mapper.xml扫描

    DB数据源之SpringBoot+MyBatis踏坑过程(三)手工+半自动注解配置数据源与加载Mapper.xml扫描 liuyuhang原创,未经允许禁止转载    系列目录连接 DB数据源之Spr ...

  5. Springboot+Mybatis+Pagehelper+Aop动态配置Oracle、Mysql数据源

      本文链接:https://blog.csdn.net/wjy511295494/article/details/78825890 Springboot+Mybatis+Pagehelper+Aop ...

  6. springboot与dubbo整合入门(三种方式)

    Springboot与Dubbo整合三种方式详解 整合环境: jdk:8.0 dubbo:2.6.2 springboot:2.1.5 项目结构: 1.搭建项目环境: (1)创建父项目与三个子项目,创 ...

  7. DB数据源之SpringBoot+Mybatis踏坑过程实录系列(一)

    DB数据源之SpringBoot+MyBatis踏坑过程(一) liuyuhang原创,未经允许进制转载 系列目录 DB数据源之SpringBoot+Mybatis踏坑过程实录(一) DB数据源之Sp ...

  8. Mybatis之旅第三篇-SqlMapConfig.xml全局配置文件解析

    一.前言 刚换工作,为了更快的学习框架和了解业务,基本每天都会加班,导致隔了几天没有进行总结,心里总觉得不安,工作年限越长越感到学习的重要性,坚持下去!!! 经过前两篇的总结,已经基本掌握了mybat ...

  9. Springboot & Mybatis 构建restful 服务三

    Springboot & Mybatis 构建restful 服务三 1 前置条件 成功执行完Springboot & Mybatis 构建restful 服务二 2 restful ...

随机推荐

  1. mysql5.7.X版本only_full_group_by问题解决

    一.出错原因 最近因为开发数据库与部署数据库版本不同,带来了几个问题,其中only_full_group_by问题是之前没有遇到的. 具体报错如下 [Err] 1055 - Expression #1 ...

  2. Hashlib加密,内置函数,安装操作数据库

    hashlib模块的md5加密: md5同样的内容加密后是一样的 md5加密后是不可逆的.即能加密,没办法解密. 撞库: 只是针对简单的,因为同样的内容加密后是一样的,难一点就不行了. 登录密码:注册 ...

  3. vscode设置中文语言

    https://jingyan.baidu.com/article/7e44095377c9d12fc1e2ef5b.html

  4. python基础知识总结(一)

    学完python很久了,一直想着写个学习总结,奈何懒癌晚期,现在才开始写.以下是我总结的一小部分python基础知识点的总结: 1.什么是解释型语言?什么是编译型编程语言? ''' 解释型语言:无需编 ...

  5. vs中 VMDebugger未能加载导致异常

    ,纠结了许久的一个问题,终于找到了解决 vs中 VMDebugger未能加载导致异常 错误号:80004005 搜了好多,没有一个给出完美的答案.   解决办法:工具->导入和导出设置,重置一下 ...

  6. Xmanager6 下载地址

    Xmanager-6标准版下载地址 http://xiazai.xshellcn.com/trial/Xmanager-6.0.0080_yy.exe Xmanager-6企业版下载地址 http:/ ...

  7. 关于css盒子模型和BFC的理解

    CSS盒子模型 包含元素内容(content).内边距(padding).边框(border).外边距(margin) 一般元素总宽度 = element的width+padding的左右边距+mar ...

  8. 导航栏动态添加act属性

    最近做了一个网站,需要设置导航栏的act属性,这里需要用到addClass以及removeClass: $('#topName li').removeClass('active'); $(this). ...

  9. Java-IO流之File操作和Properties操作

    java的File类主要是用来操作文件的元数据,稍作演示如下: 其中方法getAllJavaFile()是使用了过滤器FileFileter,这个过滤器只需要实现accept方法,判断什么样的文件返回 ...

  10. 在C++中,setw(int n)

    setw(int n)用来控制输出间隔例如:cout<<'s'<<setw(8)<<'a'<<endl;则在屏幕显示s        a //s与a之间 ...