继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Java动态代理之通俗理解

慕粉0415482673
关注TA
已关注
手记 297
粉丝 64
获赞 367

代理模式介绍

代理模式是一种常用的设计模式,其作用就是为目标对象提供额外的访问方式,在不修改目标对象的前提下,扩展目标对象的额外功能,比如统计执行时间,打印日志等。

代理模式分为两种:静态代理和动态代理。

需求:假如不想改动原有代码情况下,并记录用户保存方法的执行时间。示例代码如下:

接口

public interface UserService {    public void saveUser(User user);}

实现类

public class UserServiceImpl implements UserService {    private UserDao userDaoImpl;    @Override    public void saveUser(User user) {        userDaoImpl.save(user);     }    ...}

静态代理实现

静态代理是在程序运行前产生的,一般来说,代理类和目标类实现同一接口或者有相同的父类

代理类

public class UserServiceProxyImpl implements UserService {    private UserService UserServiceImpl;//代理类持有一个委托类的对象引用    public UserServiceProxyImpl(UserService UserServiceImpl) {        this.UserServiceImpl = UserServiceImpl;    }    @Override    public void saveUser(User user) {        long startTime = System.currentTimeMillis();        System.out.println("开始记录时间");        delegate.dealTask(taskName);    // 将请求分派给委托类处理        long endTime = System.currentTimeMillis();        System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");    }}

产生代理对象的静态工厂类

public class UserServiceProxyFactory {    public static UserService getInstance() {        return new UserServiceProxyImpl(new UserServiceImpl());    }}

客户端

public class ClientTest {    public static void main(String[] args) {        UserService UserServiceProxy = UserServiceProxyFactory.getInstance();        UserServiceProxy.saveUser(new User("1","张三"));    }}

运行结果

开始记录时间保存用户信息方法耗时0.01毫秒

静态代理优缺点:

优点:

1、业务类UserServiceImpl只需要关注业务逻辑本身,保证了业务类的重用性。

2、客户端Client和业务类UserServiceImpl之间没有直接依赖关系,对客户的而言屏蔽了具体实现。

缺点:

1、代理对象的一个接口只服务于一种接口类型的对象,静态代理在程序规模稍大时就无法使用。

2、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度

动态代理实现

动态代理是程序在运行过程中在JVM内部动态产生的代理对象,代理类可以实现对多种类的代理。

动态代理又分为两种:JDK动态代理和CGLIB动态代理。

JDK动态代理

JDK动态代理需先声明一个代理类和目标类之间的中间类,此中间类需要实现jdk中的一个接口InvocationHandler。源码如下:

package java.lang.reflect;public interface InvocationHandler {    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}

产生代理对象的中间类

import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class JDKProxy implements InvocationHandler {    private Object target; //持有目标对象的引用    public JDKProxy(Object target){        this.target = target;    }    //创建代理对象    public Object createProxy(){         //1.得到目标对象的classloader        ClassLoader classLoader = target.getClass().getClassLoader();         //2.得到目标对象的实现接口的class[]        Class<?>[] interfaces = target.getClass().getInterfaces();        //3.第三个参数需要一个实现InvocationHandler接口的对象        //3-1.第一种写法,让当前类实现InvocationHandler,第三个参数写this        return Proxy.newProxyInstance(classLoader, interfaces, this);        //3-2.第二种写法,第三个参数用匿名内部类的形式,先注释掉        /*return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                long startTime = System.currentTimeMillis();                System.out.println("开始记录时间");                Object ret = method.invoke(target, args);   //执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出                long endTime = System.currentTimeMillis();                System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");                return ret;            }        });*/    }    /*     在代理实例上执行目标对象的方法     参数1 就是代理对象,一般不使用    参数2 它调用的方法的Method对象    参数3 调用的方法的参数    */    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        long startTime = System.currentTimeMillis();        System.out.println("开始记录时间");        Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出        long endTime = System.currentTimeMillis();        System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");        return ret;    }}

客户端

public class ProxyTest {    @Test    public void test1() {        UserService userService = new UserServiceImpl();    //1.创建目标对象        JDKProxy factory = new JDKProxy(userService);   // 2.通过JKDProxy完成代理对象创建        UserService userServiceProxy = (UserService)factory.createProxy();        userServiceProxy.saveUser(new User("1","张三"));    }}

JDK动态代理中,需要关注的两点:

1、Proxy.newProxyInstance(classLoader, interfaces, this); 底层是怎么创建的代理对象

2、invoke方法是什么时候执行的,谁来调用的此方法

<font color=red>解析1>></font>怎么产生代理对象:

@CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)    throws IllegalArgumentException    {    if (h == null) {        throw new NullPointerException();    }    final Class<?>[] intfs = interfaces.clone();    final SecurityManager sm = System.getSecurityManager();    if (sm != null) {        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);    }    /*    * Look up or generate the designated proxy class.    */    Class<?> cl = getProxyClass0(loader, intfs);    /*    * Invoke its constructor with the designated invocation handler.    */    try {        final Constructor<?> cons = cl.getConstructor(constructorParams);        final InvocationHandler ih = h;        if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {            // create proxy instance with doPrivilege as the proxy class may            // implement non-public interfaces that requires a special permission            return AccessController.doPrivileged(new PrivilegedAction<Object>() {                public Object run() {                    return newInstance(cons, ih);                }            });        } else {            return newInstance(cons, ih);        }    } catch (NoSuchMethodException e) {        throw new InternalError(e.toString());    }}//继续看newInstance方法private static Object newInstance(Constructor<?> cons, InvocationHandler h) {    try {        return cons.newInstance(new Object[] {h} );    } catch (IllegalAccessException | InstantiationException e) {        throw new InternalError(e.toString());    } catch (InvocationTargetException e) {        Throwable t = e.getCause();        if (t instanceof RuntimeException) {            throw (RuntimeException) t;        } else {            throw new InternalError(t.toString());        }    }}

由此可以看出创建代理对象,是利用反射,先获取目标对象的构造器,然后在通过构造反射生成代理对象

<font color=red>解析2>></font>invoke方法什么时候被调用:

我们通过一个工具类把生成的代理对象的字节码输出到磁盘,然后通过反编译来查看代理对象有哪些内容

工具类如下:

public class ProxyGeneratorUtils {    /**        * 把代理类的字节码写到硬盘上        * @param fileName 文件名        * @param path 路径信息        * @param clazz 目标类的接口数组        */    public static void writeProxyClassToHardDisk(String fileName, String path, Class<?>[] clazz) {        byte[] classFile = ProxyGenerator.generateProxyClass(fileName, clazz);        FileOutputStream out = null;        try {            out = new FileOutputStream(path);            out.write(classFile);            out.flush();        } catch (Exception e) {            e.printStackTrace();        } finally {            try {                out.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    //主方法    public static void main(String[] args) {        ProxyGeneratorUtils.writeProxyClassToHardDisk("$JDKProxy1","F:/$JDKProxy1.class",UserServiceImpl.class.getInterfaces());    }}

运行main方法生成代理对象字节码文件,用JD.exe反编译打开如下

public final class $JDKProxy1 extends Proxy implements UserService {    private static Method m1;    private static Method m3;    private static Method m0;    private static Method m2;    public $JDKProxy1(InvocationHandler arg0) throws  {        super(arg0);    }    public final void saveUser(User arg0) throws  {            try {                super.h.invoke(this, m3, new Object[]{arg0});            } catch (RuntimeException | Error arg2) {                throw arg2;            } catch (Throwable arg3) {                throw new UndeclaredThrowableException(arg3);            }        }    ...

用一张图来说明调用userServiceProxy代理对象的saveUser()方法内部发生了什么


CGLIB动态代理

cglib动态代理也需要一个产生代理对象的中间类,此类需实现MethodInterceptor接口,此接口在cglib包中,目前已经被spring整合,在spring-core核心包中<br>

产生代理对象的中间类

import java.lang.reflect.Method;import org.springframework.cglib.proxy.Enhancer;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;public class CglibProxy implements MethodInterceptor {    private Object target; //持有目标对象的引用    public CglibProxy(Object target){        this.target = target;    }    //创建代理对象    public Object createProxy(){        Enhancer enhancer = new Enhancer(); //1.创建Enhancer        enhancer.setSuperclass(target.getClass());  //2.传递目标对象的class        enhancer.setCallback(this); //3.设置回调操作(相当于InvocationHanlder)        return enhancer.create();    }    //相当于InvocationHanlder中的invoke    @Override    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {        long startTime = System.currentTimeMillis();        System.out.println("开始记录时间");        Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出        long endTime = System.currentTimeMillis();        System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");        return ret;    }}

客户端

public class ProxyTest {    @Test    public void test1() {        UserService userService = new UserServiceImpl();    //1.创建目标对象        CglibProxy factory = new CglibProxy(customerService);   // 2.通过CglibProxy完成代理对象创建        UserService userServiceProxy = (UserService)factory.createProxy();        userServiceProxy.saveUser(new User("1","张三"));    }}

产生代理对象字节码,用JD.exe反编译如下

import java.lang.reflect.Method;import org.springframework.cglib.core.ReflectUtils;import org.springframework.cglib.core.Signature;import org.springframework.cglib.proxy.Callback;import org.springframework.cglib.proxy.Factory;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;public class UserServiceImpl$$EnhancerByCGLIB$$1772a9ea extends UserServiceImpl implements Factory {    ...    public final void saveUser(User paramUser)    {        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;        if (tmp4_1 == null){            tmp4_1;            CGLIB$BIND_CALLBACKS(this);        }        if (this.CGLIB$CALLBACK_0 != null) return;        super.saveUser(paramUser);    }    ...}

JDK动态代理和CGLIB动态代理区别:

1、JDK动态代理是针对于接口代理,目标类必须实现了接口,产生的代理对象也会实现该接口。

2、CGLIB代理是采用继承,产生的代理对象继承于目标类,所以目标类和目标方法不能用final修饰。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP