使用Java反射,您可以在運(yùn)行時(shí)創(chuàng)建接口的動(dòng)態(tài)實(shí)現(xiàn)。 你可以使用類java.lang.reflect.Proxy。 這個(gè)類的名字是我將這些動(dòng)態(tài)接口實(shí)現(xiàn)稱為動(dòng)態(tài)代理的原因。 動(dòng)態(tài)代理可以用于許多不同的目的,例如, 數(shù)據(jù)庫(kù)連接和事務(wù)管理,用于單元測(cè)試的動(dòng)態(tài)模擬對(duì)象,以及其他類似于AOP的方法攔截目的。
創(chuàng)建代理
您使用Proxy.newProxyInstance()方法創(chuàng)建動(dòng)態(tài)代理。 newProxyInstance()方法需要3個(gè)參數(shù):
- 用于“加載”動(dòng)態(tài)代理類的ClassLoader。
- 要實(shí)現(xiàn)的接口數(shù)組。
- 一個(gè)InvocationHandler將代理上的所有方法調(diào)用轉(zhuǎn)發(fā)。
這里是一個(gè)例子:
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[] { MyInterface.class },
handler);
運(yùn)行此代碼后,代理變量包含MyInterface接口的動(dòng)態(tài)實(shí)現(xiàn)。 所有對(duì)代理的調(diào)用都將被轉(zhuǎn)發(fā)給一般的InvocationHandler接口的處理程序?qū)崿F(xiàn)。 InvocationHandler在下一節(jié)中介紹。
InvocationHandler
如前所述,您必須將InvocationHandler實(shí)現(xiàn)傳遞給Proxy.newProxyInstance()方法。 所有對(duì)動(dòng)態(tài)代理的方法調(diào)用都被轉(zhuǎn)發(fā)給這個(gè)InvocationHandler實(shí)現(xiàn)。 以下是InvocationHandler接口的外觀:
public interface InvocationHandler{
Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
下面是一個(gè)示例實(shí)現(xiàn):
public class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//do something "dynamic"
}
}
傳遞給invoke()方法的proxy參數(shù)是實(shí)現(xiàn)接口的動(dòng)態(tài)代理對(duì)象。 大多數(shù)情況下你不需要這個(gè)對(duì)象。
傳遞給invoke()方法的Method對(duì)象表示在動(dòng)態(tài)代理實(shí)現(xiàn)的接口上調(diào)用的方法。 從Method對(duì)象中,您可以獲取方法名稱,參數(shù)類型,返回類型等。有關(guān)更多信息,請(qǐng)參閱Methods上的文本。
Object [] args數(shù)組包含調(diào)用接口中的方法時(shí)傳遞給代理的參數(shù)值。 注意:被實(shí)現(xiàn)的接口中的基本元素(int,long等)被封裝在它們的對(duì)象(Integer,Long等)中。
數(shù)據(jù)庫(kù)連接和事務(wù)管理
Spring框架有一個(gè)事務(wù)代理,可以為你啟動(dòng)和提交/回滾事務(wù)。 在高級(jí)連接和事務(wù)劃分和傳播的文本中更詳細(xì)地描述了這個(gè)工作原理,所以我只簡(jiǎn)要描述它。 通話順序變成這
web controller --> proxy.execute(...);
proxy --> connection.setAutoCommit(false);
proxy --> realAction.execute();
realAction does database work
proxy --> connection.commit()
DI容器
依賴注入容器有一個(gè)強(qiáng)大的功能,允許你將整個(gè)容器注入到它生成的bean中。 但是,由于不需要對(duì)容器接口進(jìn)行依賴,因此容器可以自行適應(yīng)設(shè)計(jì)的自定義工廠接口。 你只需要接口。 沒(méi)有執(zhí)行。 因此,工廠界面和你的類可以看起來(lái)像這樣:
public interface IMyFactory {
Bean bean1();
Person person();
...
}
public class MyAction{
protected IMyFactory myFactory= null;
public MyAction(IMyFactory factory){
this.myFactory = factory;
}
public void execute(){
Bean bean = this.myFactory.bean();
Person person = this.myFactory.person();
}
}
當(dāng)MyAction類調(diào)用由容器注入其構(gòu)造函數(shù)的IMyFactory實(shí)例上的方法時(shí),方法調(diào)用將被轉(zhuǎn)換為對(duì)IContainer.instance()方法的調(diào)用,IContainer.instance()方法是用于從容器中獲取實(shí)例的方法。 這樣一來(lái),一個(gè)對(duì)象可以在運(yùn)行時(shí)使用Butterfly Container作為工廠,而不僅僅是在創(chuàng)建時(shí)注入自己的依賴。 而這個(gè)沒(méi)有任何容器特定接口的依賴。
類AOP方法攔截
Spring框架可以攔截方法調(diào)用給一個(gè)給定的bean,前提是bean實(shí)現(xiàn)了一些接口。 Spring框架將這個(gè)bean包裝在一個(gè)動(dòng)態(tài)代理中。 所有對(duì)bean的調(diào)用都會(huì)被代理攔截。 代理可以決定在其他對(duì)象上調(diào)用其他對(duì)象的方法,而不是或者在方法調(diào)用委托給包裝的bean之后。
AOP動(dòng)態(tài)代理底層實(shí)戰(zhàn)
package com.reflection.detail;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* Created by Fant.J.
* 2018/2/7 17:01
*/
public class Reflection_DynamicProx {
public static void main(String[] args) {
CommonDao commonDao = new CommonDaoImpl();
InvocationHandler handler = new MyInvocationHandler(commonDao);
CommonDao proxy = (CommonDao) Proxy.newProxyInstance(
commonDao.getClass().getClassLoader(),
commonDao.getClass().getInterfaces(),
handler);
proxy.insert(2,3);
proxy.insert(3,3);
proxy.delete(5,2);
}
/**
* 重寫(xiě)InvocationHandler
* 將代理上的所有方法調(diào)用轉(zhuǎn)發(fā)
* 相當(dāng)于 Method一章節(jié)中,我們講到的反射實(shí)例化
* aClass.getMethod("setName", String.class);
* 然后調(diào)用ethod.invoke(people,"Fant.J");
*/
static final class MyInvocationHandler implements InvocationHandler{
//這個(gè)就是我們要代理的真實(shí)對(duì)象
private Object subject;
//構(gòu)造方法,需要把我們要代理的真實(shí)對(duì)象 傳進(jìn)來(lái) 供給invoke方法使用
public MyInvocationHandler(Object subject)
{
this.subject = subject;
}
/**
* 重寫(xiě)invoke方法
* @param proxy 代理對(duì)象
* @param method 代理方法
* @param args 代理方法傳入的參數(shù)
* @return 返回反射結(jié)果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//do something "dynamic"
System.out.println("方法【" + method.getName() + "】開(kāi)始執(zhí)行, 參數(shù)為" + Arrays.asList(args) + "...");
long start = System.currentTimeMillis();
Object result = method.invoke(subject, args);
long end = System.currentTimeMillis();
System.out.println("方法【" + method.getName() + "】執(zhí)行完成,運(yùn)算結(jié)果為:" + result + ", 用時(shí)" + (end - start) + "毫秒!");
return result;
}
}
static interface CommonDao{
public int insert(int a,int b);
public int delete(int a,int b);
}
static class CommonDaoImpl implements CommonDao{
@Override
public int insert(int a, int b) {
return a+b;
}
@Override
public int delete(int a, int b) {
return a-b;
}
}
}
方法【insert】開(kāi)始執(zhí)行, 參數(shù)為[2, 3]...
方法【insert】執(zhí)行完成,運(yùn)算結(jié)果為:5, 用時(shí)0毫秒!
方法【insert】開(kāi)始執(zhí)行, 參數(shù)為[3, 3]...
方法【insert】執(zhí)行完成,運(yùn)算結(jié)果為:6, 用時(shí)0毫秒!
方法【delete】開(kāi)始執(zhí)行, 參數(shù)為[5, 2]...
方法【delete】執(zhí)行完成,運(yùn)算結(jié)果為:3, 用時(shí)0毫秒!
有什么不懂的可以去先看下Java反射前面的東西。Java反射文集
.也可以在文章下方留言。DI容器相繼推出。
項(xiàng)目代碼:github鏈接