代理模式
代理模式和装饰器模式类似,都是在不改变同一个接口功能的前提下,对原有功能的做扩展或者增强。代理模式并没有做类似于装饰器模式多层嵌套,而是采用灵活的单一结构。在Java语言中并支持动态代理,在很多RPC框架、Spring AOP、Spring事务等领域有着广泛的应用。
代理模式是一种结构性模式,它允许将对象的访问控制和代码运行位置转移到代理对象中。类似于中介,代理对象可以控制客户端对真实对象的访问。代理模式常用于对已有功能的增强,比如访问控制、远程调用。
静态代理
代理模式分为静态代理和动态代码,一般静态代理使用的比较少,而动态代理在各种框架、中间件有着广泛的应用。
静态代理需要手动创建一个代理类,实现被代理对象的接口,并将实际对象的方法调用转发给它。静态代理的优点是简单易懂,但缺点是需要手动创建代理类,对于需要代理的类数量较多或变化频繁的情况下,代码会变得臃肿难以维护。
从上面的 UML 图中,我们可以看出代理模式有三个关键角色:
抽象主题接口类(Subject):它定义了一些方法。
主题实现类(RealSubject):实现了抽象接口类(的所有方法
代理类(StaticProxy):实现了抽象主题类的方法,并隐藏在代理后面可能其他类的实现。
代码实现
定义一个抽象主题类:
public interface Subject {
void operation();
}
主题实现类(RealSubject)的代码如下:
public class RealSubject implements Subject{
@Override
public void operation() {
System.out.println("do somthing");
}
}
代理类(StaticProxy)实现了抽象主题类,并持有主题实现类的对象,并在主题实现类的对象的operation()方法之前和之后做了功能的增强,具体代码如下:
public class StaticProxy implements Subject{
private RealSubject realSubject;
public StaticProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void operation() {
System.out.println("before operation...");
realSubject.operation();
System.out.println("after operation...");
}
}
写一个客户端实现类,代码如下:
public class Client {
public static void main(String[] args) {
testStatic();
}
public static void testStatic() {
StaticProxy staticProxy = new StaticProxy(new RealSubject());
staticProxy.operation();
}
}
运行代码,输出如下:
before operation...
do somthing
after operation...
动态代理
动态代理可以使用 Java 动态代理机制和CGLIB动态代理。
Java 动态代理
Java 动态代理是在运行时自动生成代理类并将方法调用转发到实际对象。 Java 动态代理的优点是避免了手动创建代理类的麻烦,但缺点是对于一些无法实现接口的类,无法使用动态代理。
Java 动态代理是使用Java的反射机制来实现动态代理。Java提供了java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现动态代理。
具体使用Java 动态代理的步骤是先实现InvocationHandler接口。和静态代理类似:在这个接口中,需要对真实的代理对象的功能需要做一下加强,它是实现动态代理的关键,代码如下:
public class ProxyHandler implements InvocationHandler {
private Object object;
public ProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
method.invoke(object, args);
System.out.println("After invoke " + method.getName());
return null;
}
}
实现代理如下:
public class Client {
/**
* jdk动态代理
*/
public static void testJdkProxy() {
Subject realSubject = new RealSubject();
ProxyHandler handler = new ProxyHandler(realSubject);
Subject subject = (Subject) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), handler);
subject.operation();
}
public static void main(String[] args) {
testJdkProxy();
}
}
运行代码输出:
Before invoke operation
do somthing
After invoke operation
Java动态代理是基于反射生产的一个代理类,这个代理类本身已经继承了jdk包中的Proxy对象,而Java是不允许多继承的,所以只能实现接口的方式进行代理。
它的优点如下:
- Java动态代理不需要任何依赖
- 灵活性:动态代理可以在运行时动态生成代理类,
- 可拓展性:可以通过动态代理实现一些横切关注点,比如日志记录、性能监控等,而无需修改原有的代码。
缺点:
- 性能开销:由于动态代理是在运行时动态生成代理类,相比直接调用实现类的方法,会带来一定的性能开销
- 功能限制:动态代理只能对接口进行代理,在某些情况下,无法代理实现类的方法。
CGLIB代码
在pom文件中引入cglib包,代码如下:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
version>3.2.5</version>
</dependency>
CGlib代码模式需要实现MethodInterceptor,并在intercept方法中实现真实对象的功能增强,代码如下:
public class CglibProxyFactory implements MethodInterceptor {
private Object target;//维护一个目标对象
public CglibProxyFactory(Object target) {
this.target = target;
}
//为目标对象生成代理对象
public Object getProxyInstance() {
//工具类
Enhancer en = new Enhancer();
//设置父类
en.setSuperclass(target.getClass());
//设置回调函数
en.setCallback(this);
//创建子类对象代理
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("开始cglib拦截");
// 执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("结束cglib拦截");
return returnValue;
}
}
写一个测试类:
public class Client {
public static void main(String[] args) {
testCglibProxy();
}
/**
* 测试cglib
*/
public static void testCglibProxy() {
Subject subject = new RealSubject();
Subject proxy = (Subject) new CglibProxyFactory(subject).getProxyInstance();
proxy.operation();
}
}
执行结果如下:
开始cglib拦截
do somthing
结束cglib拦截
CGLIB(Code Generation Library)是一个强大的高性能的代码生成库,用于在运行时扩展Java类和实现动态代理。与Java动态代理不同,CGLIB可以代理非接口类型的类。
CGLIB动态代理的优点:
- 性能高:相比Java动态代理,CGLIB动态代理通常能够提供更好的性能,因为它是通过生成子类来代理目标类,而不是通过实现接口。
- 功能强大:CGLIB能够代理普通类和接口类,更灵活地满足额外需求,例如代理私有方法、拦截静态方法等。
- 无需依赖接口:CGLIB动态代理可以代理没有实现任何接口的类,这使得它可以代理更多类型的类,提供更大的灵活性。
CGLIB动态代理的缺点:
- 需要额外依赖:CGLIB动态代理需要引入额外的库,增加项目的依赖,相比Java动态代理而言更为复杂。
- 对final方法和final类的限制:CGLIB无法代理final方法和final类。
- 不支持自身方法调用:CGLIB无法从代理对象中调用自身的方法,这可能会引起无限循环或抛出异常。