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

spring的动态代理aop原理以及应用

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

spring的动态代理aop原理以及应用

spring的动态代理aop
  • 一、动态代理
    • 1、被代理的接口
    • 2、被代理的接口的实现类
    • 3、利用反射创建代理对象
    • 4、日志增强的方法
    • 5、测试及结果
  • 二、AOP
    • 1、编写代理的类
    • 2、注解配置两个切面类
    • 3、测试以及结果
    • 4、xml配置
  • 三、事务增强
    • 1、配置事务增强applicationcontext.xml
    • 2、service层需要添加事务的方法
    • 3、事务传播行为REQUIRES_NEW和REQUIRED实例

一、动态代理
  • 动态代理基于接口代理或者子类代理
1、被代理的接口
public interface Calculator {
    public int add(int x, int y);
    public int sub(int x, int y);
    public int mul(int x, int y);
    public int div(int x, int y);
}
2、被代理的接口的实现类
public class MyCalculator implements Calculator{
    @Override
    public int add(int x, int y) {
        return x+y;
    }
    @Override
    public int sub(int x, int y) {
        return x-y;
    }
    @Override
    public int mul(int x, int y) {
        return x*y;
    }
    @Override
    public int div(int x, int y) {
        return x/y;
    }
}
3、利用反射创建代理对象
public class CalculatorProxy {
    public static Calculator getCalculatorProxy(Calculator calculator) {
        //获取代理对象的类加载器
        ClassLoader loader = calculator.getClass().getClassLoader();
        //获取代理对象所有实现的接口
        Class[] interfaces = calculator.getClass().getInterfaces();
        //代理对象执行的handle
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //前置通知
                LogUtils.beforeRun(method, args);
                //方法运行
                Object o = null;
                try {
                    o = method.invoke(calculator, args); //运行方法, calculator基于接口代理
                    //后置通知
                    LogUtils.ReturnRun(method, args);
                } catch (Exception e) {
                    //异常通知
                    LogUtils.ExceptionRun(method, args);
                } finally {
                    //最终通知
                    LogUtils.AfterRun(method, args);
                }
                return o; //参数返回值
            }
        };
        Object o = Proxy.newProxyInstance(loader, interfaces, h);
        return (Calculator) o;
    }
}
4、日志增强的方法
public class LogUtils {
    public static void beforeRun(Method method, Object[] args) {
        System.out.println("方法" + method.getName() + "运行前日志" + Arrays.asList(args));
    }
    public static void ReturnRun(Method method, Object[] args) {
        System.out.println("方法" + method.getName() + "运行返回日志" + Arrays.asList(args));
    }
    public static void ExceptionRun(Method method, Object[] args) {
        System.out.println("方法" + method.getName() + "运行出现异常日志" + Arrays.asList(args));
    }
    public static void AfterRun(Method method, Object[] args) {
        System.out.println("方法" + method.getName() + "运行结束日志" + Arrays.asList(args));
    }
}
5、测试及结果
@Test
public void proxyCalculator() {
    Calculator calculator = new MyCalculator();
    //获取代理对象
    Calculator proxy = CalculatorProxy.getCalculatorProxy(calculator);
    //这里proxy实际上是一个代理类型,class com.sun.proxy.$Proxy4,而不是Calculator类型
    //如果是Calculator类型,则无法对calculator的方法进行增强
    System.out.println(proxy.getClass()); 
    proxy.add(1, 2);
    System.out.println("=====================================================================");
    proxy.sub(1, 2);
    System.out.println("=====================================================================");
    proxy.mul(1, 2);
    System.out.println("=====================================================================");
    proxy.div(6, 0);
    System.out.println("=====================================================================");
}

二、AOP 1、编写代理的类
public class Calculator { //这里没有继承父类或者实现接口,aop会采用cglib代理常见该类的代理对象
    public int add(int x, int y) {
        System.out.println("执行目标方法");
        return x+y;
    }
    public int sub(int x, int y) {
        System.out.println("执行目标方法");
        return x-y;
    }
    public int mul(int x, int y) {
        System.out.println("执行目标方法");
        return x*y;
    }
    public int div(int x, int y) {
        System.out.println("执行目标方法");
        return x/y;
    }
}
2、注解配置两个切面类
  • Pointcut 配置切入点表达式
  • joinPoint 获取当前增强方法的信息
    • joinPoint.getArgs(); 获取增强方法参数列表
    • getSignature().getName(); 获取方法的名称
  • @Before(“cutPoint()”) 前置通知
  • @AfterReturning(value = “cutPoint()”,returning = “result”) 后置通知
    • returning可以用来接收返回值
  • @AfterThrowing(value = “cutPoint()”, throwing = “exception”) 异常通知
    • throwing用来指定只有是当前异常类型才会执行异常通知方法,所以一般尽量将异常往大的写
  • @Around(value = “cutPoint()”) 环绕通知
@Aspect //标记这是一个切面类
@Component //注入容器中
public class Logs {
    //配置全局的增强方法
    @Pointcut("execution(public int com.myproject.service.Calculator.*(..))")
    public void cutPoint() { }

    
    //前置通知
    @Before("cutPoint()")
    public static void beforeRun(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[logs]" + "方法" + method + "前置通知" + Arrays.asList(args));
    }

    //返回通知
    //returning可以用来接收返回值
    @AfterReturning(value = "cutPoint()",returning = "result")
    public static void ReturnRun(JoinPoint joinPoint, Object result) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[logs]" +"方法" + method + "返回通知" + Arrays.asList(args) + "返回值为" + result);
    }

    //异常通知
    //throwing用来指定只有是当前异常类型才会执行异常通知方法,所以一般尽量将异常往大的写
    @AfterThrowing(value = "cutPoint()", throwing = "exception")
    public static void ExceptionRun(JoinPoint joinPoint, NullPointerException exception) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[logs]" + "方法" + method + "异常日志通知" + Arrays.asList(args));
    }

    //后置通知
    @After("cutPoint()")
    public static void AfterRun(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[logs]" + "方法" + method + "后置通知" + Arrays.asList(args));
    }

    //环绕通知
    @Around(value = "cutPoint()")
    public static Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //方法执行返回值
        Object result = null;
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        try {
            //前置通知
            System.out.println("[logs环绕]" + "方法" + method + "前置通知" + Arrays.asList(args));
            //底层实际通过反射对目标方法进行调用
            result = joinPoint.proceed(args);
            System.out.println("[logs环绕]" + "方法" + method + "返回通知" + Arrays.asList(args) + "返回值为" + result);
        } catch (Exception e) {
            System.out.println("[logs环绕]" + "方法" + method + "异常通知" + Arrays.asList(args));
            throw new RuntimeException();
        } finally {
            System.out.println("[logs环绕]" + "方法" + method + "后置通知" + Arrays.asList(args));
        }
        //返回目标方法执行的结果
        return result;
    }
}
@Aspect //标记这是一个切面类
@Component //注入容器中
public class Valid { //参数验证的增强
    //配置全局的增强方法
    @Pointcut("execution(public int com.myproject.service.Calculator.*(..))")
    public void cutPoint() { }

    
    //前置通知
    @Before("cutPoint()")
    public static void beforeRun(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[Valid]" + "方法" + method + "前置通知" + Arrays.asList(args));
    }

    //返回通知
    //returning可以用来接收返回值
    @AfterReturning(value = "cutPoint()",returning = "result")
    public static void ReturnRun(JoinPoint joinPoint, Object result) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[Valid]" +"方法" + method + "返回通知" + Arrays.asList(args) + "返回值为" + result);
    }

    //异常通知
    //throwing用来指定只有是当前异常类型才会执行异常通知方法,所以一般尽量将异常往大的写
    @AfterThrowing(value = "cutPoint()", throwing = "exception")
    public static void ExceptionRun(JoinPoint joinPoint, NullPointerException exception) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[Valid]" + "方法" + method + "异常日志通知" + Arrays.asList(args));
    }

    //后置通知
    @After("cutPoint()")
    public static void AfterRun(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs(); //获取参数列表
        String method = joinPoint.getSignature().getName(); //获取方法的名称
        System.out.println("[Valid]" + "方法" + method + "后直通知" + Arrays.asList(args));
    }
}
3、测试以及结果
public class AopTest {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

    @Test
    public void test1() {
        //获取切面类
        Calculator calculator = context.getBean(Calculator.class);
        //这里的calculator不是Calculator类型,而是一个代理对象,只有代理对象才能对类的方法进行增强
        System.out.println(calculator.getClass());
        int add = calculator.add(1, 2);
        System.out.println(add);
    }
}

4、xml配置



   
   
   

   
      
      

      

         
         
         
         
         
      
      
         
         
         
         
      
   


三、事务增强 1、配置事务增强applicationcontext.xml




   

   

   
      
      
      
      
   

   
      
   

   
      
   

   

2、service层需要添加事务的方法
  • 注解配置事务只需在方法上标记 @Transactional
@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;

    

    @Transactional(propagation = Propagation.REQUIRED)
    public void balanceSwap(int money) {
        System.out.println("ccc向用户ddd转账" + money);
        accountDao.addBalance(money);
        accountDao.subBalance(money);
    }
}
@Service
public class CouterService {
    @Autowired
    private CouterDao couterDao;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update(int money) {
        couterDao.update(money);
    }
}
@Service
public class MuliService {
    @Autowired
    private AccountService accountService;
    @Autowired
    private CouterService couterService;
    @Transactional
    public void updateAll(int money) {
        accountService.balanceSwap(money);//REQUIRED
        couterService.update(money); //REQUIRES_NEW
        int i = 1/0; //制造异常
    }
}
  • 以上updateAll事务里面有两个方法,balanceSwap、update,两个方法都添加了事务;
  • balanceSwap的事务传播属性是REQUIRED,因此balanceSwap直接加入到updateAll事务中执行;
  • update的事务传播属性是REQUIRES_NEW,update会重新开启另外一个事务执行;
  • 当出现异常时,update事务能够成功提交,而balanceSwap事务回滚;
  • 提示,对方法添加事务时,应该使用其代理对象进行调用,否则事务不会生效,例如以下:
@Service
public class AccountService2 {
    @Autowired
    private AccountDao accountDao;
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update1() {
        accountDao.subBalance(10);
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update2() {
        accountDao.addBalance(10);
    }
    @Transactional
    public void update3() { 
        //如果按照事务的传播行为REQUIRES_NEW,即使遇到异常,这两个事务也是正常提交的,但这里两个事务都回滚
        update1();
        update2();
        int i = 1/0;
    }
}
  • 出现这种情况就是因为update3调用update1和update2是在AccountService2类内部调用的,没用使用AccountService2的代理对象进行调用,因此这里实际上就是单纯的调用了update1和update2两个方法,跟事务没有任何关系,我们前面说了aop对方法进行增强就是采用代理对象对方法进行调用。
3、事务传播行为REQUIRES_NEW和REQUIRED实例
service() { //该方法添加了事务
    //REQUIRED
    a(){
        //REQUIRES_NEW
        b(){}
        //REQUIRED
        c(){}
    }
    //REQUIRES_NEW
    d(){
        do()
        //REQUIRED
        e(){
            //REQUIRES_NEW
            f(){
                Exception2
            }
        }
        //REQUIRES_NEW
        g(){}
    }
    Exception1
}
  • 如果在Exception1发生异常,事务b, d, e, g能够成功提交;a,c事务回滚
  • 如果在Exception2发生异常,事务b能够成功提交;a, c, f, g, e事务回滚,准确来说g不会执行,因为前面已经出现异常了,do事务在REQUIRES_NEW下可以提交,在REQUIRED下回滚
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/287391.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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