代理模式(Proxy Pattern)是Java中常用的一种设计模式,属于对象结构型模式。其核心思想是为其他对象提供一种代理以控制对这个对象的访问。这种设计模式在软件设计中非常有用,因为它可以在不修改原始类代码的前提下,通过引入代理类来给原始类附加额外的功能或控制。代理模式可以分为静态代理和动态代理两种。下面将详细介绍这两种代理模式的原理、应用场景以及给出相应的Java代码示例。
一、代理模式原理
代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。
代理模式中有三个主要角色:
1、抽象角色(Subject)
声明了真实对象和代理对象的共同接口,使得代理对象可以在不修改接口的情况下代替真实对象。
2、真实角色(RealSubject)
代理对象所代表的真实对象,是最终要引用的对象。
3、代理角色(Proxy)
代理对象,内部含有对真实对象的引用,并且可以控制对真实对象的访问,同时可以在客户端和目标对象之间起到中介的作用。
二、静态代理
静态代理是指在程序编译期就已经确定了代理类和被代理类的关系。你必须显式地编写代理类,并实现与真实主题相同的接口。
1、静态代理的步骤
1)创建接口:
定义抽象角色。
2)创建实现类:
实现接口中的方法,作为真实角色。
3)创建代理类:
同样实现接口,并在代理类中持有真实对象的引用,通过代理类的方法来调用真实对象的方法,并可以在调用前后添加额外的逻辑。
4)客户端通过代理类访问真实对象。
2、Java示例代码
假设我们有一个Animal接口,以及一个实现了该接口的AnimalImpl类。我们需要为这个类创建一个静态代理类StaticProxy。
// 1. 创建接口
public interface Animal { void run(); void eat();
} // 2. 创建实现类
public class AnimalImpl implements Animal { @Override public void run() { System.out.println("猎豹跑的都很快"); } @Override public void eat() { System.out.println("动物为了生存,一般都喜欢进食"); }
} // 3. 创建代理类
public class StaticProxy implements Animal { private Animal animal; // 目标对象 // 构造函数 public StaticProxy(Animal _animal) { this.animal = _animal; } @Override public void run() { System.out.println("事物开始前"); animal.run(); System.out.println("事物开始后"); } @Override public void eat() { System.out.println("事物开始前"); animal.eat(); System.out.println("事物开始后"); }
} // 4. 客户端
public class TestMain { public static void main(String[] args) { // 1. 创建目标对象 Animal animal = new AnimalImpl(); // 2. 创建代理对象 StaticProxy staticProxy = new StaticProxy(animal); // 3. 执行方法 staticProxy.run(); staticProxy.eat(); }
}
三、动态代理
动态代理是在运行时动态生成代理类,不需要显式地编写代理类。Java提供了两种动态代理机制:JDK动态代理和CGLIB动态代理。
1、说明
JDK动态代理适用于实现了接口的类,利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。
JDK动态代理的步骤
1)创建接口
定义抽象角色。
2)创建实现类
实现接口中的方法,作为真实角色。
3)创建InvocationHandler实现类
实现invoke方法,该方法将处理对代理实例上所有方法的调用。
通过Proxy类的newProxyInstance方法创建代理对象,将接口数组、类加载器和InvocationHandler实例作为参数传递给此方法。
2、JDK动态代理示例代码
// 1. 创建接口
public interface MyService { void doSomething();
} // 2. 创建实现类
public class MyServiceImpl implements MyService { @Override public void doSomething() { System.out.println("Doing something..."); }
} // 3. 创建InvocationHandler实现类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private final Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before method call"); Object result = method.invoke(target, args); System.out.println("After method call"); return result; }
} // 4. 创建代理对象
import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { MyService service = new MyServiceImpl(); MyService proxy = (MyService) Proxy.newProxyInstance( MyService.class.getClassLoader(), new Class[]{MyService.class}, new MyInvocationHandler(service) ); proxy.doSomething(); }
}
3、CGLIB动态代理
CGLIB是一个高性能的代码生成库,它在运行时生成代理子类,适用于没有实现接口的类。
1)CGLIB动态代理的步骤
定义类:不需要实现任何接口。
创建MethodInterceptor实现类:实现intercept方法,用于拦截并处理方法的调用。
使用Enhancer类创建代理对象,并设置回调方法。
2)CGLIB动态代理示例代码
// 1. 定义类
public class MyService { public void doSomething() { System.out.println("Doing something..."); }
} // 2. 创建MethodInterceptor实现类
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method; public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before method call"); Object result = proxy.invokeSuper(obj, args); System.out.println("After method call"); return result; }
} // 3. 创建代理对象
import net.sf.cglib.proxy.Enhancer; public class Main { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(MyService.class); enhancer.setCallback(new MyMethodInterceptor()); MyService service = (MyService) enhancer.create(); service.doSomething(); }
}
四、代理模式的应用场景
代理模式在多种场景下都非常有用,包括但不限于以下几种情况:
1、远程代理
为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。
2、虚拟代理
根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。
3、保护代理
控制对原始对象的访问,用于对象应该有不同的访问权限的时候。
4、智能指引
取代了简单的指针,它在访问对象时执行一些附加操作,比如对指向实际对象的引用计数、第一次引用一个持久对象时,将它装入内存等。
五、总结
代理模式是一种非常有用的设计模式,它可以在不修改原始类代码的情况下,通过引入代理类来给原始类附加额外的功能或控制。在Java中,静态代理和动态代理是实现代理模式的两种主要方式,它们各自适用于不同的场景。静态代理在编译时就确定了代理类和被代理类的关系,而动态代理则在运行时动态生成代理类。理解并掌握代理模式对于提高代码的可维护性和可扩展性具有重要意义。