反射是指在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意方法和屬性;這種動(dòng)態(tài)獲取信息以及動(dòng)態(tài)調(diào)用對(duì)象方法的功能稱為java語(yǔ)言的反射機(jī)制。Java中有兩種方式實(shí)現(xiàn)反射,接下來我們來一一分析一下。
1.通過java體系自帶的反射機(jī)制,首先我們來看一個(gè)例子。
- 定義一個(gè)接口
public interface GoodService {
String sayHello(String name);
}
- 添加實(shí)現(xiàn)類
public class GoodServiceImpl implements GoodService {
@Override
public String sayHello(String name) {
return name + " 你好";
}
}
- 定義動(dòng)態(tài)反射的代理類
public class ControllerProxy implements InvocationHandler {
private GoodService goodService;
public ControllerProxy(GoodService goodService) {
this.goodService = goodService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object value = method.invoke(goodService,args);
System.out.println("after");
return value;
}
}
- 最后測(cè)試一下
public class GoodClient {
/**
*
* @param args
*/
public static void main(String[] args){
GoodService goodService = new GoodServiceImpl();
ControllerProxy proxy = new ControllerProxy(goodService);
GoodService gs = (GoodService) Proxy.newProxyInstance(proxy.getClass().getClassLoader(),goodService.getClass().getInterfaces(),proxy);
String dd = gs.sayHello("張三");
System.out.println(dd);
}
}
通過上面的列子可以發(fā)現(xiàn),如果我們需要對(duì)接口進(jìn)行擴(kuò)展則無(wú)須對(duì)源碼進(jìn)行修改,只需對(duì)代理類進(jìn)行擴(kuò)展即可達(dá)到目的,所以反射機(jī)制提供了一種對(duì)Java擴(kuò)展的方式,這種方式無(wú)侵入性。但是這種方式也不是十全十美,也有自己的瓶頸,首先被代理的只能是接口,這樣就限制了使用范圍,其次是性能問題,通過反射獲取對(duì)象的屬性的值要遠(yuǎn)比直接通過屬性取值,最后反射的應(yīng)用會(huì)模糊應(yīng)用內(nèi)實(shí)際要發(fā)生的事。
2.除了java自帶的反射機(jī)制外,另一種實(shí)現(xiàn)方式就是cglib,cglib完全避開了java自帶的反射機(jī)制帶來的問題,cglib不僅僅可以代理接口,同時(shí)可以給普通類進(jìn)行代理,同時(shí)它采用fastclass機(jī)制為代理類和被代理類各生成一個(gè)Class,這個(gè)Class會(huì)為代理類或被代理類的方法分配一個(gè)index(int類型)。
這個(gè)index當(dāng)做一個(gè)入?yún)?,F(xiàn)astClass就可以直接定位要調(diào)用的方法直接進(jìn)行調(diào)用,這樣省去了反射調(diào)用,所以調(diào)用效率比JDK動(dòng)態(tài)代理通過反射調(diào)用高。接下來我們擼段代碼來看看
- 直接定義cglib代理類
public class MethodInterProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return obj;
}
}
- 實(shí)現(xiàn)類直接采用上面的實(shí)現(xiàn)類
- 運(yùn)行結(jié)果
public class GoodClient {
/**
*
* @param args
*/
public static void main(String[] args){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(GoodServiceImpl.class);
MethodInterProxy mproxy = new MethodInterProxy();
enhancer.setCallbacks(new Callback[]{NoOp.INSTANCE,mproxy});
enhancer.setCallbackFilter(new MethodFilter());
GoodService gsd = (GoodService) enhancer.create();
String ds = gsd.sayHello("DE");
System.out.println(ds);
}
}
通過上面的例子可以看到cglib直接對(duì)實(shí)現(xiàn)類進(jìn)行代理,cglib底層采用ASM進(jìn)行字節(jié)碼生成,這種方式遠(yuǎn)比jdk自帶的反射機(jī)制要復(fù)雜。
最后兩者進(jìn)行對(duì)比
1.JDK動(dòng)態(tài)代理是實(shí)現(xiàn)了被代理對(duì)象的接口,Cglib是繼承了被代理對(duì)象。
2.JDK和Cglib都是在運(yùn)行期生成字節(jié)碼,JDK是直接寫Class字節(jié)碼,Cglib使用ASM框架寫Class字節(jié)碼,Cglib代理實(shí)現(xiàn)更復(fù)雜,生成代理類比JDK效率低。
3.JDK調(diào)用代理方法,是通過反射機(jī)制調(diào)用,Cglib是通過FastClass機(jī)制直接調(diào)用方法,Cglib執(zhí)行效率更高。