栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring-AOP

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring-AOP

Spring-AOP
      • AOP
        • 概述
        • 动态代理
          • 代码示例 - 无参构造器
          • 代码示例 - 有参构造器
          • 运用简易AOP思想优化简易计算器
        • 面向切面编程 AOP
          • 快速入门
          • @Before
          • @AfterReturning
          • @AfterThrowing
          • @After
          • @Around
          • @Pointcut
        • 多个切面类的执行顺序

大家好呀!我是小笙,本节我来讲述一下AOP的机制以及实现

AOP

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问,被代理的对象可以是远程对象,创建开销大的对象或需要安全控制的对象

​ --《Head First 设计模式》

概述

​ AOP 英文:Aspect Oriented Programming 即 面向切面编程 和Ioc一样是一种设计思想

​ 目的:处理OOP在解决侵入式业务上的不足 (什么是侵入性业务?类似日志统计、性能分析等就属于侵入性业务)

​ 代理模式: 静态代理 动态代理


动态代理
  • 创建一个代理类,用来管理实现了该接口的类和对象
  • 可以在该接口类的实现方法前后添加业务并且可以动态创建

代码示例 - 无参构造器

VehicleProxyProvider 代理类

public class VehicleProxyProvider {

    
    private Vehicle targetVehicle;

    public VehicleProxyProvider(Vehicle targetVehicle) {
        this.targetVehicle = targetVehicle;
    }

    
    public Vehicle getProxy(){

        // 1.得到类加载器 ClassLoader
        ClassLoader classLoader = targetVehicle.getClass().getClassLoader();

        System.out.println(classLoader);

        // 2.要代理的对象接口信息,底层是用接口来完成调用
        Class[] interfaces = targetVehicle.getClass().getInterfaces();

        // 3.创建InvocationHandler,调用处理器/对象
        // 因为InvocationHandler是接口,我们可以通过匿名对象的方式来创建该对象
        InvocationHandler invocationHandler = new InvocationHandler(){
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("开始运行...");
                Object result = method.invoke(targetVehicle, args);
                System.out.println("停止运行...");
                return result;
            }
        };


        Vehicle v = (Vehicle)Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return v;
    }
}

被代理的接口以及实现该接口的对象

interface Vehicle {
    public void run();
}
public class Car implements Vehicle{
    @Override
    public void run() {
        System.out.println("小汽车哒哒哒...");
    }
}
public class Ship implements Vehicle{
    @Override
    public void run() {
        System.out.println("轮船嘟嘟嘟...");
    }
}

测试类

@Test
public void run(){
    Vehicle car = new Car();
    VehicleProxyProvider proxy = new VehicleProxyProvider(car);
    proxy.getProxy().run();
    //开始运行...
    //小汽车哒哒哒...
    //停止运行...
}
代码示例 - 有参构造器

加法减法的运算

CalculatorProxy代理类

public class CalculatorProxy {
    private Calculator targetCalculator;

    public CalculatorProxy(Calculator targetCalculator) {
        this.targetCalculator = targetCalculator;
    }

    public Calculator getProxy(){
        // 1.获取类加载器
        ClassLoader classLoader = targetCalculator.getClass().getClassLoader();

        // 2.获取接口信息
        Class[] interfaces = targetCalculator.getClass().getInterfaces();

        // 3.创建InvocationHandler,调用处理器/对象
        InvocationHandler invocationHandler = new InvocationHandler(){

            @Override
            public Double invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Double result = null;
                try {
                    // 前置通知
                    System.out.println("方法执行开始-日志-方法名-" + method.getName() + "-参数" + Arrays.toString(args));
                    result = (Double)method.invoke(targetCalculator, args);
                    // 返回通知
                    System.out.println("方法执行正常结束-日志-方法名-" + method.getName() + "-结果" + result);
                } catch (Exception e) {
                    e.printStackTrace();
                    // 如果反射执行方法时出现异常 - 异常通知
                    System.out.println("方法执行异常-日志-方法名-" + method.getName() + "异常信息" + e.getMessage());
                } finally {
                    // 最终通知
                    System.out.println("方法执行最终结束-日志-方法名-" + method.getName());
                }
                return result;
            }
        };
        return (Calculator) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

被代理的接口以及实现该接口的对象

public interface Calculator {
    public double calculatorResult(double num1,double num2);
}

public class Add implements Calculator{
    @Override
    public double calculatorResult(double num1, double num2) {
        return num1 + num2;
    }
}

public class Sub implements Calculator{
    @Override
    public double calculatorResult(double num1, double num2) {
        return num1 - num2;
    }
}

测试类

public class CalculatorTest {
    @Test
    public void test(){
        CalculatorProxy calculatorProxy = new CalculatorProxy(new Sub());
        calculatorProxy.getProxy().calculatorResult(10.0,2.0);
        //方法执行开始-日志-方法名-calculatorResult-参数[10.0, 2.0]
        //方法执行正常结束-日志-方法名-calculatorResult-结果8.0
        //方法执行最终结束-日志-方法名-calculatorResult
    }
}
运用简易AOP思想优化简易计算器

上述存在的问题:代码的耦合性太高,很难拓展代码等

将前置代码,后置代码,异常代码,最终代码脱离出代理类,更加灵活运用

public class LnsAOP {
    public static void before(Method method, Object[] args){
        // 前置通知
        System.out.println("方法执行开始-日志-方法名-" + method.getName() + "-参数" + Arrays.toString(args));
    }

    public static void after(Method method, Object[] args){
        // 前置通知
        System.out.println("方法执行开始-日志-方法名-" + method.getName() + "-参数" + Arrays.toString(args));
    }

    public static void abnormal(Method method, Exception e){
        // 如果反射执行方法时出现异常 - 异常通知
        System.out.println("方法执行异常-日志-方法名-" + method.getName() + "异常信息" + e.getMessage());
    }

    public static void last(Method method){
        // 最终通知
        System.out.println("方法执行最终结束-日志-方法名-" + method.getName());
    }
}
public class CalculatorProxy {
    private Calculator targetCalculator;

    public CalculatorProxy(Calculator targetCalculator) {
        this.targetCalculator = targetCalculator;
    }

    public Calculator getProxy(){
        // 1.获取类加载器
        ClassLoader classLoader = targetCalculator.getClass().getClassLoader();

        // 2.获取接口信息
        Class[] interfaces = targetCalculator.getClass().getInterfaces();

        // 3.创建InvocationHandler,调用处理器/对象
        InvocationHandler invocationHandler = new InvocationHandler(){

            @Override
            public Double invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Double result = null;
                try {
                    LnsAOP.before(method,args);
                    result = (Double)method.invoke(targetCalculator, args);
                    LnsAOP.after(method,args);
                } catch (Exception e) {
                    e.printStackTrace();
                    LnsAOP.abnormal(method,e);
                } finally {
                    LnsAOP.last(method);
                }
                return result;
            }
        };
        return (Calculator) Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
    }
}

面向切面编程 AOP

将被代理的类和相关日志杂糅在代理类中,动态组合

快速入门

首先引入四个包

配置类



    
    
    
    

被代理类

public interface Calculator {
    public double calculatorResult(double num1,double num2);
}
@Component // 用于注入到ioc容器
public class Add implements Calculator{
    @Override
    public double calculatorResult(double num1, double num2) {
        return num1 + num2;
    }
}
@Component
public class Sub implements Calculator{
    @Override
    public double calculatorResult(double num1, double num2) {
        return num1 - num2;
    }
}

切面类

@Component
@Aspect
public class AspectJ {
    
    @Before(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Add.calculatorResult(double,double))")
    public static void log1(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature() + ": 前置通知~");
    }

    @After(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Add.calculatorResult(double,double))")
    public static void log2(JoinPoint joinPoint){
        System.out.println(joinPoint.getTarget()+ ": 后置通知~");
    }

    @Before(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Sub.calculatorResult(double,double))")
    public static void log3(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature() + ": 前置通知~");
    }

    @After(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Sub.calculatorResult(double,double))")
    public static void log4(JoinPoint joinPoint){
        System.out.println(joinPoint.getTarget()+ ": 后置通知~");
    }
}

测试类

public class AOPTest {
    @Test
    public void test(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("bean4.xml");
        Calculator add = ioc.getBean("add", Calculator.class);
        Calculator sub = ioc.getBean("sub", Calculator.class);
        // 编译: Add类型 运行: Proxy代理类型
        System.out.println(add); // com.Al_tair.proxy.calculatorAOP.Add@61d6015a
        System.out.println(add.getClass()); // class com.sun.proxy.$Proxy14
        add.calculatorResult(3,2);
        sub.calculatorResult(4,2);
        //double com.Al_tair.proxy.calculatorAOP.Calculator.calculatorResult(double,double): 前置通知~
        //com.Al_tair.proxy.calculatorAOP.Add@61d6015a: 后置通知~
        //double com.Al_tair.proxy.calculatorAOP.Calculator.calculatorResult(double,double): 前置通知~
        //com.Al_tair.proxy.calculatorAOP.Sub@3315d2d7: 后置通知~
    }
}

注意:需要jdk1.8(版本不要太高)


@Before
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
    String value();

    String argNames() default "";
}

代码示例

@Before(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Add.calculatorResult(double,double))")
public static void log1(JoinPoint joinPoint){
    System.out.println(joinPoint.getSignature() + ": 前置通知~");
}

@AfterReturning
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {
    String value() default "";

    String pointcut() default "";

    String returning() default ""; // 接收方法返回值

    String argNames() default "";
}

代码示例

// returning 指定返回值的引用名
@AfterReturning(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Add.calculatorResult(double,double))",returning = "res")
public static void log2(JoinPoint joinPoint,Object res){
    System.out.println(res.toString()+ ": 返回通知~");
}

@AfterThrowing
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterThrowing {
    String value() default "";

    String pointcut() default "";

    String throwing() default ""; // 接收异常信息

    String argNames() default "";
}

代码示例

// throwing 指定异常引用名
@AfterThrowing(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Add.calculatorResult(double,double))", throwing = "throwable")
public void log3(JoinPoint joinPoint, Throwable throwable) {
    Signature signature = joinPoint.getSignature();
    System.out.println("异常通知~")

@After
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface After {
    String value();

    String argNames() default "";
}

代码示例

@After(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Sub.calculatorResult(double,double))")
public static void log4(JoinPoint joinPoint){
    System.out.println(joinPoint.getTarget()+ ": 后置通知~");
}

@Around
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Around {
    String value();

    String argNames() default "";
}

代码示例

//1. @Around: 表示这是一个环绕通知[完成其它四个通知的功能]
@Around(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Sub.calculatorResult(double,double))")
public Object doAround(ProceedingJoinPoint joinPoint) {
    Object result = null;
    String methodName = joinPoint.getSignature().getName();
    try {
        //1.相当于前置通知完成的事情
        Object[] args = joinPoint.getArgs();
        List argList = Arrays.asList(args);
        System.out.println("AOP环绕通知[-前置通知]" + methodName + "方法开始了--参数有:" + argList);
        //在环绕通知中一定要调用joinPoint.proceed()来执行目标方法
        result = joinPoint.proceed();
        //2.相当于返回通知完成的事情
        System.out.println("AOP环绕通知[-返回通知]" + methodName + "方法结束了--结果是:" + result);
    } catch (Throwable throwable) {
        //3.相当于异常通知完成的事情
        System.out.println("AOP环绕通知[-异常通知]" + methodName + "方法抛异常了--异常对象:" + throwable);
    } finally {
        //4.相当于最终通知完成的事情
        System.out.println("AOP环绕通知[-后置通知]" + methodName + "方法最终结束了...");
    }
    return result;
	//AOP环绕通知[-前置通知]calculatorResult方法开始了--参数有:[4.0, 2.0]
    //AOP环绕通知[-返回通知]calculatorResult方法结束了--结果是:2.0
    //AOP环绕通知[-后置通知]calculatorResult方法最终结束了
}
 

@Pointcut

思考:execution()是否有大量的重复代码,能否抽出共同点?

引出切入点,减少代码复用

public class AspectJ {
    @Pointcut(value = "execution(public double com.Al_tair.proxy.calculatorAOP.Add.calculatorResult(double,double))")
    public void myPointCut(){}
   
    @Before(value = "myPointCut()")
    public static void log1(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature() + ": 前置通知~");
    }

    @AfterReturning(value = "myPointCut()",returning = "res")
    public static void log2(JoinPoint joinPoint,Object res){
        System.out.println(res.toString()+ ": 后置通知~");
    }
}

多个切面类的执行顺序

优先级别:value的值越小,反而前置通知优先级别越高(类似Fileter过滤的规则)

结果分析 (order1 > order2)
double com.Al_tair.proxy.calculatorAOP.Calculator.calculatorResult(double,double): 前置通知2~
double com.Al_tair.proxy.calculatorAOP.Calculator.calculatorResult(double,double): 前置通知1~
5.0: 后置通知1~
5.0: 后置通知2~
double com.Al_tair.proxy.calculatorAOP.Calculator.calculatorResult(double,double): 前置通知2~
double com.Al_tair.proxy.calculatorAOP.Calculator.calculatorResult(double,double): 前置通知1~
com.Al_tair.proxy.calculatorAOP.Sub@22e357dc: 后置通知1~
com.Al_tair.proxy.calculatorAOP.Sub@22e357dc: 后置通知2~
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号