善行日记,cz6717航班,太空飞车党
只有光头才能变强
回顾前面:
多线程就先告一段落了,昨天写完多线程,本来打算是看IO的知识点的,后来看了一下IO的几种模型,又翻了一下《Java编程思想》。不知道从哪下手~~
在看到FilterInputStream
和FilterOutputStream
时看到了之前常听见的装饰模式(对IO一定了解的同学可能都会知道那么一句话:在IO用得最多的就是装饰模式了)!
看到这里你以为我要讲装饰模式了么?不是,今天我们来讲讲什么是代理模式(就是这么皮,装饰模式明天讲吧~)。
受知乎@Beautiful Java文章和《设计模式之禅》的启发,我也来搞一篇脑洞小开的文章..
由标题可知,这篇文章是写给我女朋友看的。自从她知道我开了公众号以后就每天看我写的文章,之前写点小算法的时候她觉得编程还是挺有意思,想去学学。可是,从我开始写Java容器、多线程,她说一点都看不懂了。于是乎,现在来写点既高大尚、又容易懂的东西。
GirlFriend girlFriend = new GirlFriend(); sayingProxy(girlFriend);
那么接下来就开始吧,如果文章有错误的地方请大家多多包涵,不吝在评论区指正哦~
声明:本文使用JDK1.8
代理模式是一种非常好理解的一种设计模式,生活中处处都有代理:
无论是经纪人、黄牛、游戏代练、律师他们都是得帮我们干活。但是他们不能一手包办的,仅仅在“我”的基础上处理一些杂碎的东西(我们不愿意干、或者干不了的东西)。
再举个例子:
所以说代理模式就是:当前对象不愿意干的,没法干的东西委托给别的对象来做,我只要做好本分的东西就好了!
这里有一个程序员接口,他们每天就是写代码
public interface Programmer { // 程序员每天都写代码 void coding(); }
Java3y也是一个程序员,他也写代码(每个程序员写的代码都不一样,所以分了接口和实现类)
public class Java3y implements Programmer { @Override public void coding() { System.out.println("Java3y最新文章:......给女朋友讲解什么是代理模式......."); } }
此时Java3y已经是一个网红了,他不想枯燥地写代码。他在想:“在写代码时能赚钱就好咯,有人给我钱,我才写代码”。但是,Java3y的文笔太烂了,一旦有什么冬瓜豆腐,分分钟变成过气网红,这是Java3y不愿意看到的。
而知乎、博客园这种平台又不能自己给自己点赞来吸引流量(-->当前对象无法做)
所以Java3y去请了一个程序员大V(代理)来实现自己的计划,这个程序员大V会每次让Java3y发文章时,就给Java3y点赞、评论、鼓吹这文章好。只要流量有了,钱就到手了。
public class ProgrammerBigV implements Programmer { // 指定程序员大V要让谁发文章(先发文章、后点赞) private Java3y java3y ; public ProgrammerBigV(Java3y java3y) { this.java3y = java3y; } // 程序员大V点赞评论收藏转发 public void upvote() { System.out.println("程序员大V点赞评论收藏转发!"); } @Override public void coding() { // 让Java3y发文章 java3y.coding(); // 程序员大V点赞评论收藏转发! upvote(); } }
文章(代码)还是由Java3y来发,但每次发送之后程序员大V都会点赞。
public class Main { public static void main(String[] args) { // 想要发达的Java3y Java3y java3y = new Java3y(); // 受委托程序员大V Programmer programmer = new ProgrammerBigV(java3y); // 受委托程序员大V让Java3y发文章,大V(自己)来点赞 programmer.coding(); } }
这样一来,不明真相的路人就觉得Java3y是真厉害,知识付费。
经过一段时间,Java3y尝到甜头了,觉得这是一条财路。于是Java3y给足了程序员大V钱,让程序员大V只做他的生意,不能做其他人的生意(断了其他人的财路)。
于是乎,程序员大V只做Java3y一个人的生意:
public class ProgrammerBigV implements Programmer { // 指定程序员大V要给Java3y点赞 private Java3y java3y ; // 只做Java3y的生意了 public ProgrammerBigV() { this.java3y = new Java3y(); } // 程序员大V点赞评论收藏转发 public void upvote() { System.out.println("程序员大V点赞评论收藏转发!"); } @Override public void coding() { // 让Java3y发文章了 java3y.coding(); // 程序员大V点赞评论收藏转发! upvote(); } }
于是乎,程序员大V想要赚点零花钱的时候直接让Java3y发文章就好了。
public class Main { public static void main(String[] args) { // 受委托程序员大V Programmer programmer = new ProgrammerBigV(); // 受委托程序员大V让Java3y发文章,大V来点赞 programmer.coding(); } }
此时,真实对象(Java3y)对外界来说是透明的。
程序员大V看到Java3y一直顺风顺水,赚大钱了。觉得是时候要加价了,于是在点赞完毕后就跟Java3y说每点完一次赞加100块!
于是乎,程序员大V就增添了另外一个方法:addMoney()
public class ProgrammerBigV implements Programmer { // ..省略了上面的代码 // 加价啦 public void addMoney() { System.out.println("这次我要加100块"); } @Override public void coding() { // 让Java3y发文章了 java3y.coding(); // 程序员大V点赞评论收藏转发! upvote(); // 加价 addMoney(); } }
于是乎程序员大V每次都能多100块:
几年时间过去了,Java3y靠着程序员的大V点赞还是没发财(本质上Java3y还没有干货,没受到大众的认可)。此时已经有很多人晋升成了程序员大V了,但是之前的那个程序员大V还是一直累加着钱...虽然在开始的时候Java3y尝到了甜头,但现在Java3y财政已经匮乏了。
Java3y将自己的失败认为:一定是那个程序员大V转门为我点赞被识破了,吃瓜群众都知道了,他收费又那么贵。
于是Java3y不请程序员大V了,请水军来点赞(水军便宜,只要能点赞就行了):
public class Main { public static void main(String[] args1) { // Java3y请水军 Java3y java3y = new Java3y(); Programmer programmerWaterArmy = (Programmer) Proxy.newProxyInstance(java3y.getClass().getClassLoader(), java3y.getClass().getInterfaces(), (proxy, method, args) -> { // 如果是调用coding方法,那么水军就要点赞了 if (method.getName().equals("coding")) { method.invoke(java3y, args); System.out.println("我是水军,我来点赞了!"); } else { // 如果不是调用coding方法,那么调用原对象的方法 return method.invoke(java3y, args); } return null; }); // 每当Java3y写完文章,水军都会点赞 programmerWaterArmy.coding(); } }
每当Java3y发文章的时候,水军都会点赞。
Java3y感叹:请水军真是方便啊~
我们来看看究竟是怎么请水军的:
Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,该方法需要三个参数:
在编写动态代理之前,要明确几个概念:
上面也说了:代理对象会实现接口的所有方法,这些实现的方法交由我们的handler来处理!
invoke()
调用所以动态代理调用过程是这样子的:
很明显的是:
使用静态代理时:
使用动态代理时:
我们之前写中文过滤器的时候,需要使用包装设计模式来设计一个request类。如果不是Servlet提供了实现类给我们,我们使用包装设计模式会比较麻烦
现在我们学习了动态代理了,动态代理就是拦截直接访问对象,可以给对象进行增强的一项技能
public void doFilter(final ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException { final HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); //放出去的是代理对象 chain.doFilter((ServletRequest) Proxy.newProxyInstance(CharacterEncodingFilter.class.getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断是不是getParameter方法 if (!method.getName().equals("getParameter")) { //不是就使用request调用 return method.invoke(request, args); } //判断是否是get类型的 if (!request.getMethod().equalsIgnoreCase("get")) { return method.invoke(request, args); } //执行到这里,只能是get类型的getParameter方法了。 String value = (String) method.invoke(request, args); if (value == null) { return null; } return new String(value.getBytes("ISO8859-1"), "UTF-8"); } }), response); }
第一次以这种方式写文章,举的例子可能会不妥,希望大家见谅~
本文主要讲解了代理模式的几个要点,其实还有一些细节的:比如“强制代理”(只能通过被代理对象找到代理对象,不能绕过代理对象直接访问被代理对象)。只是用得比较少,我就不说了~~
要实现动态代理必须要有接口的,动态代理是基于接口来代理的(实现接口的所有方法),如果没有接口的话我们可以考虑cglib代理。
cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能!
这里我就不再贴出代码来了,因为cglib的代理教程也很多,与动态代理实现差不多~~~
总的来说:代理模式是我们写代码中用得很多的一种模式了,Spring的AOP底层其实就是动态代理来实现的-->面向切面编程。具体可参考我之前写的那篇文章:
其实只要记住一点:原有的对象需要额外的功能,想想动态代理这项技术!
参考资料:
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友
文章的目录导航:
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
浅析我对 String、StringBuilder、StringBuffer 的理解
使用IDEA搭建SSM框架的详细教程(spring + springMVC +MyBatis)
Springboot整合freemarker 404问题解决方案
引入mybatis-plus报 Invalid bound statement错误问题的解决方法
网友评论