欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java之动态代理

程序员文章站 2022-06-09 20:43:47
...

Java之动态代理


首先,我们需要明白代理就是本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。举例:春季回家买票让人代买。

**注意:
1.虽然让别人来做了,但是钱还是自己来付,只不过就是我只需要找代理付钱就可以实现我想要的了。
2.代理分为静态代理动态代理。静态代理其实就是用包装类来实现,动态代理就是jvm通过工具类Proxy和InvocationHandler接口为我们在程序运行过程中产生的这个代理对象。**(而程序运行过程中产生对象其实就是反射,所以动态代理其实就是通过反射来为某一个对象生成一个代理对象。


1.静态代理(包装设计模式包装类)

设计一个dao接口,代码如下:

//进行各种操作的接口
public interface Dao {
    public abstract void find();
    public abstract void save();
    public abstract void delete();
    public abstract void update();
}

进行用户操作Dao的实现类,代码如下:

public class UserDao implements Dao {
    public void find() {
        System.out.println("find()");
    }
    public void save() {
        System.out.println("save()");
    }
    public void delete() {
        System.out.println("delete()");
    }
    public void update() {
        System.out.println("update()");
    }
}

我们要注意:代理对象UserDaoProxy和被代理对象UserDao需要实现同样的接口(原因很简单,即你本身具备买票功能只不过图方便通过代理买车票,代理也得具备买票功能吧),所以代理对象持有一个被代理对象,代理对象的方法被调用时,我们可以做一些动作,再去调用真正被代理对象的方法,就比如下面的例子在代理对象中对用户的权限进行检查。

public class UserDaoProxy implements Dao {
    private Dao dao;
    public UserDaoProxy(Dao dao) {
        this.dao = dao;
    }
    public void delete() {
        // 检查权限
        System.out.println("检查权限");
        this.dao.delete();
    }
    public void find() {
        this.dao.find();
    }
    public void save() {
        this.dao.save();
    }
    public void update() {
        this.dao.update();
    }
}

进行简单测试一下,代码如下:

    //相当于买票功能让代理去做了,而且还做了一个检查的操作
    Dao dao = new UserDaoProxy(new UserDao());
    dao.find();
    dao.save();
    dao.update();
    dao.delete();

分析:上面的静态代理就是一个包装类的思想。而且静态代理的缺点也很明显,如果被代理对象有n多操作,对于每个操作我们需要加上一些小动作,那么我们这个代理类的代码的编写估计会把我们搞死。所以动态代理对象就是来解决这些问题的。


1.动态代理

  • Proxy类中的方法创建动态代理类对象public static Object newProxyInstance(ClassLoader loader,Class
public class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象,即被代理对象
    private User user; //对用户的权限进行判断,所以在这个准备动手脚的类中不得有User啊

    public MyInvocationHandler(Object target, User user) {
        this.target = target;
        this.user = user;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("delete".equals(method.getName())) {
            if (!"admin".equals(user.getUsername())) {
                throw new RuntimeException("没有访问 delete 的权限");
            }
        }
        /*
         * 不动任何的手脚调用被代理对象同样的方法.代理对象的X方法被调用时,我们就调用被代理代理X方法,调用者给代理对象传什么值,
         * 我们就给被代理对象传什么值,被代理对象返回什么值,我们就让代理对象返回什么值
         */
        Object result = method.invoke(target, args);
        System.out.println("成功运行");//我们在可以运行的操作后,均打印一下提示

        return result; // 返回的是代理对象

        // System.out.println("权限校验");
        // Object result = method.invoke(target, args);
        // System.out.println("日志记录");
        // 以上三句就是同时对各种方法执行前后动了手脚,而上面的判断语句就是在执行方法前,而且是指定类delete方法前,进行了验证工作!!!
    }
}

2.创建一个能够生成动态代理对象的类,即持着一个被代理对象,通过Proxy工具类的newDaoProxy方法产生该被代理对象的动态代理对象

public class ConStructorDaoProxy {
    // 持有一个 被代理对象
    private Dao daoTarget;

    public ConStructorDaoProxy(Dao daoTarget) {
        this.daoTarget = daoTarget;
    }

    // 产生代理对象的第一种写法,按部就班的按照反射方式的一步一步地来。
    public Dao newDaoProxy(User user) throws Exception {
        // 调用工具类 Proxy 的方法,自动生成一个类, 告诉工具类去实现什么接口
        Class proxyClass = Proxy.getProxyClass(this.daoTarget.getClass().getClassLoader(),
                this.daoTarget.getClass().getInterfaces());
        Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
        MyInvocationHandler handler = new MyInvocationHandler(this.daoTarget, user);
        // 产生代理对象
        Dao daoProxy = (Dao) constructor.newInstance(handler);
        return daoProxy;
    }

    // 产生代理对象的第二种写法,使用工具类Proxy现成的方法直接new代理对象实例,所以平时编写中还是用这一种吧
    public Dao newDaoProxy1(User user) {
        MyInvocationHandler handler = new MyInvocationHandler(this.daoTarget, user);
        Dao daoProxy = (Dao) Proxy.newProxyInstance(this.daoTarget.getClass().getClassLoader(),
                this.daoTarget.getClass().getInterfaces(), handler);
        return daoProxy;
    }
}

测试一下:

//为测试创建一个User类
class User {
    private String username;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
}

//进行测试
public class Test {
    public static void main(String[] args) throws Exception {
        //初始化创建代理对象类,并传入被代理对象
        ConStructorDaoProxy conStructorDaoProxy = new ConStructorDaoProxy(new UserDao());
        //需要被验证权限的用户
        User user = new User();
        user.setUsername("admin");
        //创建动态代理对象,并且将带验证的用户信息传入    
        Dao daoProxy = conStructorDaoProxy.newDaoProxy(user);
        //使用代理对象进行方法调用
        daoProxy.find();
        daoProxy.save();
        daoProxy.update();
        daoProxy.delete();
    }
}

测试结果不言而喻,如果用户是admin,那么肯定是可以进行删除操作的,否则就报一个删除操作没有权限的异常。如:

//权限不够,即用户不是admin的的测试结果:
    find()  
    哈哈
    Exception in thread "main" save()
    哈哈
    update()
    哈哈
    java.lang.RuntimeException: 没有访问 delete 的权限
//我们还可以发现,动态代理运行各个方法时,使用的多线程并发的机制。

3.总结一下动态代理大概的过程

结合上面的代码实现,我们可以总结一下动态代码的运行过程:Proxy类通过newProxyInstance方法创建动态代理类对象,该方法说白了就是用被代理对象的类加载器来动态的加载一个代理对象类(该代理类实现的接口和被代理类实现的接口一样),拿到InvocationHandler接口的实现类(该类中对于原来的操作动了很多手脚)中的动态代理对象proxy。

所以设计动态代理时,就拿上面的需求的例子来说:我们只需要明确对哪个对象(例子中是UserDao)进行代理,然后在InvocationHandler接口的实现类中把小动作按照API的要求设计好(例子中就是权限的判断),最后通过Proxy.newProxyInstance产生代理对象(该代理对象用Dao接口接收就行,因为代理对象和被代理对象需要实现同样的接口),进而调用相关方法就行了

相关标签: java 动态代理

上一篇: Java SE Lesson 2

下一篇: Java SE 反射