概念:
什么是代理模式: 为其他对象提供一种代理,来控制对这个对象的访问;有些情况下一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
作用
1.隐藏委托类的具体实现
2.实现客户端和委托类的解耦,不改动委托类的情况下增加功能(如日志)
类图
以去超市买东西为例
静态代理:
预先就确定了代理和被代理对象间的关系,在编程中指编译前,代理类和被代理类的关系已经确定了;
/** * 委托类和代理类都实现了factory接口 **/ public interface Factory { void sell(); void ad(); }
public class Shop implements Factory { private Factory factory; public Shop(Factory factory) { this.factory = factory; } @Override public void sell() { System.out.println("商店代理销售"); factory.sell(); } @Override public void ad() { System.out.println("商店代理广告"); factory.ad(); } }
public class Vendor implements Factory { @Override public void sell() { System.out.println("供应商销售产品"); } @Override public void ad() { System.out.println("供应商打广告"); } }
public class Client { public static void main(String[] args) { // 供应商---被代理类 Vendor vendor = new Vendor(); // 创建供应商的代理类Shop Factory shop = new Shop(vendor); // 客户端使用时面向的是代理类Shop。 shop.sell(); shop.ad(); } }
动态代理:
静态代理每个代理类都必须实现代理接口,如果业务很多,会导致类迅速膨胀,为了避免这点,java基于反射提供了动态代理,实现方式常见的有两种
1.JDK提供的InvocationHandler接口和java.lang.reflect包下的Proxy类
2.cglib实现的动态代理
基于动态代理,无论业务接口和委托类如何变化,代理类都可以不变化。
基于InvocationHandler接口
代理类和委托对象实现同一接口
public interface User { void login(String userName, String pwd); void logout(String userName); }
实现类:
public class UserImpl implements User { @Override public void login(String userName, String pwd) { System.out.println("用户名:" + userName + ",登录成功"); } @Override public void logout(String userName) { System.out.println("用户名:" + userName + ",注销"); } }
代理类:
public class UserDynamicProxy implements InvocationHandler { // 代理对象 private Object target; public <T> T getProxyInstance(Object target) { // 委托对象,真正的业务对象 this.target = target; // 获取Object类的ClassLoader ClassLoader cl = target.getClass().getClassLoader(); // 获取接口数组 Class<?>[] cs = target.getClass().getInterfaces(); // 获取代理对象并返回 return (T) Proxy.newProxyInstance(cl, cs, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object r = method.invoke(target, args); return r; } }
测试:
public class DynamicClient { public static void main(String[] args) { // 真实角色,委托人 User user = new UserImpl(); // 获取委托对象user的代理对象 UserDynamicProxy proxy = new UserDynamicProxy(); User userProxy = proxy.getProxyInstance(user); userProxy.login("a1","abcd"); userProxy.login("a2","abcd"); userProxy.login("a3","abcd"); userProxy.login("a4","abcd"); userProxy.login("a5","abcd"); userProxy.logout("a1"); } }
输出:
用户名:a1,登录成功
用户名:a2,登录成功
用户名:a3,登录成功
用户名:a4,登录成功
用户名:a5,登录成功
用户名:a1,注销
基于JDK有个前提条件就是“代理对象和委托对象继承同一接口”,如果没有接口怎么办呢?这时候我们可以菜用Cglib实现动态代理。
基于Cglib
委托类
public class UserClient { public void login(String userName, String pwd) { System.out.println("用户名:" + userName + ",登录成功"); } public void logout(String userName) { System.out.println("用户名:" + userName + ",注销"); } }
代理类
public class UserClientCglibProxy implements MethodInterceptor { // 委托对象 private Object target; public <T>T getProxyInstance(Object target) { this.target = target; // 增强类对象 Enhancer enhancer = new Enhancer(); // 设置其超类为target的类类型 enhancer.setSuperclass(this.target.getClass()); // 回调方法 enhancer.setCallback(this); // 创建代理对象 return (T)enhancer.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object r = proxy.invokeSuper(obj, args); return r; } }
测试:
public class DynamicClient { public static void main(String[] args) { UserClient userClient = new UserClient(); // 获取委托对象user的代理对象 UserClientCglibProxy userClientCglibProxy = new UserClientCglibProxy(); UserClient userProxy = userClientCglibProxy.getProxyInstance(userClient); userProxy.login("a1","abcd"); userProxy.login("a2","abcd"); userProxy.login("a3","abcd"); userProxy.login("a4","abcd"); userProxy.login("a5","abcd"); userProxy.logout("a1"); } }