设计模式之动态代理模式

一、问题

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 invocation handler of a proxy instance.
 *
 * 

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() {
                    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).该方法的作用是找到

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

设计模式之动态代理模式_第1张图片

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

 

 

你可能感兴趣的:(设计模式)