存在即合理,存在就有存在的理由,java动态代理的存在是为了解决静态代理出现的不足,所以首先看一看什么是静态代理:
有如下接口:
public interface SayHello {
void sayHello();
}
有如下实现:
public class HelloImpl1 implements SayHello {
public void sayHello() {
System.out.println("你好");
}
}
public class HelloImpl2 implements SayHello {
public void sayHello() {
System.out.println("Hello");
}
}
此时我们如果有一种需求,就是在每一种实现了SayHello接口的sayHello方法前面或后面都加一个固定的操作,比如说每次sayHello前握手,那么我们要去修改每一个实现类吗?如果不使用代理模式,这是必须的,所以出现了静态代理:
我们需要通过代理类取访问具体的实现对象,所以代理类也需要实现具体的接口,这样做是方便我们知道我们在每个方法前后都做了什么,我想,其实代理类不实现SayHello接口也可以使用,但是这样的类多了以后,会很混乱,不知道某个代理类具体是代理什么的,实现接口后,很明显就知道是为了代理某个接口的实现类
public class HelloProxy implements SayHello{
//通过代理取访问这个target目标对象
SayHello target;
public HelloProxy(SayHello target) {
this.target = target;
}
public void sayHello() {
//先握手
System.out.println("握手");
target.sayHello();
}
}
这样代理之后,我们就不用一个个去修改实现类了,只需要将以前调用SayHello实现类的地方改为调用这个HelloProxy代理对象的sayHello()就可以了。如果最开始就使用代理模式,那么为了加“握手”操作就只需要修改代理类,其他地方完全不用修改。
以上就是静态代理,为什么说是静态呢?
试想,如果我们修改了接口呢?如果接口新添加了一个方法,我们是不是要在代理类中也实现这个方法,并且再将“握手”操作添加进去(这样比喻似乎不是很合理,毕竟不是每一种方法前面都需要握手。可以把“握手”换成“安全检查”,有一种情况,是我们在调用这个类的方法前都需要进行安全检查,那这种说法就是合理的)。
所以,动态代理就是这样的:当接口改变时,我们不需要改变代理类,不需要手动的在每个新方法前面都加上“握手”操作。
动态代理如何实现呢?Java给我们提供了简单方法,分两步:
(1)说明我们想要在每个方法前后加上什么操作(不限定接口)
(2)JVM运行时动态的生成代理类(限定接口,只能生成代理某个接口的代理类,在这种接口每个方法前后都加上第(1)步中规定的操作)
我们来实现SayHello接口的代理,在方法前面加上“握手”操作,按照上面的步骤来进行:
(1)说明我们想要在每个方法前后加上什么操作
//我们需要实现InvocationHandler接口,告诉我们需要添加“握手”操作
public class Operation implements InvocationHandler {
//代理的对象是这个
Object originObj;
public Operation(Object originObj) {
this.originObj = originObj;
}
//这里使用反射来实现调用类内部的方法,所以参数有:类实例,方法,给方法的参数。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("握手");
return method.invoke(originObj, args);
}
}
这里没有指定接口,所以任意一个类都可以使用这个代理,来实现在前面加上“握手”操作。
(2)JVM运行时动态的生成代理类
我们来生成一个SayHello接口的代理类
SayHelloImp hello = new SayHelloImp();
//生成一个操作对象,告诉JVM要添加什么操作
Operation operation = new Operation(hello);
//后面的3个参数,后两个告诉Proxy要代理什么接口,添加什么操作。第一个暂时不清楚。
SayHello helloProxy = (SayHello) Proxy.newProxyInstance(hello.getClass().getClassLoader(),hello.getClass().getInterfaces(),operation);
好了,这就是动态代理的概念以及使用,想知道最终生成的代理实例代码是什么样子,请移步我的另一片博文