spring 注解生效和java代理

spring使用时,在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解是不会生效的。

一般方法

  • 把这两个方法分开到不同的类中;
  • 把注解加到类名上面;
  • 使用TransactionTemplate
    @Autowired
     private TransactionTemplate transactionTemplate;
    
      transactionTemplate.execute(transactionStatus -> {
             return null;
         });
    

特殊方法

修改配置,修改调用:较复杂,不建议使用,特殊情况例外

  1. 方法调用处改为
    ((AbstractTemplete) AopContext.currentProxy()).book(paramHeader);
  2. 方法调用者的方法不能为final,不然报:
    Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
  3. 被调用者的调用的方法必须为public

spring aop 通过获取代理对象实现事务切换 - CSDN博客

原因分析

https://blog.csdn.net/clementad/article/details/47339519

JDK代理

同一个类中的方法调用,调用时,调用的是原对象的方法。

import java.lang.reflect.*;

public class MyProxy {
    public interface IHello {
        void sayHello1();
        void sayHello2();
    }
    static class Hello implements IHello {
        public void sayHello1() {
            System.out.println("Hello world 1!!");
            sayHello2();
        }
        public void sayHello2() {
            System.out.println("Hello world 2!!");
        }
    }

    //自定义InvocationHandler
    static class HWInvocationHandler implements InvocationHandler {
        //目标对象å
        private Object target;

        public HWInvocationHandler(Object target) {
            this.target = target;
        }

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------插入前置通知代码-------------");
            //执行相应的目标方法
            Object rs = method.invoke(target, args);
            System.out.println("------插入后置处理代码-------------");
            return rs;
        }
    }
    public static void main(String[] args) throws Exception {
        //生成$Proxy0的class文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //获取动态代理类
        Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(), IHello.class);
        //获得代理类的构造函数,并传入参数类型InvocationHandler.class
        Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
        //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
        IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));
        //通过代理对象调用目标方法
        iHello.sayHello1();
    }
}

执行结果:

------插入前置通知代码-------------
Hello world 1!!
Hello world 2!!
------插入后置处理代码-------------

为何sayHello2前后没有通知代码?

以下设置生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

我们看下:$Proxy0类

package com.sun.proxy;
import blog.aop.proxy.cglib.MyProxy.IHello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public final class $Proxy0 extends Proxy implements IHello {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    private static Method m4;
  
    static {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("blog.aop.proxy.cglib.MyProxy$IHello").getMethod("sayHello1");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("blog.aop.proxy.cglib.MyProxy$IHello").getMethod("sayHello2");
    }
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final void sayHello1() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final void sayHello2() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

  }

主要看下sayHello1()方法的实现
super.h.invoke(this, m3, (Object[])null);

其中:

1. h为之前在` IHello iHello = (IHello) constructor.newInstance(new HWInvocationHandler(new Hello()));` 中设置的new HWInvocationHandler(new Hello())
2. this为$Proxy0类
3. m3为:blog.aop.proxy.cglib.MyProxy$IHello.sayHello1

执行时会执行HWInvocationHandler 类的invoke方法

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("------插入前置通知代码-------------");
            //执行相应的目标方法
            Object rs = method.invoke(target, args);
            System.out.println("------插入后置处理代码-------------");
            return rs;
        }

其中Object rs = method.invoke(target, args);会执行new Hello()类的sayHello1方法

可以看出,
最终执行的是Hello 类的sayHello1,对象也是Hello对角,代理只一次,不会对sayHello1中的方法再次代理。

引用

深度剖析JDK动态代理机制 - MOBIN - 博客园
JAVA设计模式-动态代理(Proxy)源码分析 - 张橙子 - 博客园

你可能感兴趣的:(spring 注解生效和java代理)