Java 静态代理VS动态代理
代理是一种常用的设计模式,其解决的问题是当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让客户端只了解代理类即可,从而控制对委托类对象的直接访问,隐藏和保护委托类对象。为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。
- 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
- 动态:在程序运行时运用反射机制动态创建而成。
静态代理
委托类
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"); } @Override public String findUser(String userId) { System.out.println("UserManagerImpl.findUser"); return "张三"; } @Override public void modifyUser(String userId, String userName) { System.out.println("UserManagerImpl.modifyUser"); } }
代理类
public class UserManagerImplProxy implements UserManager { // 目标对象 private UserManager userManager; // 通过构造方法传入目标对象 public UserManagerImplProxy(UserManager userManager){ this.userManager=userManager; } @Override public void addUser(String userId, String userName) { try{ //添加打印日志的功能 //开始添加用户 System.out.println("start-->addUser()"); userManager.addUser(userId, userName); //添加用户成功 System.out.println("success-->addUser()"); }catch(Exception e){ //添加用户失败 System.out.println("error-->addUser()"); } } @Override public void delUser(String userId) { userManager.delUser(userId); } @Override public String findUser(String userId) { userManager.findUser(userId); return "张三"; } @Override public void modifyUser(String userId, String userName) { userManager.modifyUser(userId,userName); } }
客户端
public class Client { public static void main(String[] args){ //UserManager userManager=new UserManagerImpl(); UserManager userManager=new UserManagerImplProxy(new UserManagerImpl()); userManager.addUser("1111", "张三"); } }
静态代理的缺点:
1)代理类和委托类实现了相同的接口,这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
2)代理对象只服务于一种类型的对象,如果要服务多类型的对象,势必要为每一种对象都进行代理。
所以,引入动态代理解决以上问题。
动态代理
委托类定义同静态代理。
代理类
//动态代理类都需要实现java.lang.reflect.InvocationHandler接口。该接口的invoke方法是调用委托类的所有方法时需要调用的。 public class LogHandler implements InvocationHandler { // 目标对象(委托类对象) private Object targetObject; //绑定关系,与具体的委托类绑定。 public Object newProxyInstance(Object targetObject){ this.targetObject=targetObject; //java.lang.reflect.Proxy类的newProxyInstance方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例 //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器 //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口 //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法 //根据传入的目标返回一个代理对象 return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(),this); } @Override //proxy表示代理,method表示原对象被调用的方法,args表示方法的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 自定义代理 if (methodName.equals("toString") && args == null) return "Proxy[" + compositeData + "]"; else if (methodName.equals("hashCode") && args == null) return compositeData.hashCode() + 0x43444948; else if (methodName.equals("equals") && args.length == 1 && method.getParameterTypes()[0] == Object.class) return equals(proxy, args[0]); else { return method.invoke(this, args); } } }
客户端
public class Client { public static void main(String[] args){ LogHandler logHandler=new LogHandler(); UserManager userManager=(UserManager)logHandler.newProxyInstance(new UserManagerImpl()); userManager.addUser("1111", "张三"); } }
动态代理的优点:
委托接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
动态代理其实就是AOP思想的实现。