JAVASE-15-动态代理

Table of Contents

1:基于JDK的动态代理

1.1:创建接口

1.2:创建实现类

1.3:创建InvocationHandler方法执行器 和生成代理类

1.4:编写代理类实际的调用。

1.5:或者使用内部类来标识InvocationHandler处理器;

2:基于CGLIB的动态代理

2.1:引入jar包

2.2:被代理类

2.3:代理了实现MethodInterceptor接口

2.4:测试:

2.5:CGLIB原理


目前java动态代理的实现分为两种

1.基于JDK的动态代理

2.基于CGILB的动态代理

在业务中使用动态代理,一般是为了给需要实现的方法添加预处理或者添加后续操作,但是不干预实现类的正常业务,把一些基本业务和主要的业务逻辑分离。我们一般所熟知的Spring的AOP原理就是基于动态代理实现的。
 

1:基于JDK的动态代理

基于JDK的动态代理就需要知道两个类:1.InvocationHandler(接口)、2.Proxy(类)

还要知道JDK是基于接口的动态代理,也就是说我们动态代理的被代理类,必须实现接口

1.1:创建接口

public interface UserInter {
    void add();
}

1.2:创建实现类

public class UserImpl implements UserInter {
    @Override
    public void add() {
        System.out.println("add.....");
    }
}

1.3:创建InvocationHandler方法执行器 和生成代理类

package com.wkl.impl.Proxy;

import com.wkl.impl.UserImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Description:方法执行器,帮我们执行方法
 * Date:       2020/6/26 - 下午 10:16
 * author:     wangkanglu
 * version:    V1.0
 */
public class MyInvocationHandler implements InvocationHandler {
    private UserImpl userImpl;

    public void setUserImpl(UserImpl userImpl) {
        this.userImpl = userImpl;
    }

    /**
     * Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
     * Method method:当前将要执行的目标对象的方法
     * Object[] args:这个方法调用时外界传入的参数值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //参数为被代理的类的实例,和传入的参数
        System.out.println("准备执行方法:"+method.getName());
        Object invoke = method.invoke(userImpl, args);
        System.out.println("执行方法:"+method.getName()+"完毕!");
        return invoke;
    }

    
}

1.4:编写代理类实际的调用。

 UserInter userInter = new UserImpl();
        //获取处理器实例
        MyInvocationHandler userProxy = new MyInvocationHandler();
        userProxy.setUserInter(userInter);

        UserInter o = (UserInter) Proxy.newProxyInstance(userInter.getClass().getClassLoader(), userInter.getClass().getInterfaces(), userProxy);
        o.add();
准备执行方法:add
add.....
执行方法:add完毕!

1.5:或者使用内部类来标识InvocationHandler处理器;

package com.wkl.impl.Proxy;

import com.wkl.impl.UserImpl;
import com.wkl.inter.UserInter;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Description:
 * Date:       2020/6/26 - 下午 10:15
 * author:     wangkanglu
 * version:    V1.0
 */
public class UserProxy {

    public UserInter getProxy(UserInter userInter){

        InvocationHandler i = new InvocationHandler() {
            /**
             * Object proxy:代理对象;给jdk使用,任何时候都不要动这个对象
             * Method method:当前将要执行的目标对象的方法
             * Object[] args:这个方法调用时外界传入的参数值
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("准备执行方法:"+method.getName());
                Object invoke = method.invoke(userInter, args);
                System.out.println("执行方法:"+method.getName()+"完毕!");
                return invoke;
            }
        };

        UserInter o = (UserInter) Proxy.newProxyInstance(userInter.getClass().getClassLoader(), userInter.getClass().getInterfaces(), i);
        return o;
    }

    public static void main(String[] args) {
        UserInter userInter = new UserImpl();
        //获取处理器实例
        UserProxy userProxy = new UserProxy();
        UserInter proxy = userProxy.getProxy(userInter);
        proxy.add();
    }

}

注:处理器接口传入的是接口的实现;

 

2:基于CGLIB的动态代理

本文主要讲的是CGLIB的动态代理,因为基于JDK的动态代理一定要继承一个接口,而绝大部分情况是基于POJO类的动态代理,那么CGLIB就是一个很好的选择,在Hibernate框架中PO的字节码生产工作就是靠CGLIB来完成的。还是先看代码。

2.1:引入jar包

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

2.2:被代理类

public class UserImpl {
    public void add() {
        System.out.println("add.....");
    }
}

 

2.3:代理了实现MethodInterceptor接口

package com.wkl.impl.Proxy;

import com.wkl.impl.UserImpl;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * Description:
 * Date:       2020/6/26 - 下午 10:55
 * author:     wangkanglu
 * version:    V1.0
 */
public class UserCGLIB implements MethodInterceptor {
    private UserImpl userimpl;

    public void setUserimpl(UserImpl userimpl) {
        this.userimpl = userimpl;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//参数为被代理的类的实例,和传入的参数
        System.out.println("准备执行方法:"+method.getName());
 用。
        method无法得到父类的方法,所以使用methoproxy
        Object invoke = methodProxy.invoke(userimpl, objects);
        System.out.println("执行方法:"+method.getName()+"完毕!");
        return invoke;
    }
}

2.4:测试:

 public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserImpl.class);
        UserCGLIB userCGLIB = new UserCGLIB();
        UserImpl user = new UserImpl();
        userCGLIB.setUserimpl(user);
        enhancer.setCallback(userCGLIB);
        UserImpl o = (UserImpl) enhancer.create();
        o.add();
    }
准备执行方法:add
add.....
执行方法:add完毕!

 

2.5:CGLIB原理

从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。
 

这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。
 

为什么我们使用methodproxy,不适用method

因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。

所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苍煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值