傳送門java
//面試
public interface InterView {
void chatting();
}
複製代碼
public class Persion implements InterView{
@Override
public void chatting() {
System.out.println("is chatting ...");
try {
//模擬面試時間
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
複製代碼
public class Persion implements InterView{
@Override
public void chatting() {
Long start = System.currentTimeMillis();
System.out.println("is chatting ...");
try {
//模擬面試時間
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
Long end = System.currentTimeMillis();
System.out.println("chatting time: " + (end - start) );
}
}
複製代碼
若是這個方法來自三方庫沒有改動源碼的權限,怎麼辦
public class Persion2 extends Persion {
@Override
public void chatting() {
long start = System.currentTimeMillis();
super.chatting();
long end = System.currentTimeMillis();
System.out.println("chatting time : " + (end - start));
}
}
複製代碼
public class Person3 implements InterView {
private Person person;
public Person3(Person person){
this.person = person;
}
@Override
public void chatting() {
long start = System.currentTimeMillis();
person.chatting();
long end = System.currentTimeMillis();
System.out.println("chatting time : " + (end - start));
}
}
複製代碼
咱們繼續增長需求,若是我還要在面試先後作記錄(打印日誌)呢,很簡單,繼承person2,並在先後添加打印日誌便可
若是我要改變執行順序呢,先獲取時間,再打印日誌呢,能夠再用一個繼承類,重寫方法,這樣會致使無限擴增
public class Person3 implements InterView {
private InterView interView;
public Person3(InterView interView){
this.interView = interView;
}
@Override
public void chatting() {
long start = System.currentTimeMillis();
interView.chatting();
long end = System.currentTimeMillis();
System.out.println("chatting time : " + (end - start));
}
}
複製代碼
public class PersonLogProxy implements Interview {
private Interview interview;
public PersonLogProxy(Interview interview) {
this.interview = interview;
}
@Override
public void chatting() {
System.out.println("chatting start...");
interview.chatting();
System.out.println("chatting end...");
}
}
複製代碼
public class Test {
public static void main(String[] args) {
Person person = new Person();
PersonLogProxy p1 = new PersonLogProxy(person);
PersonProxy p2 = new PersonProxy(p1);
p2.chatting();
}
}
複製代碼
public class Test {
public static void main(String[] args) {
PersonProxy p3 = new PersonProxy(person);
PersonLogProxy p4 = new PersonLogProxy(p3);
p4.chatting();
}
}
複製代碼
這裏會出現一個問題,從表現來看,聚合能夠實現靈活的執行順序,而繼承不能夠,爲何
面試
在上面的PersonTimeProxy的chatting方法,咱們直接調用了interview().chatting,換而言之,PersonProxy代理傳入的interview對象,這就是典型的靜態代理實現
設計模式
其實就是經過jdk自帶的Proxy.newProxyInstance()方法動態生成代理類,動態編譯,在經過反射建立對象並加載到內存中bash
能夠查看一下Proxy->newProxyInstance()的源碼,代理類的動態建立經過InvocationHandler自定義dom
咱們能夠看一下源碼中的InvocationHandler接口
jvm
import java.lang.reflect.Method;
/**
* proxy:指定動態生成的代理類
* method:接口中傳入的全部方法對象
* args:當前傳入方法的參數
*/
public interface InvocationHandler {
void invoke(Object proxy, Method method,Object[] args);
}
複製代碼
使用:ide
public class MyInvocationHandler implements InvocationHandler {
//被代理對象,object類型
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
/**
*proxy:指定動態生成的代理類,
*method:接口中傳入的對象的全部方法
*當前傳入方法的參數
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("chatting start ...");
Object returnValue = method.invoke(target, args);
System.out.println("chatting end");
return returnValue;
}
}
複製代碼
public class DynamicProxyTest {
public static void main(String[] args) {
Person person = new Person();
MyInvocationHandler handler = new MyInvocationHandler(person);
/**
* 第一個參數指定代理類的類加載器,這裏傳入當前測試類加載器
* 第二個參數是代理類須要實現的接口的實例類
* 第三個參數是invocation handler,用來處理方法的調用,這裏傳入本身實現的handler
*/
InterView proxyObject = (InterView) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader()
, person.getClass().getInterfaces(),
handler);
proxyObject.chatting();
}
}
複製代碼
此時,整個方法的調用棧變成了這樣 測試
簡而言之就是:ui
真實的業務類須要實現業務接口,代理類經過反射獲取真實業務對象的類加載器,從而在內存中建立一個真實的代理對象(靜態代理手動擴展那個),代理類須要實現invocationhandler接口,重寫invoke方法,invoke方法三個參數,分別是代理的接口,代理的方法,和方法參數,在該方法裏對真實的方法進行包裝,實現動態代理
和靜態代理的區別:靜態代理是本身去建立代理類對象,動態代理本質是在內存中根據傳入的參數,動態的生成一個代理類對象,經過生成的class文件反編譯能夠看到this
cglib是針對類來實現代理的,原理是針對指定的業務類生成一個子類,並覆蓋其中的業務方法,實現代理,由於用的是繼承,因此不能代理final修飾的類
public class PersonCglib implements MethodInterceptor {
//業務對象
private Object target;
public Object getInstance(Object target){
this.target = target;
//建立加強器
Enhancer enhancer = new Enhancer();
//指定要代理的類(指定生成子類的父類)
enhancer.setSuperclass(this.target.getClass());
//設置回調,對於代理類上全部方法的調用,都會調用callback,因此callback則須要實現intercept()方法進行攔截
enhancer.setCallback(this);
//包含三個步驟
//生成源代碼
//編譯成class文件
//加載到jvm中,建立實例並返回
return enhancer.create();
}
//實現方法回調
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("chatting start");
//這個obj對象是cglib給咱們new出來的
//cglib new出來的對象是被代理對象的子類(繼承了被代理類),繼承至關於間接持有父類的引用
methodProxy.invokeSuper(o,objects);
System.out.println("chatting end");
return null;
}
}
複製代碼
public static void main(String[] args) {
Person person = new Person();
PersonCglib cglib = new PersonCglib();
InterView interView = (InterView) cglib.getInstance(person);
interView.chatting();
}
複製代碼