首先明确代理模式是一个类的方法不能直接使用,我们在客户端和目标对象之间创建一个中介,这个中介就是动态代理,例如租房者,中介和房东之间,租房者通过中介访问房东。
在程序执行的过程中创建代理类对象,在创建过程中利用反射机制来实现的。
即为在使用过程中利用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{
@Override
public void fo() {
System.out.println("fo");
}
@Override
public int bar() {
System.out.println("bar");
return 1;
}
}
代理类
package proxy1;
public class $Proxy1 implements Foo{
@Override
public void fo() {
System.out.println("before fo");
new target1().fo();
}
@Override
public 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 {
@Override
public void fo() {
System.out.println("fo");
}
@Override
public 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;
}
@Override
public void fo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = Foo.class.getMethod("fo");
h.invoke(method, null);
}
@Override
public 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() {
@Override
public 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(),这是一个接口需要具体实现,于是就有了
{
@Override
public 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;
}
@Override
public void fo() {
try {
h.invoke(this, fo, null);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public 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;
}
@Override
public void fo() {
try {
h.invoke(this, fo, null);
} catch (InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
public 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();
}
}
}
在程序中自定义了InvocationHandler
如果删除这个接口,将引用的这个接口改变为import java.lang.reflect.InvocationHandler;实际上效果是一样的
被代理类:
package proxy4;
public class target1 implements Foo {
@Override
public void fo() {
System.out.println("fo");
}
@Override
public 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;
}
@Override
public void fo() {
try {
h.invoke(this, fo, null);
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public 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() 方法中增强类的方法