zoukankan      html  css  js  c++  java
  • Spring使用注解实现AOP

    一、AspectJ概述

    AspectJ是一个面向切面的框架,它扩展了Java语言、定义了AOP语法,能够在编译期提供代码的织入,它提供了一个专门的编译期用来生成遵守字节编码规范的Class文件。

    @Aspect是AspectJ 5新增的功能,使用JDK 5.0注解技术和正规的AspectJ切点表达式语言描述切面。因此在使用@Aspect之前,需要保证所使用的JDK是5.0或更高版本,否则将无法使用注解技术。

    Spring通过集成AspectJ实现了以注解的方式定义切面,大大减轻了配置文件的工作量。此外,因为Java的反射机制无法获取方法参数名,Spring还需要利用轻量级的字节码处理框架asm(已集成在Spring Core模块中)处理@AspectJ中所描述的方法参数名。

    二、@Aspect(定义切面)、@Before(前置增强)、@AfterReturning(后置增强)注解配置切面

    1、使用注解定义切面以实现日志切面功能,如下

    package edu.cn.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Arrays;
    
    @Aspect
    public class UserServiceLogger {
        private static final Logger log = LoggerFactory.getLogger(UserServiceLogger.class);
    
        @Before("execution(* edu.cn.service.UserService.*(..))")
        public void before(JoinPoint jp){
            log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                    + "方法。方法入参:" + Arrays.toString(jp.getArgs()));
        }
    
        @AfterReturning(pointcut = "execution(* edu.cn.service.UserService.*(..))", returning = "returnValue")
        public void afterReturning(JoinPoint jp, Object returnValue){
            log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                    + "方法。方法返回值:" + returnValue);
        }
    
    } 

    @Aspect等注解在aspectjweaver依赖下。在上述代码中,使用@Aspect注解将UserServiceLogger定义为切面,并且使用@Before注解将before()方法定义为前置增强,使用@AfterReturning方法将afterReturning()方法定于为后置增强

    为了能够获得当前连接点的信息,在增强方法中添加了JoinPoint类型的参数,Spring会自动注入该实例。

    对于后置增强,还可以定义一个参数用于接受目标方法的返回值。需要注意的是,必须在@AfterReturning注解中通过returning属性指定该参数的名称,Spring会将目标方法的返回值赋值给指定名称的参数。

    1.1、对于相同的切入点要求,可以统一定义,以便于重用和维护,如下

    package edu.cn.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Arrays;
    
    @Aspect
    public class UserServiceLogger {
        private static final Logger log = LoggerFactory.getLogger(UserServiceLogger.class);
        
        @Pointcut("execution(* edu.cn.service.UserService.*(..))")
        public void pointcut(){}
    
        @Before("pointcut()")
        public void before(JoinPoint jp){
            log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                    + "方法。方法入参:" + Arrays.toString(jp.getArgs()));
        }
    
        @AfterReturning(pointcut = "pointcut()", returning = "returnValue")
        public void afterReturning(JoinPoint jp, Object returnValue){
            log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                    + "方法。方法返回值:" + returnValue);
        }
    
    }

    切入点表达式可以使用@Pointcut注解来表示,而切入点签名则需要通过一个普通的方法定义来提供,如上述代码中的pointcut()方法,作为切入点签名的方法必须返回void类型。定义好切入点后,就可以使用“pointcut()”签名进行引用

    2、切面定义完后,还需要在Spring配置文件中完成织入工作,如下

    只需在配置文件中添加<aop:aspectj-autoproxy/>元素,就可以启用对于@AspectJ注解的支持,Spring将自动为匹配的Bean创建代理

    为了注册定义好的切面,还需在Spring配置文件中声明UserServiceLogger的一个实例。如果不需要被其他Bean引用,可以不指定id属性

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd 
                http://www.springframework.org/schema/aop 
                http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:component-scan base-package="service,dao"/> <bean class="edu.cn.service.UserServiceLogger"></bean> <aop:aspectj-autoproxy/> </beans>

    三、@AfterThrowing(异常抛出增强)、@After(最终增强)、@Around(环绕增强)注解进行增强的配置

    1、@AfterThrowing(异常抛出增强)

    使用@AfterThrowing注解可以定义异常抛出增强。如果需要获取抛出的异常,可以为增强方法声明相关类型的参数,并通过@AfterThrowing注解的throwing属性指定该参数名称,Spring会为其注入从目标方法抛出的异常实例,如下

    package edu.cn.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterThrowing;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    @Aspect
    public class ErrorLogger {
        private static final Logger log = LoggerFactory.getLogger(ErrorLogger.class);
    
        @AfterThrowing(pointcut = "execution(* edu.cn.service.UserService.*(..))", throwing = "e")
        public void afterThrowing(JoinPoint jp, RuntimeException e){
            log.error(jp.getSignature().getName() + "方法方法异常:" + e);
        }
    }

    2、@After(最终增强)

    使用@After注解可以定义最终增强

    package edu.cn.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    @Aspect
    public class AfterLogger {
        private static final Logger log = LoggerFactory.getLogger(AfterLogger.class);
        
        @After("execution(* edu.cn.service.UserService.*(..))")
        public void afterLogger(JoinPoint jp){
            log.info(jp.getSignature().getName() + "方法结束执行。");
        }
    }

    3、@Around(环绕增强)

    使用@Around注解可以定义环绕增强。通过为增强方法声明ProceedingJoinPoint类型的参数,可以获得连接点信息。通过它的proceed()方法可以调用真正的目标方法,从而实现对连接点的完全控制。

    package edu.cn.service;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Arrays;
    
    @Aspect
    public class AroundLogger {
        private static final Logger log = LoggerFactory.getLogger(AroundLogger.class);
        
        @Around("execution(* edu.cn.service.UserService.*(..))")
        public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable {
            log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName() 
                    + "方法。方法入参:" + Arrays.toString(jp.getArgs()));
            try {
                Object result = jp.proceed();
                log.info("调用" + jp.getTarget() + "的" + jp.getSignature().getName()
                + "方法。方法返回值:" + result);
            } catch (Throwable e) {
                log.error(jp.getSignature().getName() + "方法发生异常:" + e);
                throw e;
            }finally {
                log.info(jp.getSignature().getName() + "方法结束执行。");
            }
        }
    }
  • 相关阅读:
    Crumpet – 使用很简单的响应式前端开发框架
    太赞了!超炫的页面切换动画效果【附源码下载】
    你见过动物是怎么笑的吗?赶紧来看看【组图】
    Treed – 基于拖放 操作的,强大的树形编辑器
    Perfect Scrollbar – 完美的 jQuery 滚动条插件
    Jeet – 先进,直观,灵活的 CSS 网格系统
    舌尖上的设计!10个美味的餐馆和食品网站
    推荐15款最好的 Twitter Bootstrap 开发工具
    Web 前端开发人员和设计师必读精华文章【系列二十五】
    Seen.js – 使用 SVG 或者 Canvas 渲染 3D 场景
  • 原文地址:https://www.cnblogs.com/yanguobin/p/11707538.html
Copyright © 2011-2022 走看看