只有光頭才能變強
創(chuàng)新互聯(lián)建站主要從事網(wǎng)站設計制作、網(wǎng)站制作、網(wǎng)頁設計、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務。立足成都服務任縣,10余年網(wǎng)站建設經驗,價格優(yōu)惠、服務專業(yè),歡迎來電咨詢建站服務:13518219792
多線程就先告一段落了,昨天寫完多線程,本來打算是看IO的知識點的,后來看了一下IO的幾種模型,又翻了一下《Java編程思想》。不知道從哪下手~~
在看到FilterInputStream和FilterOutputStream時看到了之前常聽見的裝飾模式(對IO一定了解的同學可能都會知道那么一句話:在IO用得最多的就是裝飾模式了)!
看到這里你以為我要講裝飾模式了么?不是,今天我們來講講什么是代理模式(就是這么皮,裝飾模式明天講吧~)。
受知乎@Beautiful Java文章和《設計模式之禪》的啟發(fā),我也來搞一篇腦洞小開的文章..
由標題可知,這篇文章是寫給我女朋友看的。自從她知道我開了公眾號以后就每天看我寫的文章,之前寫點小算法的時候她覺得編程還是挺有意思,想去學學。可是,從我開始寫Java容器、多線程,她說一點都看不懂了。于是乎,現(xiàn)在來寫點既高大尚、又容易懂的東西。
GirlFriend girlFriend = new GirlFriend();
sayingProxy(girlFriend);
那么接下來就開始吧,如果文章有錯誤的地方請大家多多包涵,不吝在評論區(qū)指正哦~
聲明:本文使用JDK1.8
代理模式是一種非常好理解的一種設計模式,生活中處處都有代理:
王寶強作為一個明星,不可能什么事都由他自己干(約電視劇、排期之類的),于是他請了經紀人
去醫(yī)院掛號很麻煩怎么辦?找黃牛幫我們掛號
王者榮耀技術水平不夠,想要上分怎么辦?請游戲代練
寫點不正經的代碼被警察捉走了怎么辦?請律師幫我們打官司
無論是經紀人、黃牛、游戲代練、律師他們都是得幫我們干活。但是他們不能一手包辦的,僅僅在“我”的基礎上處理一些雜碎的東西(我們不愿意干、或者干不了的東西)。
導演找了黃寶強的經紀人讓王寶強去拍電影!
黃牛去排隊讓我們能掛上號!
游戲代練上分是我的微信賬號!
律師幫我處理法律上的問題,如果打官司失敗,牢還是由我來坐!
再舉個例子:
現(xiàn)在我是一個網(wǎng)紅,擁有很多粉絲。粉絲希望我天天寫代碼給他們看,那我肯定不能天天寫代碼啊,我豈不是很忙….于是乎,我就去找了個經紀人。這個經紀人就代表了我。當忠實粉絲想要我寫代碼的時候,應該是先找經紀人,告訴經紀人想讓我寫代碼。
十年過去了,我越來越紅了,頭發(fā)也越來越少。不是粉絲想要我寫代碼,我就寫了。我要收費了。但是呢,作為一個公眾人物,不可能是我自己說:我要收10000萬,我才會去寫代碼。于是這就讓經紀人對粉絲說:只有10000萬,我才會寫代碼。
無論外界是想要我干什么,都要經過我的經紀人。我的經紀人也會在其中考慮收費、推脫它們的請求。
經紀人就是代理,實際寫代碼的還是我
所以說代理模式就是:當前對象不愿意干的,沒法干的東西委托給別的對象來做,我只要做好本分的東西就好了!
這里有一個程序員接口,他們每天就是寫代碼
public interface Programmer{
// 程序員每天都寫代碼
void coding();
}
Java3y也是一個程序員,他也寫代碼(每個程序員寫的代碼都不一樣,所以分了接口和實現(xiàn)類)
public class Java3y implements Programmer{
@Override
public void coding(){
System.out.println("Java3y最新文章:......給女朋友講解什么是代理模式.......");
}
}
此時Java3y已經是一個網(wǎng)紅了,他不想枯燥地寫代碼。他在想:“在寫代碼時能賺錢就好咯,有人給我錢,我才寫代碼”。但是,Java3y的文筆太爛了,一旦有什么冬瓜豆腐,分分鐘變成過氣網(wǎng)紅,這是Java3y不愿意看到的。
而知乎、博客園這種平臺又不能自己給自己點贊來吸引流量(-->當前對象無法做)
所以Java3y去請了一個程序員大V(代理)來實現(xiàn)自己的計劃,這個程序員大V會每次讓Java3y發(fā)文章時,就給Java3y點贊、評論、鼓吹這文章好。只要流量有了,錢就到手了。
public class ProgrammerBigV implements Programmer{
// 指定程序員大V要讓誰發(fā)文章(先發(fā)文章、后點贊)
private Java3y java3y ;
public ProgrammerBigV(Java3y java3y){
this.java3y = java3y;
}
// 程序員大V點贊評論收藏轉發(fā)
public void upvote(){
System.out.println("程序員大V點贊評論收藏轉發(fā)!");
}
@Override
public void coding(){
// 讓Java3y發(fā)文章
java3y.coding();
// 程序員大V點贊評論收藏轉發(fā)!
upvote();
}
}
文章(代碼)還是由Java3y來發(fā),但每次發(fā)送之后程序員大V都會點贊。
public class Main{
public static void main(String[] args){
// 想要發(fā)達的Java3y
Java3y java3y = new Java3y();
// 受委托程序員大V
Programmer programmer = new ProgrammerBigV(java3y);
// 受委托程序員大V讓Java3y發(fā)文章,大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點贊評論收藏轉發(fā)
public void upvote(){
System.out.println("程序員大V點贊評論收藏轉發(fā)!");
}
@Override
public void coding(){
// 讓Java3y發(fā)文章了
java3y.coding();
// 程序員大V點贊評論收藏轉發(fā)!
upvote();
}
}
于是乎,程序員大V想要賺點零花錢的時候直接讓Java3y發(fā)文章就好了。
public class Main{
public static void main(String[] args){
// 受委托程序員大V
Programmer programmer = new ProgrammerBigV();
// 受委托程序員大V讓Java3y發(fā)文章,大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發(fā)文章了
java3y.coding();
// 程序員大V點贊評論收藏轉發(fā)!
upvote();
// 加價
addMoney();
}
}
于是乎程序員大V每次都能多100塊:

幾年時間過去了,Java3y靠著程序員的大V點贊還是沒發(fā)財(本質上Java3y還沒有干貨,沒受到大眾的認可)。此時已經有很多人晉升成了程序員大V了,但是之前的那個程序員大V還是一直累加著錢…雖然在開始的時候Java3y嘗到了甜頭,但現(xiàn)在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發(fā)文章的時候,水軍都會點贊。

Java3y感嘆:請水軍真是方便啊~
我們來看看究竟是怎么請水軍的:
Java提供了一個Proxy類,調用它的newInstance方法可以生成某個對象的代理對象,該方法需要三個參數(shù):

參數(shù)一:生成代理對象使用哪個類裝載器【一般我們使用的是被代理類的裝載器】
參數(shù)二:生成哪個對象的代理對象,通過接口指定【指定要被代理類的接口】
參數(shù)三:生成的代理對象的方法里干什么事【實現(xiàn)handler接口,我們想怎么實現(xiàn)就怎么實現(xiàn)】
在編寫動態(tài)代理之前,要明確幾個概念:
代理對象擁有目標對象相同的方法【因為參數(shù)二指定了對象的接口,代理對象會實現(xiàn)接口的所有方法】
用戶調用代理對象的什么方法,都是在調用處理器的invoke方法。【被攔截】
使用JDK動態(tài)代理必須要有接口【參數(shù)二需要接口】
上面也說了:代理對象會實現(xiàn)接口的所有方法,這些實現(xiàn)的方法交由我們的handler來處理!
所有通過動態(tài)代理實現(xiàn)的方法全部通過invoke()調用

所以動態(tài)代理調用過程是這樣子的:

很明顯的是:
靜態(tài)代理需要自己寫代理類-->代理類需要實現(xiàn)與目標對象相同的接口
而動態(tài)代理不需要自己編寫代理類--->(是動態(tài)生成的)
使用靜態(tài)代理時:
如果目標對象的接口有很多方法的話,那我們還是得一一實現(xiàn),這樣就會比較麻煩
使用動態(tài)代理時:
代理對象的生成,是利用JDKAPI,動態(tài)地在內存中構建代理對象(需要我們指定創(chuàng)建 代理對象/目標對象 實現(xiàn)的接口的類型),并且會默認實現(xiàn)接口的全部方法。
我們之前寫中文過濾器的時候,需要使用包裝設計模式來設計一個request類。如果不是Servlet提供了實現(xiàn)類給我們,我們使用包裝設計模式會比較麻煩
現(xiàn)在我們學習了動態(tài)代理了,動態(tài)代理就是攔截直接訪問對象,可以給對象進行增強的一項技能
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);
}
//執(zhí)行到這里,只能是get類型的getParameter方法了。
String value = (String) method.invoke(request, args);
if (value == null) {
return null;
}
return new String(value.getBytes("ISO8859-1"), "UTF-8");
}
}), response);
}
第一次以這種方式寫文章,舉的例子可能會不妥,希望大家見諒~
本文主要講解了代理模式的幾個要點,其實還有一些細節(jié)的:比如“強制代理”(只能通過被代理對象找到代理對象,不能繞過代理對象直接訪問被代理對象)。只是用得比較少,我就不說了~~
要實現(xiàn)動態(tài)代理必須要有接口的,動態(tài)代理是基于接口來代理的(實現(xiàn)接口的所有方法),如果沒有接口的話我們可以考慮cglib代理。
cglib代理也叫子類代理,從內存中構建出一個子類來擴展目標對象的功能!
這里我就不再貼出代碼來了,因為cglib的代理教程也很多,與動態(tài)代理實現(xiàn)差不多~~~
總的來說:代理模式是我們寫代碼中用得很多的一種模式了,Spring的AOP底層其實就是動態(tài)代理來實現(xiàn)的-->面向切面編程。具體可參考我之前寫的那篇文章:
Spring【AOP模塊】就這么簡單
其實只要記住一點:原有的對象需要額外的功能,想想動態(tài)代理這項技術!
參考資料:
《設計模式之禪》
https://wangjingxin.top/2016/11/16/proxy/
https://www.cnblogs.com/doucheyard/p/5737366.html
當前名稱:給女朋友講解什么是代理模式
網(wǎng)頁路徑:http://chinadenli.net/article12/ipcsdc.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供關鍵詞優(yōu)化、手機網(wǎng)站建設、服務器托管、網(wǎng)站制作、微信公眾號、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)