AOP-实现日志管理

AOP

实现aop的四种方式
  • AspectJ
  • AspectWerkz
  • SpringFramework
  • JBoss

AOP核心概念

  • 1、横切关注点
    对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点
  • 2、切面(aspect)
    类是对物体特征的抽象,切面就是对横切关注点的抽象
  • 3、连接点(joinpoint)
    被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造
  • 4、切入点(pointcut)
    对连接点进行拦截的定义
  • 5、通知(advice)
    所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类
  • 6、目标对象
    代理的目标对象
  • 7、织入(weave)
    将切面应用到目标对象并导致代理对象创建的过程
  • 8、引入(introduction)
    在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段
切入点语法
  • 1.@Pointcut("下面的东西")
任何类的任何返回值的任何方法
- 1. execution(public * *(..))   

任何类的set开头的方法
- 2.execution(* set*(..))       

任何返回值的规定类里面的方法
- 3.execution(* com.example.demo.AccountService.*(..))

任何返回值的,规定包或者规定包子包的任何类任何方法
- 4.execution(* com.example.demo.service..*.*(..)) 

  • 2.@Pointcut("@annotation(自定义注解)")

环绕通知(前置通知+执行方法+后置通知)语法

  • @Around("logPointCut()") //@Pointcut打在哪个方法,就填哪个方法
  • @Around(execution(* com.example.demo.service...(..)))

注意:执行方法:method(ProceedingJoinPoint joinPoint),要带joinpoint这个参数

AspectJ

  • AspectJ是目前最完善的AOP语言
  • AspectJ是对Java编程语言的扩展,通过增加了一些新的构造块支持对横切关注点的模块化封装,通过对源代码级别的代码混合实现织入,是一种典型的使用静态织入的AOP实现机制。
  • AspectJ提供了两种横切实现机制
  1. 动态横切(DynamicCrosscutting)
  2. 另一种称为静态横切(StaticCrosscutting)。
简要api
Joinpoint
- java.lang.Object[] getArgs()/*获取连接点方法运行时的入参列表*/
- Signature getSignature() /*获取连接点的方法签名对象*/
- java.lang.Object getTarget() //获取连接点所在的目标对象
- java.lang.Object getThis() //获取代理对象本身
ProceedingJoinPoint

ProceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法

//通过反射执行目标对象的连接点处的方法
- java.lang.Object proceed() throws Throwable
//通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
- java.lang.Object proceed(java.lang.Object[] args) throws Throwable

示例代码

使用aop进行日志管理(自定义注解方式)
自定义注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log{

    String value() default ""; //用于描述方法作用

    /**
     * 是否忽略返回值,仅方法上有效
     * @return
     */
    boolean ignoreReturn() default false;
}
切面类
/**
 * 日志切面
 */
@Aspect
//切面优先级
@Order(100)
@Component
public class LogAspect {
    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
    private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
    private static final String STRING_START = "\n--------------------------->\n";
    private static final String STRING_END = "\n<----------------------------\n";
/**
 * 切点
 */
/**
 * 不使用注解方式打切点
 *  //@Pointcut("execution (* com.example.demo.web..*(..))")")
 *  表示在使用Log注解的地方切入
 */
    @Pointcut("@annotation(Log)")
    public void logPointCut() {
    }

    //@Around("logPointCut()")---> 用于controller层
    public Object controllerAround(ProceedingJoinPoint joinPoint) {
        try {
            return printLog(joinPoint);
        } catch (Throwable throwable) {
            log.error(throwable.getMessage(), throwable);
            return true;
        }
    }
    //通知:拦截到连接点之后要执行的代码
        @Around("logPointCut()")
    private Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
        //获取连接点的方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法签名里的方法:方法签名里有两个方法:getReturnType   getMethod
        Method method = signature.getMethod();
        //获取类
        Class<?> targetClass = method.getDeclaringClass();
        StringBuffer classAndMethod = new StringBuffer();

        // 获取目标方法上的Log注解
        Log methodAnnotation = method.getAnnotation(Log.class);

        // 判断是否有LOG注解以及是否带有ignore参数
        if (methodAnnotation != null) {
            classAndMethod.append(methodAnnotation.value());
        }
        //拼接目标切入的类名称和方法名称
        String target = targetClass.getName() + "#" + method.getName();
        // 请求参数转JSON,对日期进行格式转换并打印出所有为null的参数
        String params = JSONObject.toJSONStringWithDateFormat(joinPoint.getArgs(), dateFormat, SerializerFeature.WriteMapNullValue);
        //日志打印拼接的调用信息
        log.info(STRING_START + "{} 开始调用--> {} 参数:{}", classAndMethod.toString(), target, params);

        long start = System.currentTimeMillis();
        //proceed()通过反射执行目标对象的连接点处的方法;
        Object result = joinPoint.proceed();
        long timeConsuming = System.currentTimeMillis() - start;
        if (methodAnnotation != null && methodAnnotation.ignoreReturn()) {
            log.info("\n{} 调用结束<-- {} 耗时:{}ms" + STRING_END, classAndMethod.toString(), target, timeConsuming);
            return result;
        }
        // 响应参数转JSON,对日期进行格式转换并打印出所有为null的参数
        log.info("\n{} 调用结束<-- {} 返回值:{} 耗时:{}ms" + STRING_END, classAndMethod.toString(), target, JSONObject.toJSONStringWithDateFormat(result, dateFormat, SerializerFeature.WriteMapNullValue), timeConsuming);
      
        return result;
    }
}

实现类
@Service
@Slf4j
public class UserServiceImpl implements UserService{

    @Autowired
private UserMapper usermapper;
    @Override
    @Log
    public User getUser(Integer id) {
        User user=usermapper.getById(id);
        log.info("message: {}",user);
        return usermapper.getById(id);
    }
}

控制台输出
--------------------------->
 开始调用--> com.example.demo.service.serviceimpl.UserServiceImpl#getUser 参数:[2]
2019-01-19 21:24:57.027 [http-nio-9091-exec-1] INFO  com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-01-19 21:24:57.406 [http-nio-9091-exec-1] INFO  com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-01-19 21:24:57.413 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==>  Preparing: SELECT * FROM user u WHERE u.id = ? 
2019-01-19 21:24:57.436 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Parameters: 2(Integer)
2019-01-19 21:24:57.468 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : <==      Total: 1
2019-01-19 21:24:57.470 [http-nio-9091-exec-1] INFO  c.e.d.s.serviceimpl.UserServiceImpl : message: User(id=2, name=321, birthday=2019-01-17, address=4)
2019-01-19 21:24:57.471 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==>  Preparing: SELECT * FROM user u WHERE u.id = ? 
2019-01-19 21:24:57.471 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Parameters: 2(Integer)
2019-01-19 21:24:57.484 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : <==      Total: 1
2019-01-19 21:24:57.516 [http-nio-9091-exec-1] INFO  c.example.demo.config.log.LogAspect : 
 调用结束<-- com.example.demo.service.serviceimpl.UserServiceImpl#getUser 返回值:{"address":"4","birthday":"2019-01-17","id":2,"name":"321"} 耗时:478ms
<----------------------------
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容