代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

相关类及接口

java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces,
    InvocationHandler h)

java.lang.reflect.InvocationHandler:这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象。
// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上发射执行
Object invoke(Object proxy, Method method, Object[] args)java.lang.ClassLoader:这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,java开发培训它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。
步骤
动态代理具体有如下四步骤:
1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

示例

package twenz.test;

public interface UserDao {
    public void save(String name);
}

package twenz.test;

public class UserDaoImp implements UserDao {

    @Override
    public void save(String name) {
        // TODO Auto-generated method stub
        System.out.println(this.getClass().getName());
        System.out.println(name);
    }
}

package twenz.test;

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

public class ProxyTest2 implements InvocationHandler {
    private UserDao target;

    public ProxyTest2(UserDao target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        //System.out.println("The object is:"+proxy.getClass().getName());
        System.out.println("The method is:"+method.getName());
        method.invoke(target, args);
        return null;
    }

    public static void  test(UserDao target) {
        ProxyTest2 proxy = new ProxyTest2(target);
        UserDao user = (UserDao)Proxy.newProxyInstance(ProxyTest2.class.getClassLoader(),
                target.getClass().getInterfaces(), proxy);
        user.save("=");
    }
}

通过调用”ProxyTest2.test(new UserDaoImp());“则可以触发代理,会执行ProxyTest2的invoke函数并进而触发UserDaoImp的save函数

只对接口的代理
这个才是本文关注的重点,那就是如果我没有实现接口,只想调用UserDao的save函数该如何?下面这个代码是一个示例:

package twenz.test;

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

public class ProxyTest implements InvocationHandler {
    private Class<UserDao> target;

    public ProxyTest(Class<UserDao> target)
    {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        //System.out.println("The object is:"+proxy.getClass().getName());
        System.out.println("The method is:"+method.getName());
        return null;
    }

    public static void  test(Class<UserDao> target) {
        ProxyTest proxy = new ProxyTest(target);
        UserDao user = (UserDao)Proxy.newProxyInstance(ProxyTest.class.getClassLoader(),
                new Class<?>[]{target}, proxy);
        user.save("");
    }

    public static void main(String args[])
    {
        ProxyTest.test(UserDao.class);
    }
}

这里我们没有实现接口UserDao,但是我们却调用了接口的函数save。不过这里invoke里面其实并没有invoke代理的函数save,只是获取了save的方法名。