玩转JDK动态代理

概述

JDK动态代理的两个主要类:
java.lang.reflect.InvocationHandler:执行代理对象的方法时的回调接口。在这个接口中编写与代理业务相关的代码。它在JDK中的定义如下。

public interface InvocationHandler {
    /**
     * @param proxy JDK生成的代理对象
     * @param method 被代理接口的方法对象
     * @param args 方法执行的参数
     * @return  
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

java.lang.reflect.Proxy:调用它的静态方法newProxyInstance创建代理对象。它为代理的接口创建实现类,并为其创建一个以InvocationHandler对象为参数的构造方法,每个代理方法都会执行InvocationHandler对象的invoke方法。

运用

A. 鱼儿开始和停止游泳时告诉大家

通过jdk动态代理,可以在一个行为前后添加性能监控、日志等操作。

下面我们来记录鱼儿开始和停止游泳的日志。

(1)定义一个FishService接口和实现

public interface FishService{
    /**
    * 游泳
    **/
    void swimming();
}

实现如下

public class FishServiceImpl implements FishService {
    public void swimming() {
        System.out.println("游泳中....");
    }
}

(2)定义SimpleInvocationHandler 用于获得代理对象和和方法回调。
在这里该类的定义稍微复杂了点,直接看我的注释就是。


public class SimpleInvocationHandler implements InvocationHandler {

    private Set<Class> interfaces = new HashSet<>();
    // 接口和默认实现
    private Map<Class, Object> additionalItf = new HashMap<>();

    /**
     * 添加被代理接口和其实现
     * @param itf 被代理的接口
     * @param defaultImpl 接口的默认实现
     * @param <T>
     */
    public <T> void additionalItf(Class<T> itf, T defaultImpl) {
        additionalItf.put(itf, defaultImpl);
        interfaces.add(itf);
    }

    /**
     * 获得代理对象
     * 通过Proxy的静态方法newProxyInstance创建代理
     * @return 返回代理对象
     */
    public Object getProxy() {
        ClassLoader loader = SimpleInvocationHandler.class.getClassLoader();
        Class[] interfaces = this.interfaces.toArray(new Class[0]);
        return Proxy.newProxyInstance(loader, interfaces, this);
    }

    /**
     * 实现InvocationHandler接口的invoke方法。
     * 该方法在执行代理对象的方法时,会回调执行。
     * @return 返回被代理的方法的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        String name = method.getName();
        try {
            System.out.println(name + "方法执行前");
            // 根据方法的声明类对象,获取方法的调用者
            Object defaultImpl = additionalItf.get(method.getDeclaringClass());
            if (defaultImpl == null) {
                System.out.println(name + "方法无默认实现");
                return null;
            }
            return method.invoke(defaultImpl, args);
        } finally {
            System.out.println(name + "方法执行后");
        }

    }
}

(3)创建代理,并执行接口方法。

public class MainObject {
    public static void main(String[] args) throws Exception {

        SimpleInvocationHandler handler = new SimpleInvocationHandler();
        // 添加被代理接口和实现类
        handler.additionalItf(FishService.class, new FishServiceImpl());

        // 获得代理对象
        FishService fishService = (FishService) handler.getProxy();
        // 通过代理对象执行代理方法
        fishService.swimming();
    }

}

输入如下。

swimming方法执行前
游泳中....
swimming方法执行后
B. 让鱼儿飞起来

可以通过JDK动态代理多个接口,使对象提供更多的服务。操作如下。

(1)定义一个飞行的接口和一个默认实现。

interface FlyService {
    /**
     * 飞行
     */
    void fly();
}

实现飞行接口,如下。

public class FlyServiceImpl implements FlyService {
    @Override
    public void fly() {
        System.out.println("飞...");
    }
}

(2)通过SimpleInvocationHandler 创建一个会飞的鱼,代码如下。


class MainObject {
    public static void main(String[] args) throws Exception {

        SimpleInvocationHandler handler = new SimpleInvocationHandler();
        handler.additionalItf(FishService.class, new FishServiceImpl());
        handler.additionalItf(FlyService.class, new FlyServiceImpl());

        FishService fishService = (FishService) handler.getProxy();
        fishService.swimming();
        // 让鱼飞起来
        ((FlyService)fishService).fly();
    }

}

运行结果:

swimming方法执行前
游泳中....
swimming方法执行后
fly方法执行前
飞...
fly方法执行后

总结

JDK动态代理的作用:
(1)通过JDK代理可以为接口创建代理。
(2)通过JDK代理可以为类增加更多的行为。

JDK动态代理的不足:
只能为接口创建代理,而不能为一个class类创建代理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值