b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

Spring AOP无法拦截内部方法调用

电脑杂谈  发布时间:2020-07-08 18:21:10  来源:网络整理

动态方法调用 为什么不安全_动态调用 webservice_js 动态调用成员方法

spring aop对象的内部方法嵌套无效

看看内部通话失败的经典示例

//如果存在事务,则抛出异常。
public class TxDemo{
   @Transactional(propagation = Propagation.NEVER)
    public void serviceH() {
        UserInfoVo infoVo = new UserInfoVo();
        infoVo.setAge(1000);
        infoVo.setUserName("H");
        userInfoDao.save2(infoVo);
    }
    public void serviceI() {
        UserInfoVo infoVo = new UserInfoVo();
        infoVo.setAge(1000);
        infoVo.setUserName("I");
        userInfoDao.save2(infoVo);
    }
    @Transactional
    public void serviceHI() {
        serviceI();
        serviceH(); 
    }
}

在上面的示例中,在serviceH标头上带注释的事务表明,如果当前存在事务,则会引发异常. 在我的测试示例中,serviceHI中有一个事务,然后再次调用serviceH. 通常情况下,应该引发异常,但是结果也不是异常,并且执行是平稳的. 为什么?

让我们从实际战斗中了解AOP的基本原理.

Spring AOP的底层不过是动态代理动态方法调用 为什么不安全,而事务也基于动态代理,那么为什么不编写动态代理呢?

代码: 测试快球

第一步是定义一个接口. 让我们基于JDK接口创建一个动态代理模型.

public interface ReadableInterface {
    void read();
    void write();
}

第二步: 实现接口类

public class StudentImpl implements ReadableInterface {
    @Override
    public void read() {
        System.out.println("read........");
        write();
    }
    @Override
    public void write() {
        System.out.println("write ..........");
    }
}

创建代理:

第3步: 创建jdk动态代理

public class ProxyObjectImpl implements InvocationHandler {
    private Object proxied = null;
    public ProxyObjectImpl() {
    }
    public ProxyObjectImpl(Object proxied) {
        this.proxied = proxied;
    }
    /**
     * // 该方法负责集中处理动态代理类上的所有方法调用
     //
     * @param proxy:第一个参数既是代理类实例
     * @param method:第二个参数是被调用的方法对象
     * @param args:第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
     * 注意:千万别把第一个参数放入method.invoke里面哦,不然就会死循环了,代理类执行代理类,变成自己代理自己了
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理做事前。。。。。。。");
        Object o=method.invoke(proxied, args);
        System.out.println("代理做事后。。。。。。");
        return o;
    }
}

动态调用 webservice_动态方法调用 为什么不安全_js 动态调用成员方法

第4步: 模拟客户通话


public class DynamicProxyDemo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        proxy1(); 
    }
    private static void proxy2() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        StudentImpl student = new StudentImpl();
        //获取委托方
        ReadableInterface readableInterface = (ReadableInterface) Proxy.newProxyInstance(StudentImpl.class.getClassLoader(),
                StudentImpl.class.getInterfaces(),
                new ProxyObjectImpl(student));
        readableInterface.read(); 
    }
}

输出为:

代理做事前。。。。。。。
read.........
write ..........
代理做事后。。。。。。

当客户调用read时,将使用代理,因此将打印“代理之前,代理之后”的字样;但是write是直接输出的,因为write()在read()内部被调用.

整个过程等同于

//客户端如下:
main(){
      proxy.read();
}
//被代理的接口类最终的样子如下:proxy.class
proxy.read(){
      print("调用前...");
      read();
      print("调用后...");
}
proxy.write(){
      print("调用前...");
      write();
      print("调用后...");
}
read(){
   this.write();
}
write(){
}

您看到了吗?在Clint中,proxy.read()被调用;剩下的代理,在read()中,直接调用write,而不是proxy.write();

这不是Spring AOP的问题,而是我们使用代理的问题.

以上分析仅是基于接口代理的jdk引起的问题.

CGLib是基于类的代理,而不是接口代理.

第一步: 基础课

public class Person {
    public void play(){
        System.out.println("person is play ....");
    }
    public void getMoney(){
        play();
        System.out.println("give me 100$");
    }
}

第2步: 编写代理

动态方法调用 为什么不安全_js 动态调用成员方法_动态调用 webservice


public class CglibProxyHandler implements MethodInterceptor {
    public CglibProxyHandler() {
    }
    /**
     * 1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("proxy before......");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("proxy after .......");
        return o1;
    }
}

第三步: 客户编写

public class CglibProxyDome {
    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "target/cglib");
        CglibProxyHandler cglibProxy = new CglibProxyHandler();
        //jdk需要提供接口,cglib需要是非私有类,且不能处理final关键字修饰的方法
        Enhancer enhancer = new Enhancer();
        //设置父类
        enhancer.setSuperclass(Person.class);
        //设置回调对象
        enhancer.setCallback(cglibProxy); 
        Person person = (Person) enhancer.create(); 
        person.getMoney();
    }
}

打印结果:

proxy before......
proxy before......
person is play ....
proxy after .......
give me 100$
proxy after .......

您会发现内部方法调用也离开了代理!

为什么?

基于类的代理模式是生成子类以继承父类

CGLib生成的子类的最终形式如下:


public class Person$$EnhasncerBy....... extends Person{
    public final void play(){
       print("代理前");
        super.play();
        print("代理后");
    }
    public final void getMoney(){
       print("代理前");
        super.getMoney();
        print("代理后");
    }
}
public class Demo(){
        Person p=new Person$$Enhasncer.....()
        p.getMoney();
}


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-273206-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...