java設計模式--代理模式(靜態,jdk,cglib)
代理(Proxy)是一種設計模式,提供了對目標物件另外的訪問方式;即通過代理物件訪問目標物件.這樣做的好處是:可以在目標物件實現的基礎上, 增強額外的功能操作,即擴充套件目標物件的功能.這裡使用到程式設計中的一個思想:不要隨意去修改別人已經寫好的程式碼或者方法,如果需改修改,可以通過代理的方式來擴充套件該方法
實際應用場景: aop
案列:
通俗說法:談戀愛,你不想去送禮物,不想幹那些比較煩人並且不重要的事(當然重要的還是我們自己來是不),你就叫個人來代替你去送禮物等(或者我就壓根不會送禮物,需要代理擴充套件)其實你就要直接和她戀愛(這裡不寫太直接了,你們懂就好了),這就是代理模式(注意:此方法需謹慎現實別模仿,不然就搞不好就給別人做嫁衣了,到時候別跑來罵我)
程式碼:
靜態代理:
介面:
package com.sl.demo.proxy;
/**
* 介面
* @author pengkun
*
*/
public interface Subject {
/**
* 約會
*/
void engagement();
}
目標類:
package com.sl.demo.proxy; /** * 小明同學 * @author pengkun * */ public class XiaoMingStudent implements Subject { @Override public void engagement() { System.out.println("哈哈,小明同學約會去絡!"); } }
代理:
package com.sl.demo.proxy; /** * 代理類 * @author pengkun * */ public class Proxy implements Subject{ //需要代理的學生 private Subject student; //建立建構函式,你總得讓我知道給誰代理吧 public Proxy(Subject student) { super(); this.student = student; } //代理擴充套件送花 public void sendFlower() { System.out.println("Proxy替小明送花"); } //代理擴充套件送巧克力 public void sendChocolate() { System.out.println("Proxy替小明送巧克力"); } @Override public void engagement() { //其他雜活累活讓代理去幹 this.sendFlower(); this.sendChocolate(); //約會肯定小明同學自己來啦 student.engagement(); System.out.println("搞定收工"); } }
測試:
@Test
public void test() {
//目標物件
Subject xm=new XiaoMingStudent();
//建立代理,給目標物件
Proxy proxy=new Proxy(xm);
proxy.engagement();
}
結果:
可以看出,代理類擴充套件了送花和送巧克力的方法,而小明同學只做最爽的那下就行了。。。
靜態代理在使用時,需要定義介面或者父類,被代理物件與代理物件一起實現相同的介面或者是繼承相同父類.
總結:
優:可以不修改目標類的情況擴充套件功能,
缺:因為要實現或繼承同一個介面所有代理類會很多,而且每次擴充套件功能的時候都要修改代理和介面
那怎麼解決呢?就是下面講的動態代理了
動態代理:也叫jdk動態代理
特點:代理物件不需要實現介面,當是目標物件必須實現
package com.sl.demo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 建立動態代理物件
* jdk代理
* @author pengkun
*
*/
public class ProxyFactory {
//持有一個目標物件
private Object target;
public ProxyFactory(Object target) {
super();
this.target = target;
}
//給目標物件建立代理物件
public Object getProxyInstance() {
//1.loader:指定當前目標物件使用類載入器,獲取載入器的方法是固定的
//2.interfaces:目標物件實現的介面的型別,使用泛型方式確認型別
//3.h:事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的方法作為引數傳入
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("幫小明送花");
System.out.println("幫小明送巧克力");
//讓小明自己去約會
Object returnValue= method.invoke(target, args);
System.out.println("搞定收工");
return returnValue;
}
}
);
}
}
測試:
@Test
public void testJDK() {
//目標物件
Subject xm=new XiaoMingStudent();
//建立代理,給目標物件
Subject proxy=(Subject) new ProxyFactory(xm).getProxyInstance();
proxy.engagement();
}
結果:
Cglib代理:
特點:
1.引入jar包spring-core-4.2.5.RELEASE.jar(注意spring-core-3.2以上才會包含cglib,3.2以下要匯入cglib.jar包)
2.代理的類不能為final,否則報錯
3.目標物件方法不能為final/static,否則不會攔截即不會執行擴充套件功能
4.與jdk動態代理區別就是目標類不用實現介面
注意:目標物件是沒實現介面的
package com.sl.demo.proxy;
/**
* 小明同學
* cglib目標類 不實現介面
* @author pengkun
*
*/
public class XiaoMingStudent2 {
public void engagement() {
System.out.println("哈哈,小明同學約會去絡!");
}
}
package com.sl.demo.proxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
* Cglib代理
* MethodInterceptor:方法攔截
* @author pengkun
*
*/
public class CglibProxyFactory implements MethodInterceptor {
//持有一個 目標物件(注意:目標物件沒有實現介面)
private Object target;
public CglibProxyFactory(Object target) {
super();
this.target = target;
}
//給目標物件建立個代理
public Object getProxyInstance() {
//建立工具類
Enhancer en=new Enhancer();
//設定父類
en.setSuperclass(XiaoMingStudent2.class);
//設定回撥函式
en.setCallback(this);
//建立代理物件(子類)並返回
return en.create();
}
/**
* 攔截
* obj:目標物件
* method:目標方法
* args:方法引數
* MethodProxy:它應該是cglib生成用來代替Method物件的一個物件
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
System.out.println("幫小明送花");
System.out.println("幫小明送巧克力");
//讓小明自己去約會
Object returnValue= method.invoke(target, args);
System.out.println("搞定收工");
return returnValue;
}
}
測試:
@Test
public void testCGLIB() {
//目標物件
XiaoMingStudent2 xm=new XiaoMingStudent2();
//建立代理,給目標物件
XiaoMingStudent2 proxy= (XiaoMingStudent2) new CglibProxyFactory(xm).getProxyInstance();
proxy.engagement();
}
最後總結:
目標物件有實現介面就用JDK代理,
目標物件沒有實現介面就用Cglib代理