代理模式:
应用场景:为其他对象提供一种代理以控制对这个对象的访问。从结构上来看和 Decorator 模式类似, 但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。 Spring 的 Proxy 模式在 AOP 中有体现,比如 JdkDynamicAopProxy 和 Cglib2AopProxy。
下面内容讲述了静态代理,动态代理,Cglib
1.1 静态代理
【练手代码】
一句话:在真实主题之间,前后均可包一层,实现代理的额外拓展功能+真实主题的原有实现, 目的是在不破坏原来真实主题的目标意图和功能下,对真实主题进行了功能加强和扩展。 interface Dun//dun 债务{ publicvoid getMoney(); }
class creditor implements Dun//creditor 债主{ publicvoid getMoney() { System.out.println("get My Money"); } }
class ProxyDun implements Dun//ProxyDun讨债人{ Dun dun;
public ProxyDun(Dun dun){ this.dun = dun; }
publicvoid getMoney() { System.out.println("我帮你讨债"); dun.getMoney(); System.out.println("讨债结束......"); } }
publicclass StaticProxyDemo { publicstaticvoid main(String[] args) { Dun test = new ProxyDun(new creditor()); test.getMoney(); } } |
【场景描述】
对于一个User类结合业务已经有了对其CRUD的操作,见下面的接口和类实现:
publicclass User { privatelonguuid; private String name; private String password;
publiclong getUuid() { returnuuid; } publicvoid setUuid(long uuid) { this.uuid = uuid; } public String getName() { returnname; } publicvoid setName(String name) { this.name = name; } public String getPassword() { returnpassword; } publicvoid setPassword(String password) { this.password = password; } } |
import com.ailk.po.User;
publicinterface UserService {
publicvoid add(User user);
publicvoid delete(long uuid);
publicvoid update(long uuid);
publicvoid getUserInfo(long uuid);
} |
import com.ailk.interfaces.UserService; import com.ailk.po.User;
publicclass UserServiceImpl implements UserService { publicvoid add(User user){ System.out.println("add用户成功"); } publicvoiddelete(long uuid){ System.out.println("delete用户成功"); } publicvoid update(long uuid){ System.out.println("update用户成功"); } publicvoid getUserInfo(long uuid){ System.out.println("getUserInfo用户成功"); } } |
import com.ailk.interfaces.UserService; import com.ailk.po.User; import com.ailk.service.impl.UserServiceImpl;
publicclass Test3 { publicstaticvoid main(String[] args) { User user = new User(); user.setName("张三"); user.setPassword("1234");
UserService service = new UserServiceImpl(); service.add(user); service.delete(3); service.update(3); service.getUserInfo(3); } } |
【问题描述】
结合上面对于User类的case,已经封装好并交付使用,突然某一天收到新需求,需要在每一个方法前面增加安全检查、日志记录或点击率分析这样的情况,那就需要在原来基础上大面积修改:
Ø 假如我们选取安全性检查作为新增需求,将该需求封装为一个方法
privatevoid checkSecurity(){ System.out.println("安全检查---------"); } |
导致结果就是:
1 整个系统中对应的CRUD方法都需要新增上述方法,导致修改面积过大且整个系统中处处散落着类似的方法,试想这才一个USER类就新增4个,那200个类需要新增多少?
2 原来的代码已经稳定,新的修改导致新的测试工作扩大,根据OCP原来,对新增开启对修改关闭,能不能想到一个好方法可以对原来的功能不破坏还加上了安全性检查?
【解决方法】
静态代理:
1 新增一个实现类StaticProxyUserImpl,让它具备两个特点:一)实现checkSecurity方法,二)同样的实现UserService接口。
2 组合优于继承的原则,让StaticProxyUserImpl通过UserService接口和UserServiceImpl发生组合关系,让StaticProxyUserImpl代理UserServiceImpl
import com.ailk.interfaces.UserService; import com.ailk.po.User;
publicclass StaticProxyUserImpl implements UserService { //目标接口 private UserService service; //通过接口关联实现类,在构造方法里面对目标对象进行代理 public StaticProxyUserImpl(UserService service) { this.service = service; } //新增方法,需要满足的业务新需求。 privatevoid checkSecurity() { System.out.println("安全检查---------"); } publicvoid add(User user) { checkSecurity(); service.add(user); }
publicvoid delete(long uuid) { checkSecurity(); service.delete(uuid); }
publicvoid update(long uuid) { checkSecurity(); service.update(uuid);
} publicvoid getUserInfo(long uuid) { checkSecurity(); service.getUserInfo(uuid); } } |
import com.ailk.interfaces.UserService; import com.ailk.po.User; import com.ailk.service.impl.StaticProxyUserImpl; import com.ailk.service.impl.UserServiceImpl;
publicclass Test3 { publicstaticvoid main(String[] args) { User user = new User(); user.setName("张三"); user.setPassword("1234");
//-------第一步测试 UserService service = new UserServiceImpl(); service.add(user); service.delete(3); service.update(3); service.getUserInfo(3); System.out.println("==============================================================="); //-------加了checkSecurity()方法,第二步测试 UserService service2 = new StaticProxyUserImpl(new UserServiceImpl()); service2.add(user); service2.delete(3); service2.update(3); service2.getUserInfo(3); } } |
测试结果: add用户成功 delete用户成功 update用户成功 getUserInfo用户成功 =============================================================== 安全检查--------- add用户成功 安全检查--------- delete用户成功 安全检查--------- update用户成功 安全检查--------- getUserInfo用户成功 |
结合上述:可以看到在不改变原来程序的前提下成功的植入了新功能。
1.2 动态代理
【场景描述】
针对上面静态代理的情况,已经解决了我们的需求。在不对原有代码修改的情况下,新
增加了我们的安全性检查方法,完成功能。但随之带来新的问题,一个类对应一个接口,代理类也对应实现类同样的接口,虽然我们把散落在系统中各个方法都收集汇拢到一块了,但一个接口就要出来一个代理类导致数量又膨胀,不利于管理。
能否整个系统中就一份,大家来使用,不用一个接口一个代理类来实现,减少因为代理接口的情况而导致系统膨胀。
【解决方法】
动态代理:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
publicclass DymicProxyImpl implements InvocationHandler {
private Object targetObject;
public Object getProxyInstance(Object targetObject){ this.targetObject = targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { checkSecurity(); return method.invoke(targetObject, args); } //新增方法,需要满足的业务新需求。 privatevoid checkSecurity(){ System.out.println("安全检查---动态代理实现--"); } } |
publicclass Test3 { publicstaticvoid main(String[] args) { User user = new User(); user.setName("张三"); user.setPassword("1234");
//-------第一步测试 UserService service = new UserServiceImpl(); service.add(user); service.delete(3); service.update(3); service.getUserInfo(3); System.out.println("==============================================================="); //-------第二步测试,加了checkSecurity()方法 UserService service2 = new StaticProxyUserImpl(new UserServiceImpl()); service2.add(user); service2.delete(3); service2.update(3); service2.getUserInfo(3); System.out.println("==============================================================="); //-------第三步测试,加了动态代理,整个系统一份统一调用 //该例被代理的对象是User,如果明天新增Customer、Order、Staff等实体,根据需要置换需要被代理的对象即可。 UserService service3 = (UserService) new DymicProxyImpl().getProxyInstance(new UserServiceImpl()); service3.add(user); service3.delete(3); service3.update(3); service3.getUserInfo(3); } } |
1.3 CGLIB代理
【场景描述】
前面的代理均是基于接口实现的代理,现实情况中可能需要被代理的类没有实现接口,也就是说上面的方法失效。那如何在类没有实现接口的情况下运用代理模式那?
考虑用CGLIB来实现。
【解决方法】
1) 什么是CGLIB?
2) 需要新增jar包
cglib-nodep-2.1_3.jar导入到Spring相关工程中。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;
publicclass CGlibProxyImpl implements MethodInterceptor { private Object targetObject;
public Object getProxyInstance(Object targetObject){ this.targetObject = targetObject; //用于生成代理对象 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(this.targetObject.getClass()); //设置回调对象为本身 enhancer.setCallback(this); return enhancer.create(); }
public Object intercept(Object arg0, Method arg1, Object[] args,MethodProxy methodProxy) throws Throwable { checkSecurity(); return methodProxy.invoke(targetObject,args); } //新增方法,需要满足的业务新需求。 privatevoid checkSecurity(){ System.out.println("安全检查---CGLIB实现--"); } } |
publicclass Test3 { publicstaticvoid main(String[] args) { User user = new User(); user.setName("张三"); user.setPassword("1234");
UserService service4 = (UserService)new CGlibProxyImpl().getProxyInstance(new UserServiceImpl()); service4.add(user); } } |