jdk動態代理與cglib動態代理
動態代理
動態代理23種設計模式之一
動態代理的基於反射的
代理模式:
作用:
1)功能增強:在原有的功能上,增加了額外的功能,做了功能增強
2)控制訪問:代理不讓使用者訪問目標,比如4S店不讓我直接找廠家
開發中,如果A類本來是呼叫C類的方法,完成某個功能 但是C不讓A呼叫 A不能訪問C,然後在A和C之間建立一個B代理類 C讓B訪問 B是代理類,既能訪問C,同時又能增加新的業務功能,增強對C的訪問 A通過訪問B,B訪問C,到了A訪問C的結果,雖然是間接訪問 有點類似使用者和廠家的關係 比如說小李想買輛邁凱倫,但是小李不能直接聯絡到邁凱倫的廠家,廠家並不提供個人業務,那怎麼辦呢?只能通過4S店了, 我們去4S店買,4S店幫我們去和廠家訂車,提車,並且還能給我們提供優質的服務。所以邁凱倫4S店就好比代理類,不僅幫我們去間接找到了廠家, 還對業務進行增強,為小李提供服務
實現代理的模式:
-
靜態代理:
1)代理類是自己實現的,自己建立一個java類,表示代理類
2)並且要代理的目標類也是確定的
-
動態代理
1)程式執行過程中,使用JDK的反射機制,建立代理類物件,並動態指定要代理的目標類
動態代理的實現分類:
- JDK動態代理:使用jdk反射包中的類和介面實現動態代理的功能
- cglib動態代理:第三方的工具庫,cglib的原理是繼承,通過繼承目標類,建立他的子類,在子類總重寫父類中同名的方法,實現功能的修改
因為cglib是繼承,重寫方法,所以要求目標類不能是final的,方法也不能是final的 cglib的要求目標類比較寬鬆,只要能繼承就可以了 cglib在很多的框架中使用,比如mybatis,spring框架中都有使用
區別:
JDK動態代理要求目標類必須有介面
複習method方法
public interface Hello { void sayHello(String name); }
public class HelloImpl implements Hello {
@Override
public void sayHello(String name) {
System.out.println("你好!"+name);
}
}
public class Test01 { @Test public void test01(){ Hello hello = new HelloImpl(); hello.sayHello("小李"); /* 你好!小李 */ }
public class Test01 {
@Test
public void test01(){
//通過反射機制來執行sayHello方法
Hello hello = new HelloImpl();
try {
Method method = Hello.class.getDeclaredMethod("sayHello", String.class);
method.invoke(hello,"啟亮");
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
你好!啟亮
*/
Object obj : 物件,要執行這個物件的方法
Object... args : 方法執行的引數值
Method是一個獨立的方法物件,代表所有sayHello方法的物件,通過invoke就可以執行任意物件的sayHello方法,然後通過invoke中引數指定具體執行那個物件中的sayHello方法,和執行方法鎖需要的引數
jdk動態代理的實現
反射包 java.lang.reflect;裡面有三個類
InvocationHandler
Method
Proxy
jdk動態代理的使用
1)Proxy類:
核心的物件,建立代理物件。之前建立物件都是new類的構造方法
現在是使用Proxy類的方法,代替new的使用
方法:newProxyInstance
原型:
public static Object newProxyInstance(
ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h
){ ... }
引數:
ClassLoader loader,
類載入器(目標物件的類載入器),負責向記憶體中載入物件,使用反射獲取物件的ClassLoader
Class<?>[] interfaces,
介面,目標物件實現的介面,也是反射獲取的
InvocationHandler h
我們自己寫的,代理類要完成的功能
返回值:就是代理的物件
2)newProxyInstance
InvocationHandler介面
原始碼:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable; }
InvocationHandler: 譯為:呼叫處理程式
invoke:表示代理物件要執行的功能程式碼,代理類要完成的功能就寫在這個方法中
完成的功能:1.呼叫目標方法,執行目標方法的功能
2.功能增強,在目標方法呼叫時,增加功能
引數說明:
proxy:jdk建立的代理物件,無需賦值
method:目標類中的方法 ,由jdk提供method物件的
args:目標類中方法的引數,由jdk提供
InvocationHandler介面:表示你的代理要幹什麼
怎麼使用:
1.既然是介面,先實現介面InvocationHandler
2.重寫invoke方法,原來靜態代理中代理類完成的功能寫在這裡
3)Method類:
表示方法的,具體來說就是目標類中的方法
作用:通過Method可以執行某個目標類的方法,Method.invoke();
Method.invoke() ; 中的invoke只是Method類中的方法
public Object invoke(...,...)是InvocationHandler介面中的方法
Method.invoke(目標物件,方法的引數);
通過這個目標物件就可以執行物件的方法,而不需要知道這個方法的名稱,因為method方法的值是jdk幫你確定的,這個物件本身就是jdk提供的
動態代理實現步驟
1.建立介面,定義目標類要完成的功能
2.建立目標類實現介面
3.建立InvocationHandler介面的實現類,在invoke方法中完成代理類的功能
● 呼叫目標方法
● 增強功能
4.使用Proxy類的靜態方法,建立代理物件,並把返回值轉換為介面型別
程式碼實現:
User.java
public interface User {
void add();
void delete();
void update();
void find();
}
UserImpl.java
public class UserImpl implements User {
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("刪");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public void find() {
System.out.println("查");
}
}
UserHandler.java
//必須實現InvocationHandler介面,完成代理類要做的功能
//1.呼叫目標方法 2.功能增強
public class UserHandler implements InvocationHandler {
private Object target = null;//目標物件
//動態代理:目標物件是動態的,不是固定的
//傳入的哪個物件,就給哪個物件建立代理
public UserHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
target:裡面這個物件是動態的,需要傳入
result:是方法執行的返回值,沒有可以不加
*/
System.out.println("前增強");//增強方法
Object result = method.invoke(target,args);//傳入的物件執行的每個方法,都會呼叫這個
System.out.println("後增強");//增強方法
//返回方法執行的返回值
return result;
}
}
MyProxy.java
public class MyProxy {
@Test
public void test() {
//1.建立目標物件
User user = new UserImpl();
//2.建立InvocationHandler物件
InvocationHandler handler = new UserHandler(user);
//3.建立代理物件
User userProxy = (User) Proxy.newProxyInstance(
user.getClass().getClassLoader(),//獲得這個物件的類載入器才能操作這個物件
user.getClass().getInterfaces(),
handler
);
//4.把型別轉換成代理物件的介面型別,因為目標物件實現了這個介面
//5.通過代理物件執行方法
userProxy.add();
System.out.println("=========");
userProxy.delete();
System.out.println("=========");
userProxy.update();
System.out.println("=========");
userProxy.find();
}
}
列印結果:
前增強
增
後增強
=========
前增強
刪
後增強
=========
前增強
改
後增強
=========
前增強
查
後增強
匿名內部類實現:
User.java
public interface User {
void add();
void delete();
void update();
void select();
}
UserImpl.java
public class UserImpl implements User {
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("刪");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public void select() {
System.out.println("查");
}
}
MyProxyTest.java
public class MyProxyTest {
private User user = new UserImpl();
@Test
public void test01(){
user.add();
user.delete();
user.update();
user.select();
System.out.println("================");
User proxy = (User) Proxy.newProxyInstance(
user.getClass().getClassLoader(),//被代理物件的類載入器
user.getClass().getInterfaces(),//獲取被代理物件所實現的介面,(有共同的特徵)
new InvocationHandler() { //就是具體實現代理的邏輯
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("A");
//所有代理物件呼叫的方法都會執行
Object invoke = method.invoke(user, args);
System.out.println("B");
return invoke;//返回給代理物件,有返回值就是此返回值
}
});
proxy.add();
System.out.println("*******");
proxy.delete();
System.out.println("*******");
proxy.update();
System.out.println("*******");
proxy.select();
}
}
*
cglib動態代理的實現
動態代理:
特點:位元組碼隨用隨建立,隨用隨載入
作用:不修改原始碼的基礎上對方法增強
分類:
基於介面的動態代理 jdk
基於子類的動態代理 cglib
基於子類的動態代理:
涉及的類:Enhancer
提供者:第三方cglib庫
如何建立代理物件:
使用Enhancer類中的create方法
建立代理物件的要求:
被代理類不能是最終類
create方法的引數:
Class:位元組碼
它是用於指定被代理物件的位元組碼。
Callback:用於提供增強的程式碼
它是讓我們寫如何代理。我們一般都是些一個該介面的實現類,通常情況下都是匿名內部類,但不是必須的。
此介面的實現類都是誰用誰寫。
我們一般寫的都是該介面的子介面實現類:MethodInterceptor
依賴:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
案例:
Producer.java
/**
* 一個生產者
*/
public class Producer {
/**
* 銷售
* @param money
*/
public void saleProduct(float money){
System.out.println("銷售產品,並拿到錢:"+money);
}
/**
* 售後
* @param money
*/
public void afterService(float money){
System.out.println("提供售後服務,並拿到錢:"+money);
}
}
測試類:
Client.java
/**
* 模擬一個消費者
*/
public class Client {
public static void main(String[] args) {
final Producer producer = new Producer();
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 執行被代理物件的任何方法都會經過該方法
* @param proxy
* @param method
* @param args
* 以上三個引數和基於介面的動態代理中invoke方法的引數是一樣的
* @param methodProxy :當前執行方法的代理物件
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增強的程式碼
Object returnValue = null;
//1.獲取方法執行的引數
Float money = (Float)args[0];
//2.判斷當前方法是不是銷售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(10000f);
}
}
//銷售產品,並拿到錢:8000.0