跟王老师学反射:动态代理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了跟王老师学反射:动态代理相关的知识,希望对你有一定的参考价值。

跟王老师学反射(十一):动态代理

主讲教师:王少华   QQ群号:483773664

学习内容

学会使用动态代理


一、动态代理

动态代理(Dynamic Proxy):相比上一节所实现的静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现。

动态代理(Dynamic proxies)是 Java 1.3 引入的特性,在Java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和接口可以生成JDK动态代理对象

二、Proxy和InvocationHandler

(一)Proxy

1 简介

Proxy提供用于创建动态代理和代理对象的静态方法,它也是所有动态代理的父类。如果我们在程序中为一个或多个接口动态生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实现,也可以使用Proxy来创建动态代理实现。

2 Proxy提供以下二个方法用于创建动态代理类和动态代理实例

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces):创建一个动态代理类所对应的Class对象,该代理类将实现interfaces所指定的多个接口。第一个ClassLoader指定生成动态代理类的类加载器。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h):直接创建一个动态代理对象,该代理对象的实现类实现了interfaces指定的系列接口,执行代理对象的每个方法时都会被替换执行invocationHandler对象的invoke方法

(二)InvocationHandler

InvocationHandler接口是代理处理程序类的实现接口,该接口作为代理实例的调用处理者的公共父类,每一个代理类的实例都可以提供一个相关的具体调用处理者(InvocationHandler接口的子类)

InvocationHandler接口中有一个方法

1
Object invoke(Object proxy,Method method, Object[] args)

参数:

proxy - 被代理的对象

method - 要调用的方法

args - 方法调用时所需要的参数,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer 或 java.lang.Boolean)的实例中。

返回:

从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException。

三、动态代理的实现

(一)动态代理的实现步骤

1. 实现InvocationHandler接口创建自己的调用处理器 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class DynmicProxy implements InvocationHandler{
    private Object targetObject;
    public DynmicProxy(){}
    public  DynmicProxy(Object targetObject) {
        this.targetObject = targetObject;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        preHandle();
        Object ret = method.invoke(targetObject, args);
        postHandle();
        return ret;
    }
    /**
     * 代理类特有的方法
     */
    private void preHandle(){
        System.out.println("preHandle");
    }
    /**
     * 代理类特有的方法
     */
    private void postHandle(){
        System.out.println("postHandle");
    }
}

2. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 

1
2
3
4
5
6
7
// 目标对象
        UserService userService = new UserServiceImpl();
        // 创建代理类
        UserService proxyUserService = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                new DynmicProxy(userService));

3、通过代理对象调用目标对象方法 

1
2
//通过代理对象调用目标对象的方法
        proxyUserService.save();

(二)代码优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class LogHandler implements InvocationHandler{
    //目标对象
    private Object targetObject;
    /**
     * 生成代理对象:就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
     * @param targetObject
     * @return 返回代理对象
     */
    public Object newProxyInstance(Object targetObject) {
        this.targetObject = targetObject;
        /**
         * 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例   
            第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 
            第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口 
            第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
         */
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                    targetObject.getClass().getInterfaces(), this);
    }
    /**
     * proxy:要代理的对象
     * method:要执行的方法
     * args:方法参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //原对象方法调用前调用日志处理
        preHandle();
        //调用目标方法:ret为目标方法返回值
        Object ret = method.invoke(targetObject, args);
        //原对象方法调用 后调用 日志处理方法
        postHandle();
        return ret;
    }
     
    /**
     * 代理类特有的方法
     */
    private void preHandle(){
        System.out.println("preHandle");
    }
    /**
     * 代理类特有的方法
     */
    private void postHandle(){
        System.out.println("postHandle");
    }
}
1
2
3
4
5
6
7
public class DynmicTest {
    public static void main(String[] args) {
        LogHandler logHandler = new LogHandler();
        UserService userService = (UserService) logHandler.newProxyInstance(new UserServiceImpl());
        userService.save();
    }
}














以上是关于跟王老师学反射:动态代理的主要内容,如果未能解决你的问题,请参考以下文章

跟王老师学反射使用反射调用属性

跟王老师学反射:使用反射调用方法

跟王老师学反射:使用反射生成并操作对象

跟王老师学反射:反射应用小案例--进行对象拷贝

跟王老师学反射:Class类:获得Class对象

跟王老师学反射:Class类:从Class类中获取信息