前提准备
在了解静态代理模式或者动态代理模式之前,我们需要先了解Java反射
Java反射是在java运行过程中,我们能够动态的知道某个类的字段(Field)、构造方法(Constructor)和方法(Method),包括该类的私有方法/属性。这种动态获取的信息以及动态调用对象的方法的功能称为 java语言的反射机制。
Class对象是用来描述类的class文件在方法区内的数据结构,也就是来描述类本身的属性。每个类可以有很多很多个引用,但是所有引用都对应着同一个Class对象(在同一个命名空间的前提下)
所以,在我们动态的获取到类的属性和方法之前,我们需要首先拿到类的Class对象,再通过Class对象拿到相应的信息。下面是一个简单的反射例子,调用某个类的私有方法。
public class TestPrivate {
public static void main(String[] args) throws Exception {
Class<?> classType = Class.forName("com.invoketest.PrivateTest");
// classType.newInstance(); 只能生成不带参数的构造方法,// classType.newInstance(); 只能生成不带参数的构造方法,
// 如果需要调用带构造参数的方法,假设只有一个参数,且是String类型的
// Constructor<?> constructor = classType.getConstructor(new Class[]{String.class});
// System.out.println(constructor.newInstance("2"));
PrivateTest p = (PrivateTest) classType.newInstance();
// getMethod只能返回public方法
// classType.getMethod("sayHello", String.class);
// getDeclareMethod 获取这个Class类里对应的声名的方法
Method method = classType.getDeclaredMethod("sayHello", new Class[]{String.class});
method.setAccessible(true);
// 直接invoke会抛出IllegalAccessException,所以不能用普通方法访问
System.out.println(method.invoke(p, new Object[]{"CC"}));
}
}
public class PrivateTest {
private String sayHello(String name) {
return "Hello " + name;
}
}
在这边就不详细讲述反射的相关概念了,我们进入正题
静态代理模式
角色:
- 抽象角色:声明真实对象和代理对象的共同接口
- 代理角色:代理角色对象内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都可以代替真实对象。同时,代理对象可以在执行对象操作时,附加其他的操作,相当于对真实对象进行分装
- 真实角色:代理角色所代表的真实对象,最终是我们要引用的对象
代码示例
真实角色
package com.proxy;
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("From real subject.");
}
}
抽象角色
package com.proxy;
public abstract class Subject {
public abstract void request();
}
代理角色
package com.proxy;
public class ProxySubject extends Subject {
// 代理角色内部引用类真实角色
private RealSubject realSubject;
@Override
public void request() {
// 这个角色操作前所增加的角色
this.preRequest();
if (null == realSubject) {
realSubject = new RealSubject();
}
// 真实角色所完成的事情
realSubject.request();
// 真实角色操作之后所附加的角色
this.postRequest();
}
private void preRequest() {
System.out.println("pre request");
}
private void postRequest() {
System.out.println("post request");
}
}
客户端
package com.proxy;
public class Client {
public static void main(String[] args) {
// 代理角色和真实角色同时具有的接口
Subject subject = new ProxySubject();
subject.request();
}
}
缺点
由于静态代理模式需要提前知道我们需要代理的真实类,且一个真实类对应一个代理类,会导致代码的急剧膨胀。并且我们需要知道具体调用真实角色的哪个方法,需要把方法都列举给我们,这是不方便的。那么我们可以使用动态代理模式来解决这些问题
动态代理模式
动态代理模式运用了反射的机制,实现了InvocationHandler这个接口,持有一个Object类型的对象(这就没有写死具体是哪个真实类,可以一个代理类代理多个真实类),通过实现类invoke方法传入的method参数来知道调用Object具体的哪个方法,再进行调用。
代码示例1
最简单的一种形式,只有一个真实类
实现了InvocationHandler的实现类
package com.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传进来一个对象
* 此外,该类还实现了Invoke方法,该方法的method的Invoke其实就是调用被代理对象将要执行的方法
* 方法参数是sub
*/
public class DynamicSubject implements InvocationHandler {
// 真正调用的对象类型
private Object sub;
public DynamicSubject(Object object) {
this.sub = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before calling " + method);
// 调用哪个真实对象的方法,传入真实的参数
method.invoke(sub, args);
System.out.println("after calling " + method);
return null;
}
}
真实类
package com.dynamicproxy;
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("From Real Subjcet");
}
}
公共接口
package com.dynamicproxy;
public interface Subject {
public void request();
}
客户端
package com.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Client2 {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
InvocationHandler handler = new DynamicSubject(realSubject);
Class<?> classType = handler.getClass();
// 下面的代码一次性生成代理
Subject instance = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(), handler);
instance.request();
}
}
代码示例2
有两个真实的实现类的情况
package com.dynamictest3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class CommonInvocationHander implements InvocationHandler {
private Object object;
public CommonInvocationHander(Object o) {
this.object = o;
}
public CommonInvocationHander() {
}
public void setObject(Object o) {
this.object = o;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object, args);
}
}
package com.dynamictest3;
public interface Foo {
void doAction();
}
package com.dynamictest3;
public class FooImpl implements Foo {
@Override
public void doAction() {
System.out.println("in FooImpl doAction!");
}
}
package com.dynamictest3;
public class FooImpl2 implements Foo {
@Override
public void doAction() {
System.out.println("in FooImpl2 doAction !");
}
}
package com.dynamictest3;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
// FooImpl instance1 = new FooImpl();
// Foo proxy = (Foo) Proxy.newProxyInstance(instance1.getClass().getClassLoader(),
// instance1.getClass().getInterfaces(), new CommonInvocationHander(instance1));
// proxy.doAction();
CommonInvocationHander hander = new CommonInvocationHander();
Foo f = null;
hander.setObject(new FooImpl());
f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[]{Foo.class}, hander);
f.doAction();
System.out.println("---------------------------");
hander.setObject(new FooImpl2());
f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[]{Foo.class}, hander);
f.doAction();
}
}
第二个例子就很好的展示了动态代理的优势。