反射与代理
java中反射跟代理是有點(diǎn)不好理解,除非要自己真寫過代碼,要不然還真不知道怎么玩。其實(shí)說白了也就沒啥神秘的,反射本質(zhì)就運(yùn)行時(shí)加載類編譯后的class文件,然后根據(jù)java.lang.Class所提供的API進(jìn)行操作,包括獲取該類的包名、所實(shí)現(xiàn)的接口名、所繼承的父類名,以及該類自己的類名、方法名、字段名、構(gòu)造函數(shù)名,真正有用的是利用java.lang.reflect提供的API直接調(diào)用方法,修改字段值,用構(gòu)造函數(shù)實(shí)例化對(duì)象,這就是反射為啥能動(dòng)態(tài)的秘密。具體參見下面代碼:
package com.wulinfeng.io;import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry;public class Reflection {public static void main(String[] args) throws Exception {Class clz = Member.class;printClassInfo(Member.class);// 通過構(gòu)造函數(shù)實(shí)例化對(duì)象Constructor c = clz.getConstructor(String.class, String.class, int.class, String.class);Member m = (Member) c.newInstance("9527", "liangchaowei", 53, "18912346987");System.out.println(m.toString());// 構(gòu)造方法名調(diào)用java.lang.reflect.Method的invoke方法重置字段值Map<String, Object> hashMap = new HashMap<>();hashMap.put("number", "0001");hashMap.put("name", "wulinfeng");hashMap.put("age", 33);hashMap.put("phone", "13812345678");for (Entry<String, Object> entry : hashMap.entrySet()) {String key = entry.getKey();Method method = clz.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1),entry.getValue().getClass());if (Modifier.isPublic(method.getModifiers())) {method.invoke(m, entry.getValue());}}System.out.println(m.toString());// 私有字段需要通過設(shè)置Accessible為true才能重新設(shè)值Field name = clz.getDeclaredField("name");name.setAccessible(true);name.set(m, "zhangtianyou");System.out.println(m.toString());}private static void printClassInfo(Class clz) {System.out.printf("類名:%s,是否接口:%b,是否基本類型:%b,是否數(shù)組:%b,父類:%s\n", clz.getName(), clz.isInterface(),clz.isPrimitive(), clz.isArray(),clz.getSuperclass() != null ? clz.getSuperclass().getName() : "父類不存在");System.out.printf("包名:%s,修飾符:%s\n", clz.getPackage() != null ? clz.getPackage().getName() : "包不存在",Modifier.toString(clz.getModifiers()));for (Constructor c : clz.getConstructors()) {System.out.printf("構(gòu)造函數(shù)名:%s\t", c.getName());}System.out.println();for (Field field : clz.getDeclaredFields()) {System.out.printf("字段名:%s\t", field.getName());}System.out.println();for (Method m : clz.getMethods()) {System.out.printf("方法:%s\t", m.getName());}System.out.println();} }?
package com.wulinfeng.io;import java.io.Serializable; public class Member implements Serializable {static {System.out.println("I'm wumanshu!");}private String number; private String name; private Integer age; private String phone; public Member(String number, String name, int age, String phone) { this.number = number; this.name = name; this.age = age; this.phone = phone; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public String toString() { return String.format("(%s,%s,%d,%s)", number, name, age, phone); } }運(yùn)行結(jié)果:
類名:com.wulinfeng.io.Member,是否接口:false,是否基本類型:false,是否數(shù)組:false,父類:java.lang.Object 包名:com.wulinfeng.io,修飾符:public 構(gòu)造函數(shù)名:com.wulinfeng.io.Member 字段名:number 字段名:name 字段名:age 字段名:phone 方法:getNumber 方法:toString 方法:getName 方法:setName 方法:getAge 方法:setNumber 方法:setAge 方法:getPhone 方法:setPhone 方法:wait 方法:wait 方法:wait 方法:equals 方法:hashCode 方法:getClass 方法:notify 方法:notifyAll I'm wumanshu! (9527,liangchaowei,53,18912346987) (0001,wulinfeng,33,13812345678) (0001,zhangtianyou,33,13812345678)代理本身就是設(shè)計(jì)模式的一種,就是一種行為,可以有多種實(shí)現(xiàn)方式。從類的角度看就是一個(gè)接口中的方法,有多個(gè)不同的類來實(shí)現(xiàn)。代理從類角度分接口代理和類代理,接口代理jdk本身提供了對(duì)應(yīng)的API實(shí)現(xiàn),但必須要有接口的存在;類代理由cglib提供支持,可以沒有接口。從實(shí)現(xiàn)角度分靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理需要先定義好一個(gè)接口,多個(gè)實(shí)現(xiàn)類,其中一個(gè)實(shí)現(xiàn)類調(diào)用了另一個(gè)實(shí)現(xiàn)類的接口方法,在編譯期就完成了代理,這里其實(shí)就是裝飾模式的一種實(shí)現(xiàn);動(dòng)態(tài)代理用到了上面提及的反射,在運(yùn)行時(shí)生成對(duì)象實(shí)例,并通過實(shí)現(xiàn)java.lang.reflect.InvocationHandler的invoke方法完成代理。具體實(shí)現(xiàn)參考下面:
? ?靜態(tài)代理:
package com.wulinfeng.io;public interface Log {void warn(String name);} package com.wulinfeng.io;public class HelloLog implements Log {@Overridepublic void warn(String name) {System.out.printf("hello %s, you have be traced.\n", name);}} package com.wulinfeng.io;import java.util.logging.Level; import java.util.logging.Logger;public class HelloLogProxy implements Log {private HelloLog helloLog;public HelloLogProxy(HelloLog helloLog) { this.helloLog = helloLog; } public void warn(String name) { log("方法開始****"); helloLog.warn(name); log("方法結(jié)束****"); } private void log(String msg) { Logger.getLogger(HelloLog.class.getName()).log(Level.WARNING, msg); } public static void main(String[] args) {; Log log = new HelloLogProxy(new HelloLog()); log.warn("wumanshu"); } }運(yùn)行結(jié)果:
三月 20, 2017 12:58:51 上午 com.wulinfeng.io.HelloLogProxy log 警告: 方法開始**** hello wumanshu, you have be traced. 三月 20, 2017 12:58:51 上午 com.wulinfeng.io.HelloLogProxy log 警告: 方法結(jié)束****動(dòng)態(tài)代理:
package com.wulinfeng.io;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.logging.Level; import java.util.logging.Logger; public class LogHander implements InvocationHandler { private Object target; // 生成目標(biāo)類 public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log("begin log: " + method.getName()); System.out.println("begin print: " + method.getName()); Object result = method.invoke(target, args); log("finish log: " + method.getName()); System.out.println("finish print: " + method.getName()); return result; } /** * 日志打印 * * @param msg */ private void log(String msg) { Logger.getLogger(HelloLog.class.getName()).log(Level.WARNING, msg); } public static void main(String[] args) { LogHander lh = new LogHander(); Log log = (Log) lh.bind(new HelloLog()); log.warn("world"); } }運(yùn)行結(jié)果:
begin print: warn hello world, you have be traced. finish print: warn 三月 20, 2017 12:59:50 上午 com.wulinfeng.io.LogHander log 警告: begin log: warn 三月 20, 2017 12:59:50 上午 com.wulinfeng.io.LogHander log 警告: finish log: warn這里從結(jié)果看日志打印并不符合我們預(yù)期,我們預(yù)期日志打印位置應(yīng)該跟靜態(tài)代理運(yùn)行結(jié)果一致才對(duì)。從標(biāo)準(zhǔn)輸出來看代理是沒問題的,所以問題出現(xiàn)在日志打印滯后了,這跟虛擬機(jī)執(zhí)行日志打印到控制臺(tái)的速度有關(guān),因?yàn)榧虞d并打印log的速度不及標(biāo)準(zhǔn)輸出,所以產(chǎn)生了延時(shí),再跑一次就好可以看到日志打印的位置正常了。
轉(zhuǎn)載于:https://www.cnblogs.com/wuxun1997/p/6583244.html
總結(jié)
- 上一篇: 在Android Studio环境下使用
- 下一篇: @Html.Action()