demo
在本 demo 中我們將創(chuàng)建一個(gè) door 接口,實(shí)現(xiàn)對(duì)接口中所有方法的代理。
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();
}
}
運(yùn)行
輸出
start proxy
enter the home door
end proxy
start proxy
out the home door
end proxy
僅代理某個(gè)方法
可以看到我們將 Door 接口中的所有方法都代理,但在實(shí)際業(yè)務(wù)中可能僅代理某些方法。
接下來(lái)做一些小改動(dòng)來(lái)支持這個(gè)功能。我們決定通過(guò)在方法上添加注解決定是否真正代理(PS:本質(zhì)上實(shí)現(xiàn)類(lèi)的所有方法都會(huì)被代理)
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 時(shí)判斷是否執(zhí)行代理邏輯。
public class Proxy implements InvocationHandler {
private HashMap<String, Object> 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
實(shí)現(xiàn)一個(gè) before-after
真實(shí)業(yè)務(wù)上不可能所有方法都是一樣的代理邏輯(PS:logger 是可以使用同一個(gè)邏輯的)如何模擬實(shí)現(xiàn)一個(gè)類(lèi)似真實(shí)業(yè)務(wù)的 before-after,我們決定在 NeedProxy 和 Proxy 上下工夫。
BeforeAfter.java 定義一個(gè) before-after 接口,這里可以根據(jù)不同的業(yè)務(wù)邏輯實(shí)現(xiàn),after 方法頁(yè)可以包裝以下 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("打開(kāi)室外燈");
}
@Override
public void after(Object result) {
System.out.println("關(guān)閉室外燈");
}
}
Door.java 目前代理邏輯中還沒(méi)有一個(gè) HomeDoorEnterBeforeAfter 實(shí)例,可能 BeforeAfter 構(gòu)造方法需要依賴。下方采用反射構(gòu)造實(shí)例。
public class Proxy implements InvocationHandler {
private HashMap<String, BeforeAfter> 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();
}
}
輸出
打開(kāi)室外燈
enter the home door
關(guān)閉室外燈
out the home door
小結(jié)
可以看到 JDK 的動(dòng)態(tài)代理實(shí)現(xiàn)代理顆粒度較為粗糙,并且需要入侵所有方法,性能上可能有所下降。讀者如果對(duì) Proxy 有興趣的話,可以進(jìn)一步了解 cglib 是如何實(shí)現(xiàn)代理的。