代理的三种方式(静态,动态和cglib)
静态代理
静态代理是java中的一种设计模式,通过创建一个代理类来代替原始类,从而控制对原始类的访问.代理类和原始类之间都实现了相同的接口.使得客户端在使用时无需关心具体的实现细节.代理的概念已经解释了,那为什么叫做静态代理呢?是因为该代理在编译阶段就已经确定了代理类和原始类之间的关系.
静态代理的主要特点:
-
编译时确定:
- 在静态代理中,代理类和原始类的关系是在编译时确定的。这意味着在程序运行之前,代理类的代码就已经生成,并且编译器检查了代理类和原始类之间的关系。
-
接口实现:
- 静态代理通常要求代理类和原始类都实现相同的接口。这是为了确保代理类能够代替原始类,同时客户端代码无需关心具体的实现。
- 相同的接口:
- 原始类(RealSubject)和代理类(ProxySubject)通常都会实现相同的接口,比如上面例子中的
Subject
接口。这确保了代理类具有与原始类相同的方法签名,即相同的公共接口。
- 原始类(RealSubject)和代理类(ProxySubject)通常都会实现相同的接口,比如上面例子中的
- 代替原始类:
- 由于代理类和原始类实现了相同的接口,客户端代码可以通过接口类型引用对象,而不需要关心具体是原始类还是代理类。客户端代码可以将对象视为实现了某个接口的对象,而不关心具体是哪个类的实例。
- 客户端无需关心具体的实现:
- 客户端代码只需要与接口打交道,而不需要关心具体的实现类。客户端代码通过接口引用对象,这使得代理类和原始类可以在不影响客户端的情况下进行替换。这样的设计使得客户端代码更加灵活,可以轻松切换不同的实现类,而不需要修改客户端代码。
-
代理控制:
- 通过代理类,可以在原始类的方法执行前后插入额外的逻辑,实现对原始类的访问控制和功能增强。这使得代理在某些情况下可以用于实现日志记录、性能监控、事务管理等功能。
代码示例:
假设有一个接口 Subject
:
javaCopy codepublic interface Subject {
void request();
}
和一个原始类 RealSubject
:
javaCopy codepublic class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
然后,创建一个代理类 ProxySubject
:
javaCopy codepublic class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 额外逻辑(在原始方法执行前后插入)
System.out.println("ProxySubject: Before request.");
realSubject.request();
System.out.println("ProxySubject: After request.");
}
}
在这个示例中,ProxySubject
是 Subject
接口的实现,通过包含一个 RealSubject
对象,它能够在原始方法执行前后插入额外的逻辑。
使用示例:
javaCopy codepublic class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxy = new ProxySubject(realSubject);
// 客户端无需关心具体的实现
proxy.request();
}
}
在这个例子中,客户端通过代理类 ProxySubject
访问原始类 RealSubject
的方法,而不直接调用原始类的方法。
动态代理
java动态代理是一种在运行时创建代理类的机制,它允许在不提前知道代理类的就提类型的情况下,动态的创建一个代理对象来代替原始类.与静态代理相较,动态代理更加灵活,可以代理任意的接口类型,不需要为每个被代理类编写专门的代理类,而是通过java的反射机制在运行时动态的生成代理类.动态代理类主要使用java.lang.reflect.Proxy类和
java.lang.reflect.InvocationHandler接口实现.
动态代理又称作jdk代理或者接口代理.Proxy
类用于创建动态代理类的实例,而 InvocationHandler
接口则负责处理代理类的方法调用。
- 在运行时创建代理类:
- 动态代理机制允许在程序运行时创建代理类,而不是在编译时确定。这使得我们可以在不提前知道具体代理类型的情况下,动态地创建代理对象来代替原始类。
- 反射机制的应用:
- 动态代理主要使用了Java的反射机制。具体而言,使用
java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。Proxy
类用于创建动态代理类的实例,而InvocationHandler
接口则负责处理代理类的方法调用。
- 动态代理主要使用了Java的反射机制。具体而言,使用
- 代理任意接口类型:
- 与静态代理相比,动态代理更加灵活,因为它可以代理任意的接口类型。在动态代理中,代理类实现了一个或多个接口,并且在运行时生成的代理类会实现这些接口的方法。
- 无需为每个被代理类编写专门的代理类:
- 在动态代理中,我们不需要为每个具体的被代理类编写专门的代理类。相反,我们可以通过一个通用的代理类处理多个接口类型的代理。
代码示例:
假设有一个接口 Subject
和一个实现该接口的原始类 RealSubject
:
javaCopy codepublic interface Subject {
void request();
}
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
使用动态代理创建代理对象:
javaCopy codeimport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxyExample {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
Subject proxySubject = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[]{Subject.class},
new MyInvocationHandler(realSubject)
);
// 通过代理对象调用方法
proxySubject.request();
}
}
class MyInvocationHandler implements InvocationHandler {
private final Subject realSubject;
public MyInvocationHandler(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 额外逻辑(在原始方法执行前后插入)
System.out.println("Dynamic Proxy: Before request.");
Object result = method.invoke(realSubject, args);
System.out.println("Dynamic Proxy: After request.");
return result;
}
}
在这个例子中,Proxy.newProxyInstance
方法会在运行时动态地创建代理对象,代理对象会实现 Subject
接口,并且使用 MyInvocationHandler
处理方法调用。这种方式可以灵活地处理多个接口类型的代理。
cglib代理
cglib代理是一个第三方开源库,用户在java运行时生成字节码并创建代理类.与基于接口的java动态代理不同,cglib代理可以代理普通类(即使他们没有实现任何接口).cglib使用ASM库来生成字节码,并通过继承方式创建代理类 ,因此又被称作子类代理.
- 代理类的生成:
- CGLIB通过继承目标类来创建代理类。它创建一个目标类的子类,并重写其中的方法,以在方法调用前后插入横切逻辑。
- 无需实现接口:
- 与JDK动态代理不同,CGLIB代理不要求目标类实现接口。它可以代理没有实现接口的类。
- 性能:
- CGLIB代理的性能通常比JDK动态代理稍差,因为它涉及到类的继承。但在实际应用中,通常不会明显影响性能。
- Final类和方法:
- CGLIB无法代理final类和final方法,因为它无法重写这些final方法。
- 可见性:
- CGLIB代理只能代理可见的非final类的可见的非final方法。
下面是一个简单的示例,演示了CGLIB代理的使用:
javaCopy codeimport net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CGLIBExample {
public static void main(String[] args) {
// 创建目标类的Enhancer
Enhancer enhancer = new Enhancer();
// 设置目标类的父类(被代理类)
enhancer.setSuperclass(MyService.class);
// 设置方法拦截器
enhancer.setCallback(new MyMethodInterceptor());
// 创建代理类
MyService proxy = (MyService) enhancer.create();
// 调用代理类的方法
proxy.doSomething();
}
}
class MyService {
public void doSomething() {
System.out.println("MyService: Doing something.");
}
}
class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 横切逻辑(在原始方法执行前后插入)
System.out.println("CGLIB Proxy: Before " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB Proxy: After " + method.getName());
return result;
}
}
在这个例子中,MyService
是目标类,MyMethodInterceptor
是方法拦截器,使用CGLIB的Enhancer
来创建代理类。生成的代理类继承了MyService
,并在方法调用前后插入了横切逻辑。
java动态代理和cglib代理的区别
- jdk动态代理只能基于接口,代理生成的对象只能赋值给接口变量,而Cglib就不存在这个问题,Cglib是通过生成子类来实现的,代理对象既可以赋值给实现类,又可以赋值给接口。
- CgLib 创建的动态代理对象性能比 JDK 创建的动态代理对象的性能高不少,但是 CGLib 在创建代理对象时所花费的时间却比 JDK 多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib 合适,反之,使用 JDK 方式要更为合适一些。同时,由于 CGLib 由于是采用动态创建子类的方法,对于 final 方法,无法进行代理.
文章参考: