一、什麼是代理模式
先來看看百度的解釋
代理模式的定義:爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
——百度百科
個人感覺這個將的太抽象了,不過這裏有個概念-“中介”,這個詞就很能體現代理模式的精髓,一切代理模式其實都是一箇中介,能夠讓使用者能夠更加方便去使用,而不需要考慮其中複雜的過程。
二、使用代理模式的優缺點
1.優點
- 讓代碼的職責更加清晰,經過代理之後,在使用方法時就不需要考慮業務邏輯之外的東西
- 具有高擴展性,能夠很好的與其他方法進行組合
- 代理對象可以在客戶端和目標對象之間起到中介的作用,這樣起到了中介的作用和保護了目標對象的作用
2.缺點
- 增加了系統複雜度,這也是不可避免的,代理模式在不影響使用的情況下,增加了額外的功能,必然會增加複雜度
- △ 由於在客戶端和真實對象之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢(個人覺得,實例之間的相互調用應該是一個很迅速的過程,有沒有代理的效率應該是差不多的)
三、代理模式的分類
我這裏不是按照實現方法來進行分類的,而是按照代理模式的本質來分類的。
1.增強型代理
1. 特點
核心功能不變(不能修改原來的功能)
業務無關性(不修改核心的業務邏輯,降低系統的複雜性)
使用習慣不變
附加新功能(只是讓需要代理的功能附加不影響原來的功能的功能,相當於加個buff)
普適性(增強的功能能夠加到其他方法中)
總的來說,增強型代理只是對一個方法進行一些無關業務的操作,安全校驗,上鎖,記錄等一些無關業務的操作。
2. 常見的例子
增強代理的實現可以有靜態代理,也可以有動態代理
常見的例子有:
- spring的aop,聲明型事物
- 監控操作(當用戶操作後,做一個記錄)
2.鏈接型代理
1. 特點
跟增強型代理一些相似的特點
核心功能不變
業務無關性
使用更加方便
總的來說,鏈接型代理就是簡化我們對方法的調用,也是不改變業務,一般來說,不會做一些增強型的操作。
2. 常見的例子
鏈接型代理的實現同樣也是可以靜態,也可以動態。
常見的例子有:
- MyBtis的mapper代理
- springCloud中的ribbon,zuul等
四、代理模式的實現
1.靜態代理
靜態代理就是採用手動寫代碼的方式來實現,可以在不修改目標對象功能的前提下,實現功能擴展,擴展多樣化,但是需要編寫很多方法的擴展,管理成本增大
首先需要創建一個接口,接口中定義被代理類和代理類都要實現的方法,然後創建被代理類和代理類
/**
* @author xxj
* 靜態代理測試
*/
public class StaticProxy {
public static void main(String[] args) {
Company company=new Company("房地產公司","靠海別墅");
CompanyProxy companyProxy=new CompanyProxy(company,"小張");
companyProxy.sale();
}
/**
* 接口
* 賣東西的接口
*/
public interface Salor{
public void sale();
}
/**
* 被代理類
* 需要代理的公司
*/
public static class Company implements Salor{
String name;
String product;
public Company(String name,String product){
this.name=name;
this.product=product;
}
@Override
public void sale() {
System.out.println(name+"賣出"+product);
}
}
/**
* 代理類
* 中介
*/
public static class CompanyProxy implements Salor{
private Company company;
private String worker;
/**
* @param company 需要代理的公司
*/
public CompanyProxy(Company company,String worker){
this.company=company;
this.worker=worker;
}
@Override
public void sale() {
System.out.println(worker+"推銷--聯繫客戶---");
System.out.println("聯繫"+company.name+"進行交易--");
company.sale();
System.out.println(worker+"拿提成--");
}
}
}
個人感覺這更像鏈接型代理,因爲CompanyProxy的sale方法中是有做相關操作的(如果把打印到控制檯理解爲一個操作),而增強型代理則是打印一下狀態信息或者是監控,而不會去執行company.sale()
2.動態代理
動態代理生成的代理類都是通過動態拼接字節碼產生的,實際上代理類並不存在,而靜態代理中代理類則是真是存在的
JDK動態代理
JDK動態代理就不需要手動地去擴展方法的功能了,而是由一個實現了InvocationHandler接口的類來統一管理,不用寫每個方法的擴展,使代理類的管理成本降低,但是會是代理類的創建變得麻煩,這就可以套上一個工廠模式來創建代理類了
首先依然是要創建一個接口,被代理類需要實現這個接口,然後在創建一箇中介類實現InvocationHandler接口,然後調用Proxy提供的靜態方法newProxyInstance來創建代理類(注意需要用接口接收)
/**
* @author xxj
* JDK動態代理
*/
public class DynamicProxy {
public static void main(String[] args) {
Company company=new Company("房產公司","傍山別墅");
//中介公司
CompanyHandler companyHandler=new CompanyHandler(company,"啥都能賣公司");
//類加載器,可以強行理解爲銷售組長,要由他來叫人幹活
ClassLoader classLoader=company.getClass().getClassLoader();
//叫張三來替啥都能賣公司幹活
Salor zhangsan= (Salor) Proxy.newProxyInstance(classLoader,
company.getClass().getInterfaces(),companyHandler);
zhangsan.sale();
}
/**
* handle類
* 相當於中介公司
*/
public static class CompanyHandler implements InvocationHandler{
private Company company;
private String agentCompany;
public CompanyHandler(Company company,String agentCompany){
this.company=company;
this.agentCompany=agentCompany;
}
/**
* @param proxy 貌似不能使用
* @param method 調用invoke的method信息
* @param args method的參數信息
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(agentCompany+"推銷--聯繫客戶---");
System.out.println("聯繫"+company.name+"進行交易--");
//執行company的方法
method.invoke(company,args);
System.out.println(agentCompany+"拿提成--");
return null;
}
}
/**
* 被代理類
* 需要代理的公司
*/
public static class Company implements Salor {
String name;
String product;
public Company(String name,String product){
this.name=name;
this.product=product;
}
@Override
public void sale() {
System.out.println(name+"賣出"+product);
}
}
/**
* 接口
* 賣東西的接口
*/
public interface Salor{
public void sale();
}
}
Cglib動態代理
Cglib動態代理使用起來就比JDK動態代理方便多了,只需要寫一個ProxyFactory類就可以給其他類做代理了,而且不需要再寫接口了,但是它生成代理類的過程比較複雜,還需要依賴第三方jar包
首先,需要導入spring的spring-corejar包,然後只需要寫一個代理工廠類,需要實現MethodInterceptor接口,還要在內部提供構建代理類的方法,直接把被代理類放入代理工廠類就可以生成代理類了
maven
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.8.RELEASE</version>
</dependency>
/**
* @author xxj
* Cglib動態代理
*/
public class CglibDynamicProxy {
public static void main(String[] args) {
Company company=new Company("房產公司","私人莊園");
Company proxy= (Company) new ProxyFactory(company,"一定能賣出公司").getProxyInstance();
proxy.sale();
}
public static class ProxyFactory implements MethodInterceptor {
private Company company;
private String agentCompany;
public ProxyFactory(){}
public ProxyFactory(Company company, String agentCompany){
this.company=company;
this.agentCompany=agentCompany;
}
//給目標對象創建一個代理對象
public Object getProxyInstance(){
//1.工具類
Enhancer en = new Enhancer();
//2.設置父類
en.setSuperclass(company.getClass());
//3.設置回調函數
en.setCallback(this);
//4.創建子類(代理對象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println(agentCompany+"推銷--聯繫客戶---");
System.out.println("聯繫"+company.name+"進行交易--");
//執行company的方法
method.invoke(company,objects);
System.out.println(agentCompany+"拿提成--");
return null;
}
}
/**
* 被代理類
* 需要代理的公司
*/
public static class Company {
String name;
String product;
public Company(){}
public Company(String name,String product){
this.name=name;
this.product=product;
}
public void sale() {
System.out.println(name+"賣出"+product);
}
}
}
五、總結
無論那種代理模式都需要滿足不改變核心功能,不改變業務邏輯這兩個特點,一旦違背了,就會極大的增加系統的複雜度。
這裏分別舉兩個生活中的例子來作爲本章節的結束吧。
增強型代理,就像是微波爐(代理對象),當我們需要加熱食物(被代理對象)時,就將食物放入微波爐中,然後我們就會得到一個加熱了的食物(附加新功能)。這個過程沒有改變食物的本質(不改變核心功能),我們依然能喫(說加熱太燙吃不了的,你就槓吧)(使用習慣不變)一切食物都可以加熱(普適性)你想加熱別的東西也可以。
鏈接型代理,這個可以舉的例子太多了,像宅急送,我們只需要手機下單就可以購買漢堡(使用更加方便)我們手機下單最終做的事情就是買漢堡(不改變核心功能)
——————————————————————————————
如果本文章內容有問題,請直接評論或者私信我。如果覺得寫的還不錯的話,點個贊也是對我的支持哦
未經允許,不得轉載!