demo
在本 demo 中我们将创建一个 door
接口,实现对接口中所有方法的代理。
door.java
public interface Door {
void enter();
void out();
}
HomeDoorImpl.java
public class HomeDoorImpl implements Door{
@Override
public void enter() {
System.out.println("enter the home door");
}
@Override
public void out() {
System.out.println("out the home door");
}
}
main
and proxy
public class Proxy implements InvocationHandler {
private Object target;
public Proxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("start proxy");
Object res = method.invoke(target, args);
System.out.println("end proxy");
return res;
}
public static void main(String[] args) {
Door door;
Proxy proxy = new Proxy(door = new HomeDoorImpl());
door = (Door) java.lang.reflect.Proxy.newProxyInstance(door.getClass().getClassLoader(),
door.getClass().getInterfaces(), proxy);
door.enter();
door.out();
}
}
运行
输出
start proxy
enter the home door
end proxy
start proxy
out the home door
end proxy
仅代理某个方法
可以看到我们将 Door
接口中的所有方法都代理,但在实际业务中可能仅代理某些方法。
接下来做一些小改动来支持这个功能。我们决定通过在方法上添加注解决定是否真正代理(PS:本质上实现类的所有方法都会被代理)
NeedProxy.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedProxy {}
HomeDoorImpl.java
为 enter
方法添加注解
public class HomeDoorImpl implements Door{
@NeedProxy
@Override
public void enter() {
System.out.println("enter the home door");
}
@Override
public void out() {
System.out.println("out the home door");
}
}
Proxy.java
使用 Map 集合保存了需要代理的方法,在 invoke function 时判断是否执行代理逻辑。
public class Proxy implements InvocationHandler {
private HashMap proxyMethods = new HashMap<>();
private Object target;
public Proxy(Object target) {
this.target = target;
Method[] methods = target.getClass().getMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(NeedProxy.class)) {
proxyMethods.put(method.getName(), Void.TYPE);
break;
}
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (proxyMethods.containsKey(method.getName())) {
return invoke0(proxy, method, args);
}
return method.invoke(target, args);
}
private Object invoke0(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
System.out.println("start proxy");
Object res = method.invoke(target, args);
System.out.println("end proxy");
return res;
}
public static void main(String[] args) {
Door door;
Proxy proxy = new Proxy(door = new HomeDoorImpl());
door = (Door) java.lang.reflect.Proxy.newProxyInstance(door.getClass().getClassLoader(),
door.getClass().getInterfaces(), proxy);
door.enter();
door.out();
}
}
输出
------start proxy-----
enter the home door
------end proxy-------
out the home door
实现一个 before-after
真实业务上不可能所有方法都是一样的代理逻辑(PS:logger 是可以使用同一个逻辑的)如何模拟实现一个类似真实业务的 before-after,我们决定在 NeedProxy
和 Proxy
上下工夫。
BeforeAfter.java
定义一个 before-after 接口,这里可以根据不同的业务逻辑实现,after
方法页可以包装以下 result
再吐出去。
public interface BeforeAfter{
void before(Object[] args);
void after(Object result);
}
NeedProxy.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NeedProxy {
Class extends BeforeAfter> beforeAfter();
}
HomeDoorImpl.java
public class HomeDoorImpl implements Door{
@NeedProxy(beforeAfter = HomeDoorEnterBeforeAfter.class)
@Override
public void enter() {
System.out.println("enter the home door");
}
@Override
public void out() {
System.out.println("out the home door");
}
}
HomeDoorEnterBeforeAfter.java
public class HomeDoorEnterBeforeAfter implements BeforeAfter {
@Override
public void before(Object[] args) {
System.out.println("打开室外灯");
}
@Override
public void after(Object result) {
System.out.println("关闭室外灯");
}
}
Door.java
目前代理逻辑中还没有一个 HomeDoorEnterBeforeAfter
实例,可能 BeforeAfter
构造方法需要依赖。下方采用反射构造实例。
public class Proxy implements InvocationHandler {
private HashMap proxyMethods = new HashMap<>();
private Object target;
public Proxy(Object target) throws IllegalAccessException, InstantiationException {
this.target = target;
Method[] methods = target.getClass().getMethods();
for (Method method : methods) {
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(NeedProxy.class)) {
NeedProxy needProxy = (NeedProxy) annotation;
proxyMethods.put(method.getName(), needProxy.beforeAfter().newInstance());
break;
}
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
BeforeAfter beforeAfter;
if ((beforeAfter = proxyMethods.get(method.getName())) != null) {
return invoke0(beforeAfter, proxy, method, args);
}
return method.invoke(target, args);
}
private Object invoke0(BeforeAfter beforeAfter, Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
beforeAfter.before(args);
Object res = method.invoke(target, args);
beforeAfter.after(res);
return res;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Door door;
Proxy proxy = new Proxy(door = new HomeDoorImpl());
door = (Door) java.lang.reflect.Proxy.newProxyInstance(door.getClass().getClassLoader(),
door.getClass().getInterfaces(), proxy);
door.enter();
door.out();
}
}
输出
打开室外灯
enter the home door
关闭室外灯
out the home door
小结
可以看到 JDK 的动态代理实现代理颗粒度较为粗糙,并且需要入侵所有方法,性能上可能有所下降。读者如果对 Proxy
有兴趣的话,可以进一步了解 cglib
是如何实现代理的。