華碩將於 5 月 11 日推出新款 ROG Zephyrus
1.代理模式介紹
代理模式就是:提供了對目標物件額外的訪問方式,即通過代理物件訪問目標物件,這樣可以在不修改原目標物件的前提下,提供額外的功能操作,擴充套件目標物件的功能。
通俗的來講就是中介/代售點。舉個栗子:火車票如果都要在火車站買就會特別擁擠,於是產生了代售點,分散售賣就會很方便,代售點就是代理了火車站物件。
代理模式的作用:
- 一個客戶類不想或者不能直接引用一個委託物件,代理類物件可以在客戶類和委託物件之間起到中介的作用,其特徵是代理類和委託類實現相同的介面。
- 通過給代理類增加額外的功能來擴充套件委託類的功能,這樣做我們只需要修改代理類而不需要再修改委託類。舉個例子:1.提供一組方法給普通使用者,特別方法給管理員使用者 2.你想訪問facebook或者twitter ,需要通過代理點進行訪問。
按照代理建立的時期來進行分類的:
- 靜態代理:是由程式設計師建立或特定工具自動生成原始碼,在對其編譯,執行之前,代理類.class檔案就已經被建立了
- 動態代理:是在程式執行時通過反射機制動態建立的。
2. 靜態代理
需要代理物件和目標物件實現一樣的介面。
優點:
- 可以在不修改目標物件的前提下擴充套件目標物件的功能。
缺點:
- 冗餘。由於代理物件要實現與目標物件一致的介面,會產生過多的代理類。
- 不易維護。一旦介面增加方法,目標物件與代理物件都要進行修改。
例項:儲存使用者功能的靜態代理實現
介面類: IUserDao
package com.proxy;
public interface IUserDao {
public void save();
}
目標物件:UserDao
package com.proxy;
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("儲存資料");
}
}
靜態代理物件:UserDapProxy 需要實現IUserDao介面!
package com.proxy;
public class UserDaoProxy implements IUserDao {
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
//建構函式
@Override
public void save() {
System.out.println("開啟事務"); //擴充套件了額外功能
target.save();
System.out.println("提交事務");
}
}
測試類:TestProxy
package com.proxy;
import org.junit.Test;
public class StaticUserProxy {
@Test
public void testStaticProxy(){
//目標物件
IUserDao target = new UserDao();
//代理物件
UserDaoProxy proxy = new UserDaoProxy(target);
proxy.save();
}
}
輸出結果:
開啟事務
儲存資料
提交事務
3. 動態代理
動態代理利用了JDK API,動態地在記憶體中構建代理物件,從而實現對目標物件的代理功能。動態代理又被稱為JDK代理或介面代理。
與靜態代理的區別:
- 靜態代理在編譯時就已經實現,編譯完成後代理類是一個實際的class檔案
- 動態代理是在執行時動態生成的,即編譯完成後沒有實際的class檔案,而是在執行時動態生成類位元組碼,並載入到JVM中
- 通過反射代理方法,比較消耗系統性能,但可以減少代理類的數量,使用更靈活
特點:
- 動態代理物件不需要實現介面,但是要求目標物件必須實現介面,否則不能使用動態代理
JDK中生成代理物件主要涉及的類有:
(1)java.lang.reflect Proxy,主要方法為:
static Object newProxyInstance(ClassLoader loader, //指定當前目標物件使用類載入器
Class<?>[] interfaces, //目標物件實現的介面的型別
InvocationHandler h //事件處理器
)
//返回一個指定介面的代理類例項,該介面可以將方法呼叫指派到指定的呼叫處理程式。
(2)java.lang.reflect InvocationHandler,主要方法為
Object invoke(Object proxy, Method method, Object[] args)
// 在代理例項上處理方法呼叫並返回結果。
舉例:儲存使用者功能的動態代理實現
介面類: IUserDao
package com.proxy;
public interface IUserDao {
public void save();
}
目標物件:UserDao
package com.proxy;
public class UserDao implements IUserDao{
@Override
public void save() {
System.out.println("儲存資料");
}
}
動態代理物件:UserProxyFactory
package com.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyFactory {
private Object target;// 維護一個目標物件
public ProxyFactory(Object target) { //建構函式
this.target = target;
}
// 為目標物件生成代理物件
public Object getProxyInstance() {
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("開啟事務");
// 執行目標物件方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務");
return null;
}
});
}
}
測試類:TestProxy
package com.proxy;
import org.junit.Test;
public class TestProxy {
@Test
public void testDynamicProxy (){
IUserDao target = new UserDao();
System.out.println(target.getClass()); //輸出目標物件資訊
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass()); //輸出代理物件資訊
proxy.save(); //執行代理方法
}
}
輸出結果:
class com.proxy.UserDao
class com.sun.proxy.$Proxy4
開啟事務
儲存資料
提交事務
4. cglib代理
cglib (Code Generation Library )是一個第三方程式碼生成類庫,執行時在記憶體中動態生成一個子類物件從而實現對目標物件功能的擴充套件。
使用cglib代理的目標物件可以不實現介面,通過生成類位元組碼實現代理,比反射稍快,不存在效能問題,但cglib會繼承目標物件,需要重寫方法,所以目標物件不能為final類。
特點:
- 如果想代理沒有實現介面的類,可以使用cglib實現。JDK的動態代理的目標物件必須實現介面
- cglib是一個強大的高效能的程式碼生成包,它可以在執行期擴充套件Java類與實現Java介面。廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
- cglib底層是通過使用一個小而快的位元組碼處理框架ASM,來轉換位元組碼並生成新的類。
使用cglib需要引入cglib的jar包,如果你已經有spring-core的jar包,則無需引入,因為spring中包含了cglib。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
舉例:儲存使用者功能的動態代理實現
目標物件:UserDao
package com.cglib;
public class UserDao{
public void save() {
System.out.println("儲存資料");
}
}
代理物件:ProxyFactory
package com.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class ProxyFactory implements MethodInterceptor{
private Object target;//維護一個目標物件
public ProxyFactory(Object target) {
this.target = target;
}
//為目標物件生成代理物件
public Object getProxyInstance() {
//工具類
Enhancer en = new Enhancer();
//設定父類
en.setSuperclass(target.getClass());
//設定回撥函式
en.setCallback(this);
//建立子類物件代理
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開啟事務");
// 執行目標物件的方法
Object returnValue = method.invoke(target, args);
System.out.println("關閉事務");
return null;
}
}
測試類:TestProxy
package com.cglib;
import org.junit.Test;
public class TestProxy {
@Test
public void testCglibProxy(){
//目標物件
UserDao target = new UserDao();
System.out.println(target.getClass());
//代理物件
UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
//執行代理物件方法
proxy.save();
}
}
輸出結果
class com.cglib.UserDao
class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6
開啟事務
儲存資料
關閉事務