spring切面-AOP见解

对于aop的理解是对业务代码的增强,比如,记录日志,事务控制,异常处理等等,aop是采用动态代理实现的一种技术,也可以启到降低代码的耦合的作用。

aop具体的概念不做解释,切点,切面这些都可以查到,这里只做代码的实现

切入点表达式execution

/**
 * execution表达式-切入点表达式:
 *  语法:execution(修饰符  返回值  包.类.方法名(参数) throws异常)
 *      修饰符:一般省略不写,
 *          示例: public 公共方法
 *               * 任意方法
 *      返回值:不可省略,必须
 *          示例:void 没有返回值
 *               String 返回字符串
 *               * 任意
 *      包:一般标注切面应用范围
 *          示例 :com.demo.controller 固定包
 *                com.demo.controller.. controller包下的所有子包,包括自己
 *                com.demo.*.service demo下的任意service,如com.demo.co.service
 *      类:与包结合限定范围
 *          示例:TestController 固定应用某一个类
 *               *.Controller 以Controller结尾的所有的类
 *               Test.* 以Test开头的所有的类
 *               * 任意类
 *      方法名:与包+类结合使用,不可省略
 *             示例:beforeMethod 固定某个方法
 *                  before.* 以before开头的方法
 *                  *.after 以after结尾的方法
 *                  * 任意方法
 *      ():方法参数
 *          示例:() 无参
 *               (int) 一个int类型参数
 *               (int,String) 一个int类型和一个string类型参数
 *               (..) 任意参数
 *      throws异常:一般省略不写
 *          示例:throws Exception 抛出可抛异常
 *
 */

五种通知代码实现 

@Aspect //表明这个类是切面类
@Component //交由spring管理
public class AspectCommon {
    //记录日志
    private static Logger LOGGER = LoggerFactory.getLogger(AspectCommon.class);

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss SSS");

    /**
     * 前置通知 在切点方法执行之前执行
     * @param joinPoint
     */
    @Before(value = "execution(* com.demo.controller..*.*(..))")
    public void beforeMethod(JoinPoint joinPoint){
        String sdate =sdf.format(new Date());
        Signature signature = joinPoint.getSignature();
        LOGGER.info("{}-方法执行开始,开始时间:{}",signature.getName(),sdate);
    }

    /**
     * 后置通知,在切点方法执行之后执行,但获取不到方法的返回值
     * @param joinPoint
     */
    @After(value = "execution(* com.demo.controller..*.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String sdate =sdf.format(new Date());
        Signature signature = joinPoint.getSignature();
        LOGGER.info("{}-方法执行结束,结束时间:{}",signature.getName(),sdate);
    }

    /**
     * 环绕通知,相当于@before和@after的组合,在切点方法执行之前和之后执行,可以获取到返回值
     *  ProceedingJoinPoint是JoinPoint实现子类
     * @param proceedingJoinPoint
     */
    @Around(value = "execution(* com.demo.controller..*.*(..))")
    public void aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object result=null;
        // 当前时间
        long startTime = System.currentTimeMillis();
        String sdate =sdf.format(startTime);
        // 待执行的方法
        Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
        LOGGER.info("{}-方法执行开始,参数-{},结束时间:{}",method.getName(),proceedingJoinPoint.getArgs(),sdate);
        try {
            //转到切点方法执行
            result = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        String eDate = sdf.format(endTime);
        // 方法耗时日志输出
        LOGGER.info("{}-方法执行结束,结束时间:{},耗时:{},返回结果【{}】", method.getName(), eDate,(endTime-startTime),result);
    }


    /**
     * 返回通知:切点方法正常执行结束后执行,可以获取到返回值
     *   注意:这里不能使用ProceedingJoinPoint,不然会报 ProceedingJoinPoint is only supported for around advice
     * @param joinPoint
     */
    @AfterReturning(value = "execution(* com.demo.controller..*.*(..))",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        LOGGER.info("{}-方法执行结束,返回结果:【{}】", name, result);
    }

    /**
     * 异常通知:在切点方法执行出异常时执行,可返回异常信息
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "execution(* com.demo.controller..*.*(..))",throwing = "e")
    public void afterReturningMethod(JoinPoint joinPoint,Exception e){
        String name = joinPoint.getSignature().getName();
        LOGGER.info("{}-方法执行结束,异常信息:【{}】", name, e.getMessage());
    }

}

代码基于spring,需要在spring的配置文件中开启对aop的支持,否则aop不起效果,如果是springboot项目,是不需要在类上标注对应的注解@EnableAspectJAutoProxy

因为在AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说只要引入了AOP依赖后,默认已经增加了@EnableAspectJAutoProxy

附@PointCut参数类型和区别:

表达式标签

  • execution():用于匹配方法执行的连接点
  • args(): 用于匹配当前执行的方法传入的参数为指定类型的执行方法
  • this(): 用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;
  • target(): 用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;
  • within(): 用于匹配指定类型内的方法执行;
  • @args():于匹配当前执行的方法传入的参数持有指定注解的执行;
  • @target():用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;
  • @within():用于匹配所以持有指定注解类型内的方法;
  • @annotation:用于匹配当前执行方法持有指定注解的方法;

其中execution 是用的最多的,

within和@within

  • within(com.test.spring.aop.pointcutexp.*) pointcutexp包里的任意类.
  • within(com.test.spring.aop.pointcutexp…*) pointcutexp包和所有子包里的任意类.
  • @within(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的所有类的任意方法.

this

  • this(com.test.spring.aop.pointcutexp.Intf) 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.

当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型.

@target

  • @target(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的所有类的任意方法.

@annotation

  • @annotation(org.springframework.transaction.annotation.Transactional) 带有@Transactional标注的任意方法.

@within和@target针对类的注解,@annotation是针对方法的注解

args 和 @args

  • @args(org.springframework.transaction.annotation.Transactional) 参数带有@Transactional标注的方法.
  • args(String) 参数为String类型(运行是决定)的方法.

例子

@Pointcut(value="args(param)", argNames="param")  
private void pointcut1(String param){}  
@Pointcut(value="@annotation(secure)", argNames="secure")  
private void pointcut2(Secure secure){}  
      
@Before(value = "pointcut1(param) && pointcut2(secure)", argNames="param, secure")  
public void before6(JoinPoint jp, String param, Secure secure) {  
……  
}


@Pointcut("execution(* com.savage.aop.MessageSender.*(..)) && args(param)")
public void log(){
}
 
@Before("log(String param)") 
public void beforeLog(){
     //todo something....
}
 
//等同于
@Before("execution(* com.savage.aop.MessageSender.*(..)) && args(param)") 
public void beforeLog(){
     //todo something....
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值