java中反射跟代理是有点不好理解,除非要自己真写过代码,要不然还真不知道怎么玩。其实说白了也就没啥神秘的,反射本质就运行时加载类编译后的class文件,然后根据java.lang.Class所提供的API进行操作,包括获取该类的包名、所实现的接口名、所继承的父类名,以及该类自己的类名、方法名、字段名、构造函数名,真正有用的是利用java.lang.reflect提供的API直接调用方法,修改字段值,用构造函数实例化对象,这就是反射为啥能动态的秘密。具体参见下面代码:
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); // 通过构造函数实例化对象 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()); // 构造方法名调用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()); // 私有字段需要通过设置Accessible为true才能重新设值 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,是否数组:%b,父类:%s ", clz.getName(), clz.isInterface(), clz.isPrimitive(), clz.isArray(), clz.getSuperclass() != null ? clz.getSuperclass().getName() : "父类不存在"); System.out.printf("包名:%s,修饰符:%s ", clz.getPackage() != null ? clz.getPackage().getName() : "包不存在", Modifier.toString(clz.getModifiers())); for (Constructor c : clz.getConstructors()) { System.out.printf("构造函数名:%s ", c.getName()); } System.out.println(); for (Field field : clz.getDeclaredFields()) { System.out.printf("字段名:%s ", field.getName()); } System.out.println(); for (Method m : clz.getMethods()) { System.out.printf("方法:%s ", 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);
}
}
运行结果:
类名:com.wulinfeng.io.Member,是否接口:false,是否基本类型:false,是否数组:false,父类:java.lang.Object
包名:com.wulinfeng.io,修饰符:public
构造函数名: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)
代理本身就是设计模式的一种,就是一种行为,可以有多种实现方式。从类的角度看就是一个接口中的方法,有多个不同的类来实现。代理从类角度分接口代理和类代理,接口代理jdk本身提供了对应的API实现,但必须要有接口的存在;类代理由cglib提供支持,可以没有接口。从实现角度分静态代理和动态代理,静态代理需要先定义好一个接口,多个实现类,其中一个实现类调用了另一个实现类的接口方法,在编译期就完成了代理,这里其实就是装饰模式的一种实现;动态代理用到了上面提及的反射,在运行时生成对象实例,并通过实现java.lang.reflect.InvocationHandler的invoke方法完成代理。具体实现参考下面:
静态代理:
package com.wulinfeng.io;
public interface Log {
void warn(String name);
}
package com.wulinfeng.io;
public class HelloLog implements Log {
@Override
public void warn(String name) {
System.out.printf("hello %s, you have be traced.
", 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("方法结束****");
}
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");
}
}
运行结果:
三月 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
警告: 方法结束****
动态代理:
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;
// 生成目标类
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");
}
}
运行结果:
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
这里从结果看日志打印并不符合我们预期,我们预期日志打印位置应该跟静态代理运行结果一致才对。从标准输出来看代理是没问题的,所以问题出现在日志打印滞后了,这跟虚拟机执行日志打印到控制台的速度有关,因为加载并打印log的速度不及标准输出,所以产生了延时,再跑一次就好可以看到日志打印的位置正常了。