1.模拟 jdk 动态代理
jdk动态代理直接看源码看不懂,因为内部使用asm动态生成代理类的字节码,所以我们采用模拟的方式来学习。
public class A12copy {
interface Foo {
void foo();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
public static void main(String[] args) {
Foo proxy = new $Proxy0();
proxy.foo();
}
}
public class $Proxy0 implements Foo {
@Override
public void foo() {
//1.功能增强
System.out.println("before....");
//2.调用目标
new A12copy.Target().foo();
}
}
但这么写功能增强和调用目标都写死了,应该写成抽象方法,用的时候提供抽象实现,有利于将来扩展。
public class A12copy {
interface Foo {
void foo();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}
interface InvacationHandler {
public void invoke();
}
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvacationHandler() {
@Override
public void invoke() {
// 1.功能增强
System.out.println("before....");
// 2.调用目标
new Target().foo();
}
});
proxy.foo();
}
}
public class $Proxy0 implements Foo {
private InvacationHandler h;
public $Proxy0(InvacationHandler h) {
this.h = h;
}
@Override
public void foo() {
h.invoke();
}
}
将需要增强的功能在invoke中实现,代理类只需要调用代理目标的invoke方法就行。代理类此时不需要以来目标了,甚至都不需要知道目标的存在。代理类只需要知道接口和InvacationHandler就行了。
此时的问题是如果interface Foo {
void foo();
}接口加了一个bar方法,它在代理类掉的也是foo的输出。
继续改进代码
public class A12copy {
interface Foo {
void foo();
void bar();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
@Override
public void bar() {
System.out.println("target bar");
}
}
interface InvacationHandler {
public void invoke(Method method,Object[] args)throws Throwable;
}
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvacationHandler() {
@Override
public void invoke(Method method,Object[] args) throws Throwable{
// 1.功能增强
System.out.println("before....");
// 2.调用目标
//new Target().foo();
method.invoke(new Target(),args);
}
});
proxy.foo();
proxy.bar();
}
}
public class $Proxy0 implements Foo {
private InvacationHandler h;
public $Proxy0(InvacationHandler h) {
this.h = h;
}
@Override
public void foo() {
try {
Method foo = Foo.class.getMethod("foo");
h.invoke(foo, new Object[0]);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public void bar() {
try {
Method bar = Foo.class.getMethod("bar");
h.invoke(bar, new Object[0]);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
输出合理了。
此时foo方法和bar方法都时void,若有返回值还需要改进
public class A12copy {
interface Foo {
void foo();
int bar();
}
static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
@Override
public int bar() {
System.out.println("target bar");
return 100;
}
}
interface InvacationHandler {
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable;
}
public static void main(String[] args) {
Foo proxy = new $Proxy0(new InvacationHandler() {
@Override
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
// 1.功能增强
System.out.println("before....");
// 2.调用目标
//new Target().foo();
return method.invoke(new Target(),args);
}
});
proxy.foo();
proxy.bar();
}
}
public class $Proxy0 implements Foo {
private InvacationHandler h;
public $Proxy0(InvacationHandler h) {
this.h = h;
}
@Override
public void foo() {
try {
h.invoke(this,foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public int bar() {
try {
Object result = h.invoke(this,bar, new Object[0]);
return (int) result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
static Method foo;
static Method bar;
static {
try {
foo = Foo.class.getMethod("foo");
bar = Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}
主要改进点 1.bar有了返回值,改了几处。2.foo = Foo.class.getMethod("foo");在方法内每次都会调用,其实调用一次就行,放到静态代码块中,3.调整了foo和bar抛异常的类型,按运行时异常和检查异常抛。
最后将InvacationHandler接口注释掉导入jdk的InvacationHandler可以让public class $Proxy0 extends Proxy,可以简化构造方法就不演示l。