java--动态代理技术

动态代理技术

在学习动态代理之前,需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。

代理模式

代理模式是常用的java设计模式,他的特点是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等,它们之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

静态代理

静态代理是由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成,缺点是接口与代理类是1v1,有多个接口需要代理,就需要新建多个代理类,比较繁琐。

静态代理的列子

显示方法运行的时间

UserServiceTarget 目标类 实现了UserService
TimeHandler 包含了重复代码,间接调用目标
UserServiceProxy 代理类 实现了UserService

接口

public interface UserService {
    public void b();
    public void c();
}

委托类实现接口

// 目标类
public class UserServiceTarget implements UserService {

    public void b() {
        System.out.println("b");
    }

    public void c() {
        System.out.println("c");
    }
}

代理类实现接口

// 静态代理 - 自己编码实现的代理类
// 动态代理 - jdk可以在运行期间,动态地生成这个代理类
// proxy 代理 (封装复杂逻辑,它去间接调用 TimeHandler 和 业务代码), 让使用者觉得简单
public class UserServiceProxy implements UserService {

    public void b() {
        try {
            TimeHandler timeHandler = new TimeHandler();
            //得到方法 类名.class.getMethod(方法名)
            Method b = UserServiceTarget.class.getMethod("b");
            //反射调用,传递方法,下面的计算类里就可以调用此方法
            timeHandler.invoke(b);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public void c() {
        try {
            TimeHandler timeHandler = new TimeHandler();
            Method c = UserServiceTarget.class.getMethod("c");
            timeHandler.invoke(c);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

计算时间的代码

public class TimeHandler {

    private UserServiceTarget userService = new UserServiceTarget();

    //  b() c()
    public void invoke(Method method) {
        System.out.println("==================================");
        long start = System.nanoTime();

        // 调用这个方法 method 
        // 正常调用: 对象.方法名(参数)
        // 反射调用: 方法.invoke(对象, 参数); 
        try {
            //调用 UserServiceTarget 中的方法
            method.invoke(userService);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("花费了: " + (end - start));
    }
}

测试类

public class TestTimeHandler {
    public static void main(String[] args) {
        UserServiceProxy proxy = new UserServiceProxy();
        proxy.b();
        proxy.c();
    }
}

结果

==================================
b
花费了: 201800
==================================
c
花费了: 65691

动态代理(Dynamic Proxy )

原始编写java 代码的方式是从java文件编译为class文件,然后class文件经过翻译后变为机器语言,而动态代理就是体现了class文件到机器语言这一过程。

  • 原始编写java 代码的方式

.java  ->  javac  ->  *.class -> 类加载 

  • 动态代理

.class -> 类加载

动态代理例子

显示方法运行的时间

 UserServiceTarget 目标类 实现了UserService
 TestDynamicProxy 包含了计算时间代码,间接调用目标
 UserServiceProxy 代理类 实现了UserService

接口

public interface UserService {

    public void a(int x, int y);
    public void b();
    public void c();
}

目标类(委托类)

// 目标类
public class UserServiceTarget implements UserService {


    public void a(int x, int y) {
        System.out.println("a");
    }

    public void b() {
        System.out.println("b");
    }

    public void c() {
        System.out.println("c");
    }
}

测试类

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

// dynamic 动态的
// proxy 代理
public class TestDynamicProxy {

    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        // 1. 生成了类的字节码
        // 参数1 string 表示代理类的名字,随意起
        // 参数2 class[] 表示代理类要实现的接口
        // 返回的结果就是 代理类 的字节码
        byte[] bytes = ProxyGenerator.generateProxyClass("UserServiceProxy2", new Class[]{UserService.class});

        // 2. 执行类加载
        // 类加载的最终结果体现为 Class 类对象, 要借助类加载器
        ClassLoader cl = new ClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                return defineClass(name, bytes, 0, bytes.length);
            }
        };
        Class c = cl.loadClass("UserServiceProxy2"); // 进行类加载, 获得了 UserServiceProxy 类对象

        // 3. 创建代理类实例对象
        // 不能正常创建它的实例对象, new UserServiceProxy2();

        // 获取代理类的构造方法
        Constructor constructor = c.getConstructor(InvocationHandler.class);

        UserServiceTarget target = new UserServiceTarget();
        // 创建实例对象, 强制转换为它的接口类型
        UserService proxy = (UserService)constructor.newInstance(new InvocationHandler() {
            @Override
            // method 就是 a , b, c 等方法对象
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("进入了 invoke");
                long start = System.nanoTime();

                // 方法.invoke(目标, 参数); 通过这行代码调用目标类的方法
                method.invoke(target, args);

                long end = System.nanoTime();
                System.out.println("花费了:" + (end - start));
                return null;
            }
        });

        // 4. 使用代理对象
        proxy.b();
        proxy.c();
        proxy.a(10,20);
    }
}

结果

进入了 invoke
b
花费了:682903
进入了 invoke
c
花费了:233935
进入了 invoke
a
花费了:267017

以上两种代理方法可以看出,静态代理中的方法实现太过繁琐(业务逻辑多),相比之下,动态代理的业务逻辑就简洁多了。

总结

简而言之,动态代理也就是间接的调用委托的方法,并完善方法的过程,这样使代码简洁,并且在编码时,代理逻辑与业务逻辑相互独立,个不影响,没有侵入,没有耦合(耦合也就是一处代码修改,另一处代码也要修改,如果偶合性强,那么修改一处代码时,需要改多处代码,才可以把程序修改完毕,反之则修改处少)。

猜你喜欢

转载自blog.csdn.net/grey_mouse/article/details/87740562