Spring AOP详解

Spring AOP简介

AOP(Aspect Oriented Programming)把软件系统分为两个部分:核心关注点和横切关注点

  • 核心关注点:业务处理的主要流程
  • 横切关注点:特点是经常发生在核心关注点的多处,而各处基本相似

典型应用场景:权限认证、日志

需求
  • 1.实现一个计算器接口,需要有计算加减乘除的方法
  • 2.为了便于日后核查问题,需要在日志总记录每次计算的入参及计算结果

基本接口

public interface ArithmeticCalculator {
    int add(int i, int j);

    int sub(int i, int j);

    int mul(int i, int j);

    int div(int i, int j);
}

基本接口的实现

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}
版本1--2B青年写法

手动撸代码,下下策

public class ArithmeticCalculatorImpl implements ArithmeticCalculator {

    public int add(int i, int j) {
        System.out.println("The method add begins with [" + i + ", " + j + "]");
        int result = i + j;
        System.out.println("The method add end with " + result);
        return result;
    }

    public int sub(int i, int j) {
        System.out.println("The method sub begins with [" + i + ", " + j + "]");
        int result = i - j;
        System.out.println("The method sub end with " + result);
        return result;
    }

    public int mul(int i, int j) {
        System.out.println("The method mul begins with [" + i + ", " + j + "]");
        int result = i * j;
        System.out.println("The method  mul end with " + result);
        return result;
    }

    public int div(int i, int j) {
        System.out.println("The method div begins with [" + i + ", " + j + "]");
        int result = i / j;
        System.out.println("The method div end with " + result);
        return result;
    }
}
版本2--文艺青年写法(代理模式)

基于代理模式的实现增加了一个代理类,在代理类中增加了前置方法和后置方法,不失为一种轻量级解决方案。

public class ArithmeticCaculatorProxy implements ArithmeticCalculator {
    //要代理的对象
    private ArithmeticCalculator arithmeticCalculator;

    public ArithmeticCaculatorProxy(ArithmeticCalculator arithmeticCalculator){
        this.arithmeticCalculator = arithmeticCalculator;
    }

    private void before(int ... args) {
        System.out.println("The method add begins with " + Arrays.toString(args));
    }

    private void after(int ... args) {
        System.out.println("The method add ends with " + Arrays.toString(args));
    }

    public int add(int i, int j) {
        before(i, j);
        int result = arithmeticCalculator.add(i, j);
        after(result);
        return result;
    }

    public int sub(int i, int j) {
        before(i, j);
        int result = arithmeticCalculator.sub(i, j);
        after(result);
        return result;
    }

    public int mul(int i, int j) {
        before(i, j);
        int result = arithmeticCalculator.mul(i, j);
        after(result);
        return result;
    }

    public int div(int i, int j) {
        before(i, j);
        int result = arithmeticCalculator.div(i, j);
        after(result);
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        ArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();
        ArithmeticCaculatorProxy proxy = new ArithmeticCaculatorProxy(arithmeticCalculator);
        proxy.add(1, 5);
        System.out.println("----------");
        proxy.sub(5, 3);
        System.out.println("----------");
        proxy.mul(3, 7);
        System.out.println("----------");
        proxy.div(9, 3);
    }
}
版本3--装B青年写法(动态代理)

这种方式简单粗暴,直接对代理对象下手。

public class ArithmeticCaculatorProxy {
    //要代理的对象
    private ArithmeticCalculator target;

    public ArithmeticCaculatorProxy(ArithmeticCalculator target){
        this.target = target;
    }

    public ArithmeticCalculator getProxy() {
        ArithmeticCalculator proxy = null;
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class[] interfaces = new Class[]{ArithmeticCalculator.class};
        InvocationHandler handler = new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                System.out.println("The method " + methodName + " begins with" + Arrays.asList(args));
                System.out.println("Invoke...");
                //执行方法
                Object result = method.invoke(target, args);
                //日志
                System.out.println("The method " + methodName + " ends with " + result);
                return result;
            }
        };
        proxy = (ArithmeticCalculator) Proxy.newProxyInstance(classLoader, interfaces, handler);
        return proxy;
    }
}

public class Main {
    public static void main(String[] args) {
        ArithmeticCalculator target = new ArithmeticCalculatorImpl();
        ArithmeticCalculator proxy = new ArithmeticCaculatorProxy(target).getProxy();
        proxy.add(1, 5);
        System.out.println("----------");
        proxy.sub(5, 3);
        System.out.println("----------");
        proxy.mul(3, 7);
        System.out.println("----------");
        proxy.div(9, 3);
    }
}
版本4--武林高手写法之一(Spring AOP注解)
package com.umgsai.aop.level4;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/**
 * Created by shangyidong on 17/6/29.
 */
//指定切面的优先级,当有多个切面时,数值越小优先级越高
@Order(1)
//把这个类声明为一个切面:需要把该类放入到IOC容器中。再声明为一个切面.
@Aspect
@Component
public class LoggingAspect {
    /**
     * 声明切入点表达式,一般在该方法中不再添加其他代码。
     * 使用@Pointcut来声明切入点表达式。
     * 后面的通知直接使用方法名来引用当前的切入点表达式。
     */
    @Pointcut("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))")
    public void declareJoinPointExpression() {}

    /**
     *前置通知,在目标方法开始之前执行。
     *@Before("execution(public int com.spring.aop.impl.ArithmeticCalculator.add(int, int))")这样写可以指定特定的方法。
     * @param joinpoint
     */
    @Before("declareJoinPointExpression()")
    //这里使用切入点表达式即可。后面的可以都改成切入点表达式。如果这个切入点表达式在别的包中,在前面加上包名和类名即可。
    public void beforeMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("前置通知:The method "+ methodName +" begins with " + args);
    }

    /**
     *后置通知,在目标方法执行之后开始执行,无论目标方法是否抛出异常。
     *在后置通知中不能访问目标方法执行的结果。
     * @param joinpoint
     */
    @After("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(int, int))")
    public void afterMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        //List<Object>args = Arrays.asList(joinpoint.getArgs());  后置通知方法中可以获取到参数
        System.out.println("后置通知:The method "+ methodName +" ends ");
    }

    /**
     *返回通知,在方法正常结束之后执行。
     *可以访问到方法的返回值。
     * @param joinpoint
     * @param result 目标方法的返回值
     */
    @AfterReturning(value="execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))", returning="result")
    public void afterReturnning(JoinPoint joinpoint, Object result) {
        String methodName = joinpoint.getSignature().getName();
        System.out.println("返回通知:The method "+ methodName +" ends with " + result);
    }

    /**
     *异常通知。目标方法出现异常的时候执行,可以访问到异常对象,可以指定在出现特定异常时才执行。
     *假如把参数写成NullPointerException则只在出现空指针异常的时候执行。
     * @param joinpoint
     * @param e
     */
    @AfterThrowing(value="execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))", throwing="e")
    public void afterThrowing(JoinPoint joinpoint, Exception e) {
        String methodName = joinpoint.getSignature().getName();
        System.out.println("异常通知:The method "+ methodName +" occurs exception " + e);
    }

    /**
     * 环绕通知类似于动态代理的全过程,ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
     * @param point 环绕通知需要携带ProceedingJoinPoint类型的参数。
     * @return 目标方法的返回值。必须有返回值。
     */
     /*不常用
    @Around("execution(public int com.umgsai.aop.level4.ArithmeticCalculator.*(..))")
    public Object aroundMethod(ProceedingJoinPoint point) {
        Object result = null;
        String methodName = point.getSignature().getName();
        try {
            //前置通知
            System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs()));
            //执行目标方法
            result = point.proceed();
            //翻译通知
            System.out.println("The method "+ methodName +" ends with " + result);
        } catch (Throwable e) {
            //异常通知
            System.out.println("The method "+ methodName +" occurs exception " + e);
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method "+ methodName +" ends");
        return result;
    }
    */
}

public class Main {
    public static void main(String[] args) {
        //创建spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-level4.xml");
        //从IOC容器中获取bean实例
        ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class);
        int result = arithmeticCalculator.add(4, 6);
        System.out.println(result);
        result = arithmeticCalculator.sub(4, 6);
        System.out.println(result);
        result = arithmeticCalculator.mul(4, 6);
        System.out.println(result);
        result = arithmeticCalculator.div(4, 0);
        System.out.println(result);
    }
}

此时需要在Spring的配置文件中加入以下注解

    <!-- 使AspectJ注解起作用:自动为匹配的类生产代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
Spring AOP注解参数详解
  • @Aspect 声明切面类,放入IOC容器中
  • @Order 指定切面的优先级,当有多个切面时,数值越小优先级越高
  • @Pointcut 声明切入点
  • @Before 前置通知
  • @After 后置通知
  • @AfterReturning 返回通知
  • @AfterThrowing 异常通知
  • @Around 环绕通知
版本5--武林高手写法之二(Spring AOP配置)
@Component("loggingAspect")
public class LoggingAspect {

    public void beforeMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinpoint.getArgs());
        System.out.println("前置通知:The method "+ methodName +" begins with " + args);
    }

    public void afterMethod(JoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName();
        //List<Object>args = Arrays.asList(joinpoint.getArgs());  后置通知方法中可以获取到参数
        System.out.println("后置通知:The method "+ methodName +" ends ");
    }

    public void afterReturning(JoinPoint joinpoint, Object result) {
        String methodName = joinpoint.getSignature().getName();
        System.out.println("返回通知:The method "+ methodName +" ends with " + result);
    }

    public void afterThrowing(JoinPoint joinpoint, Exception e) {
        String methodName = joinpoint.getSignature().getName();
        System.out.println("异常通知:The method "+ methodName +" occurs exception " + e);
    }

    public Object aroundMethod(ProceedingJoinPoint point) {
        Object result = null;
        String methodName = point.getSignature().getName();
        try {
            //前置通知
            System.out.println("The method "+ methodName +" begins with " + Arrays.asList(point.getArgs()));
            //执行目标方法
            result = point.proceed();
            //翻译通知
            System.out.println("The method "+ methodName +" ends with " + result);
        } catch (Throwable e) {
            //异常通知
            System.out.println("The method "+ methodName +" occurs exception " + e);
            throw new RuntimeException(e);
        }
        //后置通知
        System.out.println("The method "+ methodName +" ends");
        return result;
    }
}

@Component("validationAspect")
public class ValidationAspect {
    public void validateArgs(JoinPoint joinPoint) {
        System.out.println("validate:" + Arrays.asList(joinPoint.getArgs()));
    }
}

public class Main {
    public static void main(String[] args) {
        //创建spring IOC容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-level5.xml");
        //从IOC容器中获取bean实例
        ArithmeticCalculator arithmeticCalculator = applicationContext.getBean(ArithmeticCalculator.class);
        int result = arithmeticCalculator.add(4, 6);
        System.out.println(result);
        result = arithmeticCalculator.sub(4, 6);
        System.out.println(result);
        result = arithmeticCalculator.mul(4, 6);
        System.out.println(result);
        //result = arithmeticCalculator.div(4, 0);
        //System.out.println(result);
    }
}

xml配置文件如下

    <!-- 配置AOP -->
    <aop:config>
        <!-- 配置切点表达式 -->
        <aop:pointcut expression="execution(* com.umgsai.aop.level5.ArithmeticCalculator.*(..))" id="pointcut"/>
        <!-- 配置切面及通知,使用order指定优先级 -->
        <aop:aspect ref="loggingAspect" order="1">
            <!-- 环绕通知 -->
            <!--
                <aop:around method="aroundMethod" pointcut-ref="pointcut"/>
            -->
            <!-- 前置通知 -->
            <aop:before method="beforeMethod" pointcut-ref="pointcut"/>
            <!-- 后置通知 -->
            <aop:after method="afterMethod" pointcut-ref="pointcut"/>
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing"  pointcut-ref="pointcut" throwing="e"/>
            <!-- 返回通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut" returning="result"/>

        </aop:aspect>
        <aop:aspect ref="validationAspect" order="2">
            <!-- 前置通知 -->
            <aop:before method="validateArgs" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

这里有两个切面类,通过配置文件中的order即可控制优先级.

项目源码: http://git.oschina.net/umgsai/spring-aop

转载于:https://www.cnblogs.com/umgsai/p/7131606.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值