Proxy Pattern(即:代理模式),23種常用的面向對象軟件的設計模式之一
代理模式的定義:爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
優點:
- 真實的角色就是實現實際的業務邏輯,不用關心其他非本職責的事務,通過後期的代理完成一件完成事務,附帶的結果就是編程簡潔清晰。
- 代理對象可以在客戶端和目標對象之間起到中介的作用,這樣起到了的作用和保護了目標對象的作用。
- 高擴展性
一個是真正的你要訪問的對象(目標類),另一個是代理對象,真正對象與代理
對象實現同一個接口,先訪問代理類再訪問真正要訪問的對象。
代理模式的要求:
- 代理這和被代理這實現相同的接口
- 代理者必須有被代理這的實例(引用)
- 代理這調用被代理這的方法
靜態代理:簡單代理
代理者和被代理這需要是實現共同的接口,即我們的裝飾模式
創建一個潘金蓮對象,實現Kindweman接口,同時創建一個代理者王婆也實現這個接口
public class PJL implements KindWeman{
@Override
public void ShowEyes(Double money) {
System.out.println("PJL 拿到"+money+"元錢,showeyes");
}
@Override
public void DoSe(Double money) {
System.out.println("PJL 拿到"+money+"元錢,Dose");
}
}
public class WP implements KindWeman {
private KindWeman kw = null;
public WP(KindWeman kw ){
this.kw = kw;
}
@Override
public void ShowEyes(Double money) {
kw.ShowEyes(money);
}
@Override
public void DoSe(Double money) {
kw.DoSe(money);
}
}
潘金蓮需要王婆的代理,代理者調用的方法都是被代理者的
public static void main(String[] args){
KindWeman KW = new PJL();
WP p = new WP(KW);
p.ShowEyes(3.0);
p.DoSe(6.0);
}
動態代理
動態代理它可以直接給某一個目標對象生成一個代理對象,而不需要代理類存在。
動態代理與代理模式原理是一樣的,只是它沒有具體的代理類,直接通過反射生成了一個代理對象。
動態代理生成技術:
1、jdk提供一個Proxy類可以直接給實現接口類的對象直接生成代理對象。
2、cglib (spring框架)
Java.lang.reflect.Proxy類可以直接生成一個代理對象,看例子,這裏不需要王婆這個代理者,jdk給我們提供了。
@Test
public void test(){
KindWeman kw = new PJL();
//創建一個代理對象
//ClassLoader:類加載器。固定寫法,和被代理類使用相同的類加載器即可。kw.getClass().getClassLoader()
//Class[] interface:代理類要實現的接口。固定寫法,和被代理類使用相同的接口即可。kw.getClass().getInterfaces()
//InvocationHandler:策略(方案)設計模式的應用。如何代理?即代理的過程,你需要的操作
KindWeman KWproxy = (KindWeman)
Proxy.newProxyInstance(kw.getClass().getClassLoader(), kw.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//nvocationHandler中的invoke方法:調用代理類的任何方法,此方法都會執行
//Object proxy:代理對象本身的引用。一般用不着。
//Method method:當前調用的方法。通過反射, KWproxy調用誰,這裏的Method就是誰
//Object[] args:當前方法用到的參數
System.out.println("我是中間代理:需要收取費用");
Object o = method.invoke(kw, new Object[]{(Double) args[0] / 2});
System.out.println("事務完成,下次再來!");
return o;
}
});
KWproxy.ShowEyes(6.0);
KWproxy.ShowEyes(3.0);
}
使用這個思想,在我們前面講到的業務層調用ThreadLocal管理,就不必要在servce層面上編寫了。編寫一個類來動態的獲取服務,然後在代理當中添加ThreadLocal類實現事務的功能
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.itheima.service.AccountService;
import com.itheima.service.impl.AccountServiceImpl;
public class ObjectFactory {
//方法返回一個代理對象
public static AccountService getAccountService(){
final AccountService as= new AccountServiceImpl();
AccountService proxy = (AccountService) Proxy.newProxyInstance(as.getClass().getClassLoader(), as.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object invoke = null;
try {
ManagerThreadLocal.startTransacation();//begin
//執行的是真實對象的轉賬方法
invoke = method.invoke(as, args);
ManagerThreadLocal.commit();//提交事務
} catch (Exception e) {
try {
ManagerThreadLocal.rollback();//回滾事務
} catch (Exception e1) {
e1.printStackTrace();
}
}finally{
try {
ManagerThreadLocal.close();
} catch (Exception e) {
e.printStackTrace();
}//關閉
}
return invoke;
}
});
return proxy;
}
}
public void transfer(String fromname, String toname, double money) throws Exception {
// ad.updateAccount(fromname, toname, money);
AccountDao ad = new AccountDaoImpl();
//分別得到轉出和轉入賬戶對象
Account fromAccount = ad.findAccountByName(fromname);
Account toAccount = ad.findAccountByName(toname);
//修改賬戶各自的金額
fromAccount.setMoney(fromAccount.getMoney()-money);
toAccount.setMoney(toAccount.getMoney()+money);
//完成轉賬操作
ad.updateAccout(fromAccount);
int i = 10/0;
ad.updateAccout(toAccount);
}