文章目录
- 动态代理
- 静态代理的写法
- 动态代理基本写法
- 静态化具体方法
- 改成用JDK的InvocationHandler
动态代理
首先明确代理模式是一个类的方法不能直接使用,我们在客户端和目标对象之间创建一个中介,这个中介就是动态代理,例如租房者,中介和房东之间,租房者通过中介访问房东。
在程序执行的过程中创建代理类对象,在创建过程中利用反射机制来实现的。
即为在使用过程中利用JDK提供的方法才会创建对象。
动态代理的实现有两种:
JDK动态代理:使用jdk字节的类,利用反射实现的代理,反射包Java.lang.reflect包括三个类:Proxy,Method和InvocationHandler
Method:能够实现所有某个接口定义的方法,具体运行哪个具体类的方法,根据invoke传入的对象来决定的。
InvocationHandler:只有一个接口invoke() ,表示代理对象要执行的功能代码,你的代理类完成的功能就写在invoke() 方法中。
代理类完成的功能:调用目标方法,并且功能增强
静态代理的写法
被代理类接口
package proxy1;public interface Foo {void fo();int bar();
}
被代理类
package proxy1;public class target1 implements Foo{@Overridepublic void fo() {System.out.println("fo");}@Overridepublic int bar() {System.out.println("bar");return 1;}
}
代理类
package proxy1;public class $Proxy1 implements Foo{@Overridepublic void fo() {System.out.println("before fo");new target1().fo();}@Overridepublic int bar() {System.out.println("before bar");return new target1().bar();}
}
执行
package proxy1;public class Main1 {public static void main(String[] args) {Foo proxy = new $Proxy1();proxy.fo();proxy.bar();}
}
结果
before fo
fo
before bar
bar
动态代理基本写法
上面的代理类的方法都是写死的,如 new target1().fo(); return new target1().bar();
如果语句是需要动态的执行的话,那么就不好搞了,因此需要把这些动态语句,抽离出来。
这时候定义了InvocationHnadler接口,来表达这个被代理的类,通过h.invoke()方法来执行真实的类,就有了如下写法
我们通过反射的方式动态获取方法对象。
void invoke(Method method, Object[] args)
修改传入参数,一个是要执行的方法,一个是传入方法的参数
在代理中通过反射的方式来获取方法,并且将参数传进去
被代理接口
package proxy2;import java.lang.reflect.InvocationTargetException;public interface Foo {void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;int bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
}
被代理的类
package proxy2;public class target1 implements Foo {@Overridepublic void fo() {System.out.println("fo");}@Overridepublic int bar() {System.out.println("bar");return 1;}
}
代理接口
package proxy2;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public interface InvocationHandler {Object invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}
代理类
package proxy2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {proxy2.InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Method method = Foo.class.getMethod("fo");h.invoke(method, null);}@Overridepublic int bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Method method = Foo.class.getMethod("bar");String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(method, str);}
}
具体执行
package proxy2;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class Main1 {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Foo proxy = new $Proxy1(new InvocationHandler() {@Overridepublic Object invoke(Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {System.out.println("before...");return method.invoke(new target1(), args);}});proxy.fo();System.out.println(proxy.bar());}
}
通过预先定义一个InvocationHandler h,h中传入具体的类的方法即可通过反射的方式实现方法的执行。
ps:这里需要补充一下lamda表达式的写法,在new $Proxy1时传入一个new InvocationHandler(),这是一个接口需要具体实现,于是就有了
{@Overridepublic Object invoke(Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {System.out.println("before...");return method.invoke(new target1(), args);}
}
实际上,这里有个隐形的实现类,不过被隐藏起来了,可以简化为lamda表达式
Foo proxy = new $Proxy1((proxy1, method, objects) -> {System.out.println("before...");return method.invoke(new target1(), args);});
(proxy1, method, objects)就代表实现了接口的类
静态化具体方法
进一步的优化,将被代理类的方法进行静态化,这样可以加快执行速度
package proxy4;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() {try {h.invoke(this, fo, null);} catch (Throwable e) {e.printStackTrace();}}@Overridepublic int bar() {try {String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(this, bar , str);} catch (Throwable e) {e.printStackTrace();return -1;}}//静态化被代理方法static Method bar;static Method fo;static {try {bar = Foo.class.getMethod("bar");fo = Foo.class.getMethod("fo");} catch (NoSuchMethodException e) {e.printStackTrace();}}
}
package proxy3;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() {try {h.invoke(this, fo, null);} catch (InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}}@Overridepublic int bar() {try {String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(this, bar , str);} catch (InvocationTargetException | IllegalAccessException e) {e.printStackTrace();return -1;}}static Method bar;static Method fo;static {try {bar = Foo.class.getMethod("bar");fo = Foo.class.getMethod("fo");} catch (NoSuchMethodException e) {e.printStackTrace();}}
}
改成用JDK的InvocationHandler
在程序中自定义了InvocationHandler
如果删除这个接口,将引用的这个接口改变为import java.lang.reflect.InvocationHandler;实际上效果是一样的
被代理类:
package proxy4;public class target1 implements Foo {@Overridepublic void fo() {System.out.println("fo");}@Overridepublic int bar() {System.out.println("bar");return 1;}
}
代理类
package proxy4;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class $Proxy1 implements Foo {InvocationHandler h;public $Proxy1(InvocationHandler h) {this.h = h;}@Overridepublic void fo() {try {h.invoke(this, fo, null);} catch (Throwable e) {e.printStackTrace();}}@Overridepublic int bar() {try {String[] str = new String[2];str[0] = "param1";str[1] = "param2";return (int) h.invoke(this, bar , str);} catch (Throwable e) {e.printStackTrace();return -1;}}static Method bar;static Method fo;static {try {bar = Foo.class.getMethod("bar");fo = Foo.class.getMethod("fo");} catch (NoSuchMethodException e) {e.printStackTrace();}}
}
测试代码
package proxy4;import java.io.IOException;
import java.lang.reflect.InvocationTargetException;public class Main1 {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {Foo proxy = new $Proxy1((proxy1, method, objects) -> {System.out.println("before...");return method.invoke(new target1(), args);});proxy.fo();System.out.println(proxy.bar());System.out.println(proxy.getClass());System.in.read();}
}
实现InvacationHandler接口,重写invoke()方法,在invoke() 方法中增强类的方法