在说动态代理前,先说一下代理模式和静态代理。
一.代理模式
代理模式的结构图:
从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字节码生成框架,使用字节码技术生成代理类,在类的执行过程中比较高效。