抽象主題角色:聲明真實主題和代理主題的共同接口。程序員
代理主題角色:代理主題內部含有對真實主題的引用,從而在任什麼時候候操做真實主題對象;代理主題提供一個與真實主題相同的接口,以便在任什麼時候候均可以代替真實主題。代理角色一般在將客戶端調用傳遞給真實的主題以前或以後,都要執行某個操做,而不是單純的傳遞調用。面試
真實主題角色:定義代理角色所表明的的真實對象。mybatis
UML圖:ide
抽象主題post
public interface Subject { void request(); }
真實主題this
public class RealSubject implements Subject { @Override public void request() { System.out.println("真實對象的方法"); } }
代理主題spa
public class ProxySubject implements Subject { private RealSubject subject; public ProxySubject() { } @Override public void request() { pre(); if (subject == null){ subject = new RealSubject(); } subject.request(); post(); } private void pre(){ System.out.println("方法執行以前"); } private void post(){ System.out.println("方法執行以後"); } }
執行:設計
public static void main(String[] args) throws Exception { ProxySubject subject = new ProxySubject(); subject.request(); }
輸出:代理
方法執行以前
真實對象的方法
方法執行以後
JDK自帶的動態代理,實現InvocationHandler接口。code
聲明接口
public interface MyConnection extends AutoCloseable { void createStatement() throws Exception; @Override void close() throws Exception; }
真實主題
public class MyDefaultConnection implements MyConnection { @Override public void createStatement() throws Exception { System.out.println("Create Statement ..."); } @Override public void close() throws Exception { System.out.println("Close Connection ..."); } }
代理主題
public class MyConnectionProxy implements InvocationHandler { private MyConnection conn; private MyConnection proxyConn; public MyConnectionProxy(MyConnection conn) { this.conn = conn; this.proxyConn = (MyConnection) Proxy.newProxyInstance(MyConnection.class.getClassLoader(), new Class<?>[] {MyConnection.class}, this); } public MyConnection getConn() { return conn; } public MyConnection getProxyConn() { return proxyConn; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); System.out.println("==代理方法:" + methodName); if("close".equals(methodName)){ System.out.println("**不執行close方法"); }else{ return method.invoke(conn, args); } return null; } }
執行:
public static void main(String[] args) throws Exception { MyConnection connection = new MyDefaultConnection(); MyConnectionProxy proxy = new MyConnectionProxy(connection); proxy.getProxyConn().createStatement(); proxy.getProxyConn().close(); }
你會發現個人代理對象去哪裏了?實際上我放在InvocationHandler的實現類裏面了,這裏參考的是mybatis源碼的設計。
輸出:
==代理方法:createStatement Create Statement ... ==代理方法:close **不執行close方法
面試的時候可能問你:JDK動態代理和CGLib的區別是什麼,說的最多的就是JDK須要一個接口,而CHLib不須要接口就能實現動態代理。
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency>
須要代理的對象
public class Programmer { public void work(){ System.out.println("程序員正在敲代碼..."); } public final void finalCannotOverride(){ System.out.println("final方法不能被生成的子類覆蓋"); } private void privateCannotOverride(){ System.out.println("private方法不能被生成的子類覆蓋"); } }
代理類
public class ProgrammerProxy implements MethodInterceptor { // 真實對象 private Object realObject; // 代理對象 private Object proxyObject; public ProgrammerProxy(Object realObject) { this.realObject = realObject; Enhancer enhancer = new Enhancer(); // 設置須要代理的對象 enhancer.setSuperclass(realObject.getClass()); // 設置代理人 enhancer.setCallback(this); this.proxyObject = enhancer.create(); } public Programmer getProxyObject() { return (Programmer) proxyObject; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { pre(); Object result = method.invoke(realObject, objects); post(); return result; } private void pre(){ System.out.println("==先吃早餐"); } private void post(){ System.out.println("==下班打卡"); } }
執行:
public static void main(String[] args) throws Exception { Programmer programmer = new Programmer(); ProgrammerProxy proxy = new ProgrammerProxy(programmer); proxy.getProxyObject().finalCannotOverride(); proxy.getProxyObject().work(); }
輸出:
final方法不能被生成的子類覆蓋 ==先吃早餐 程序員正在敲代碼... ==下班打卡