Java动态代理的使用

在说动态代理前,先说一下代理模式和静态代理。

一.代理模式

代理模式的结构图:

在这里插入图片描述

从UML图中,可以看出真正实现的类和代理类都继承了抽象的主题类(通常是接口),这样代理类和真正的实现类有相同的方法,使得真正的实现类对客户端是透明的。

二.静态代理

用户管理抽象类(接口):

public interface UserManager {

    public void addUser(String userId, String userName);
    
    public void delUser(String userId);
}

真正的用户管理实现类(委托类):

public class UserManagerImpl implements UserManager {
 
    @Override
    public void addUser(String userId, String userName) {
        System.out.println("UserManagerImpl.addUser");
    }
 
    @Override
    public void delUser(String userId) {
        System.out.println("UserManagerImpl.delUser");
    }
}

代理用户管理实现类(代理类):

public class UserManagerImplProxy implements UserManager {
    // 目标对象
    private UserManager userManager;
 
    // 通过构造方法传入目标对象
    public UserManagerImplProxy(UserManager userManager) {
        this.userManager = userManager;
    }
 
    @Override
    public void addUser(String userId, String userName) {
        // 处理前
        System.out.println("before add user");
        // 开始添加用户
        userManager.addUser(userId, userName);
        // 处理后
        System.out.println("after add user");
    }
 
    @Override
    public void delUser(String userId) {
        userManager.delUser(userId);
    }
}

客户端调用:

public class Client {  
    public static void main(String[] args){   
        UserManager um=new UserManagerImplProxy(new UserManagerImpl());  
        um.addUser("1234", "张三");  
    }  
}

运行结果:
在这里插入图片描述

静态代理的缺点:

(1)代理类和委托类(真正的实现类)实现了相同的接口,并且代理类通过委托类实现了相同的方法。这样就形成了大量的重复代码,如果接口增加一个方法,那么除了委托类要实现这个方法外,代理类也要实现该方法。这就增加了代码维护的成本。

(2)代理对象只能为一种类型的对象服务。如果要为多个对象服务,需要对多个对象进行代理,这在程序规模较大时就不适用了。上述的例子中,只为UserManager类进行代理,如果要为其它类例如Group类进行代理,需要增加Group类的代理类。即静态代理类只能为特定的接口服务,如果要为多个接口服务,则需要建立多个代理类。

三.动态代理

如果想通过一个代理类实现全部的代理功能,就要使用动态代理。动态代理分为JDK代理和cglib代理。

1.JDK动态代理

真正的用户管理实现类:

public class UserManagerImpl implements UserManager {
 
    @Override
    public void addUser(String id, String name) {
        System.out.println("user的id:" + id + ",name:" + name);
    }
 
    @Override
    public void deleteUser(String name) {
        System.out.println("已删除" + name);
    }
}

代理用户管理实现类(代理类):

public class LogHandler implements InvocationHandler{
 
    private Object targetObject;

    //Proxy为动态代理类
    public Object getInstance(Object targetObject) {
        this.targetObject = targetObject;
        Class clazz = targetObject.getClass();
        return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
    }

    /**
     * @param proxy 表示代理类
     * @param method 表示代理的方法
     * @param args 表示代理方法的参数列表
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before the method");
        Object result = method.invoke(targetObject, args);
        System.out.println("after the method");
        return result;
    }
}

在getInstance方法中,被代理对象targetObject通过参数传递进来,首先获取其class对象clazz,然后通过clazz.getClassLoader()获取ClassLoader对象,通过clazz.getInterfaces()获取targetObject所实现的所有接口。

对于newProxyInstance方法,第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器,第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口,第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法。

客户端代码:

public class Client {
    public static void main(String[] args) {
        LogHandler lh = new LogHandler();
        UserManager um = (UserManager) lh.getInstance(new UserManagerImpl());
        um.addUser("124", "李四");
        um.deleteUser("张三");
    }
}

运行结果
在这里插入图片描述

可以发现,LogHandler这个代理类可以代理不同类型的对象,那么被代理对象的所有方法都会通过代理类中的invoke方法进行处理,因此我们可以在method.invoke(targetObject, args);方法的前后设置日志系统,事务等等。

2.Cglib动态代理

(需要使用外部jar包:cglib和asm)

真正的用户管理实现类:

public class UserManagerImpl implements UserManager{
 
    @Override
    public void addUser(String id, String name) {
        System.out.println("添加用户的id:" + id + ",姓名:" + name);
    }
     
    @Override
    public void deleteUser(String name) {
        System.out.println("删除的用户:" + name);
    }
}

代理用户管理实现类(代理类):

public class LogHandler implements MethodInterceptor{

    private Object targetObject;
    
    public Object getInstance(Object targetObject) {
        this.targetObject = targetObject;
        Enhancer enh = new Enhancer();
        //将被代理类作为超类,即将代理类作为被代理类的子类
        enh.setSuperclass(targetObject.getClass());
        enh.setCallback(this);
        return enh.create();
    }

    /**  
      * @param obj 表示this,即增强的对象
      * @param method 表示被拦截的方法
      * @param args 表示方法的参数列表
      * @param methodProxy 表示用来唤醒超类的方法,超类即增强的对象
      */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("before the method");
        //Object ret = method.invoke(targetObject, args);(推荐下一行的写法)
        Object ret = methodProxy.invokeSuper(obj, args);
        System.out.println("after the method");
        return ret;
    }
 
}

客户端代码:

public class Client {
    public static void main(String[] args) {
        LogHandler lh = new LogHandler();
        UserManager um = (UserManager) lh.getInstance(new UserManagerImpl());
        um.addUser("3214", "王五");
        um.deleteUser("张三");
    }
}

运行结果:
在这里插入图片描述

可以发现,cglib动态代理在代理过程中没有使用到被代理类要实现的接口。

3.JDK动态代理和cglib动态代理的区别

(1)JDK代理机制只能代理实现了接口的类,而没有实现接口的类只能用cglib动态代理实现。即JDK动态代理只能对实现了接口的类生成代理,而不能针对类,CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)。

(2)实现动态代理的机制:JDK动态代理采用Java原生的反射API技术实现,在生成类上比较高效;CGLib动态代理底层采用ASM字节码生成框架,使用字节码技术生成代理类,在类的执行过程中比较高效。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值