设计模式之动态代理模式

一、问题

1、什么是动态代理模式

2、动态代理模式的使用方法

3、动态代理模式的使用演示

4、动态代理模式的原理分析

二、解决问题

1、动态代理模式的概念:

        所谓动态代理类是在运行时生成的class,在生成它时,你必须提供一组interface给它,则动态代理类就宣称它实现了这些interface。当然,动态代理类就充当一个代理,你不要企图它会帮你干实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

2、动态代理的使用方法:

动态代理的使用可分为四部分

  • 接口,定义方法,要被执行的方法
  • 被代理类,实现接口,执行接口的方法
  • 代理类,实现InvocationHandler,帮助被代理类实现方法
  • 客户端(在这里使用测试类)

我们可以来理一下思路:

  1. 首先我们的目的是实现接口中的方法
  2. 方法是使用被代理类,即实现该接口的具体类,但是呢,这个具体实现类只负责代理,而不负责实现,所以就需要一个代理类
  3. 代理类是实现具体的方法实现的,实现了一个InvocationHandler接口。
  4. 现在的重点就在于InvocationHandler是个什么东西,首先明确InvocationHandler的作用就是获得一个handler。
  5. handler的中文翻译是信息处理机,根据字面意思也就是说用力处理信息的机器。
  6. InvocationHandler的含义是代理实例的调用程序处理 实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
  7. 我的理解是代理模式的作用在于拦截对真实对象的访问,代理对象和真实对象(目标对象)具有相同的方法,但是对代理对象的修改不会影响到真实对象,即在代理对象被修改时,真实对象不会被修改。
  8. InvocationHandler提供了一个方法(来源于jdk API 1.6.0)
    invoke(Object proxy, Method method, Object[] args):在代理实例上处理方法调用并返回结果。

    在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。

invoke方法有三个参数:分别说明

Object proxy:在其上调用方法的代理实例

Method method:对应于在代理实例上调用的接口方法的 Method 实例

Object[] args:包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null

3、代码演示

  • 运行环境:windows下的idea
  • 依赖jar包:java.lang     (一般无需在pom.xml中手动配置依赖)
  • 项目类型:maven项目
  • 1、创建一个接口,里面包含若干个方法
package com.xawl.reflect.proxy;

public interface LagentDynamiqueInterface {
    void method1();
}
  • 2、创建具体实现类,即被代理类
package com.xawl.reflect.proxy;

public class LagentDynamiqueImp implements LagentDynamiqueInterface {
    @Override
    public void method1() {
        System.out.println("被代理类的实现方法");
    }
}
  • 3、创建代理类
package com.xawl.reflect.proxy;

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

public class LagebtClass implements InvocationHandler {
    private Object object;

    public LagebtClass(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法执行前");
        Object result = method.invoke(object, args);
        System.out.println("方法执行后");
        return result;
    }
}
  • 4、创建测试类(客户端)
package com.xawl.proxy;

import com.xawl.reflect.proxy.LagebtClass;
import com.xawl.reflect.proxy.LagentDynamiqueImp;
import com.xawl.reflect.proxy.LagentDynamiqueInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;


public class ClientProxy {
    public static void main(String[] args) {
        //获取实现具体方法的被代理类对象
        LagentDynamiqueImp lagentDynamiqueImp = new LagentDynamiqueImp();
        //将目标对象交给代理类
        InvocationHandler handler = new LagebtClass(lagentDynamiqueImp);
        //返回代理对象(要实现方法的接口)
        LagentDynamiqueInterface lagentDynamiqueInterface =  (LagentDynamiqueInterface)
                Proxy.newProxyInstance(handler.getClass().getClassLoader(),
               lagentDynamiqueImp.getClass().getInterfaces(),
                handler);
        lagentDynamiqueInterface.method1();
    }
}

4、原理分析:我们在这里是使用jdk提供好的类和接口来实现的代理模式,接下来我们手动来用代码走一下实现原理

分析:在上面我们直接使用了jdk的类和接口,分别是:Proxy,InvocationHandler,ClassLoader

所以我们现在需要来手动写这三个类

先来看一下他们的源码以及在本例中使用的功能,然后再去动手写:

1、InvocationHandler

package java.lang.reflect;

/**
 * {@code InvocationHandler} is the interface implemented by
 * the <i>invocation handler</i> of a proxy instance.
 *
 * <p>Each proxy instance has an associated invocation handler.
 * When a method is invoked on a proxy instance, the method
 * invocation is encoded and dispatched to the {@code invoke}
 * method of its invocation handler.
 *
 * @author      Peter Jones
 * @see         Proxy
 * @since       1.3
 */
public interface InvocationHandler {

    /**
     * Processes a method invocation on a proxy instance and returns
     * the result.  This method will be invoked on an invocation handler
     * when a method is invoked on a proxy instance that it is
     * associated with.
     *
     * @param   proxy the proxy instance that the method was invoked on
     *
     * @param   method the {@code Method} instance corresponding to
     * the interface method invoked on the proxy instance.  The declaring
     * class of the {@code Method} object will be the interface that
     * the method was declared in, which may be a superinterface of the
     * proxy interface that the proxy class inherits the method through.
     *
     * @param   args an array of objects containing the values of the
     * arguments passed in the method invocation on the proxy instance,
     * or {@code null} if interface method takes no arguments.
     * Arguments of primitive types are wrapped in instances of the
     * appropriate primitive wrapper class, such as
     * {@code java.lang.Integer} or {@code java.lang.Boolean}.
     *
     * @return  the value to return from the method invocation on the
     * proxy instance.  If the declared return type of the interface
     * method is a primitive type, then the value returned by
     * this method must be an instance of the corresponding primitive
     * wrapper class; otherwise, it must be a type assignable to the
     * declared return type.  If the value returned by this method is
     * {@code null} and the interface method's return type is
     * primitive, then a {@code NullPointerException} will be
     * thrown by the method invocation on the proxy instance.  If the
     * value returned by this method is otherwise not compatible with
     * the interface method's declared return type as described above,
     * a {@code ClassCastException} will be thrown by the method
     * invocation on the proxy instance.
     *
     * @throws  Throwable the exception to throw from the method
     * invocation on the proxy instance.  The exception's type must be
     * assignable either to any of the exception types declared in the
     * {@code throws} clause of the interface method or to the
     * unchecked exception types {@code java.lang.RuntimeException}
     * or {@code java.lang.Error}.  If a checked exception is
     * thrown by this method that is not assignable to any of the
     * exception types declared in the {@code throws} clause of
     * the interface method, then an
     * {@link UndeclaredThrowableException} containing the
     * exception that was thrown by this method will be thrown by the
     * method invocation on the proxy instance.
     *
     * @see     UndeclaredThrowableException
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler是一个接口,里面定义了invoke方法,该方法返回3个参数,proxy,method,args。这三个方法在上面已有说明,这里不再赘述。

2、Proxy:newProxyInstance的方法源码分析

​​​​ @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //requireNonNull:静态方法,返回h
        Objects.requireNonNull(h);

        //返回一个和接口具有相同功能的对象,但是改变该对象不会影响到接口
        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 {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //获取构造对象
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

Proxy实现了java.io.Serializable接口,这个接口没有定义方法。Proxy类里面有一个很重要的方法,newProxyInstance(ClassLoader loader,Class<?>[]Interface,InvactaionHandler handler);

3、ClassLoader类:负责加载类的对象

ClassLoader是一个抽象类,里面有一个方法是findClass(String name).该方法的作用是找到

该方法的作用是使用指定的二进制名称查找类

该方法的作用是使用指定的二进制名称查找类

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值