В разработке Java динамические прокси — это мощный метод, позволяющий создавать прокси-объекты во время выполнения для добавления поведения без изменения кода исходного класса. Динамический прокси JDK и Cglib — два основных метода реализации динамического прокси. В этой статье будут рассмотрены принципы их реализации, различия, недостатки и сравнение производительности.
«1. Принцип реализации»
JDK активный прокси опирается на механизм отражения Java.,проходитьjava.lang.reflect.Proxy
класс иInvocationHandler
Реализация интерфейса。это может толькоактерское мастерство Класс, реализующий интерфейс. Во время выполнения JDK активный прокси сгенерирует актерское мастерстводобрый,Класс актерского мастерства реализует целевой интерфейс,и делегировать вызовы методовInvocationHandler
изinvoke
метод。
«2. Пример использования»
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Service {
void perform();
}
class ServiceImpl implements Service {
public void perform() {
System.out.println("Service performed.");
}
}
class ServiceInvocationHandler implements InvocationHandler {
private Object target;
public ServiceInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before service");
Object result = method.invoke(target, args);
System.out.println("After service");
return result;
}
}
public class JDKProxyDemo {
public static void main(String[] args) {
Service service = new ServiceImpl();
Service proxy = (Service) Proxy.newProxyInstance(
service.getClass().getClassLoader(),
service.getClass().getInterfaces(),
new ServiceInvocationHandler(service)
);
proxy.perform();
}
}
«3. Преимущества и недостатки»
преимущество:
недостаток:
«1. Принцип реализации»
Cglib (библиотека генерации кода) основана на базовых операциях с байт-кодом (с использованием библиотеки ASM) и реализует прокси-серверы путем создания подклассов целевого класса и перехвата вызовов методов в подклассах. Следовательно, он может проксировать обычные классы без интерфейсов.
«2. Пример использования»
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
class Service {
public void perform() {
System.out.println("Service performed.");
}
}
class ServiceInterceptor implements MethodInterceptor {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before service");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After service");
return result;
}
}
public class CglibProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service.class);
enhancer.setCallback(new ServiceInterceptor());
Service proxy = (Service) enhancer.create();
proxy.perform();
}
}
«3. Преимущества и недостатки»
преимущество:
недостаток:
Чтобы сравнить производительность этих двух методов, мы проведем простой тест, чтобы сравнить время создания экземпляра прокси и вызова метода.
public class ProxyPerformanceTest {
interface Foo {
void doSomething();
}
static class FooImpl implements Foo {
public void doSomething() {
// Simulate some work
}
}
public static void main(String[] args) {
int iterations = 1000000;
// JDK Dynamic Proxy
Foo jdkProxy = (Foo) Proxy.newProxyInstance(
Foo.class.getClassLoader(),
new Class[]{Foo.class},
(proxy, method, args1) -> {
return method.invoke(new FooImpl(), args1);
});
long jdkStartTime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
jdkProxy.doSomething();
}
long jdkEndTime = System.currentTimeMillis();
System.out.println("JDK Dynamic Proxy time: " + (jdkEndTime - jdkStartTime) + " ms");
// CGLIB Proxy
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(FooImpl.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
return proxy.invokeSuper(obj, args1);
});
Foo cglibProxy = (Foo) enhancer.create();
long cglibStartTime = System.currentTimeMillis();
for (int i = 0; i < iterations; i++) {
cglibProxy.doSomething();
}
long cglibEndTime = System.currentTimeMillis();
System.out.println("CGLIB Proxy time: " + (cglibEndTime - cglibStartTime) + " ms");
}
}
«Результаты испытаний»
В реальных тестах динамический прокси-сервер JDK работал лучше, чем Cglib при создании экземпляров прокси, а Cglib работал лучше, чем динамический прокси-сервер JDK при вызове методов. Это связано с тем, что динамические прокси JDK полагаются на отражение при вызове методов, в то время как Cglib напрямую вызывает сгенерированные методы подкласса.
Подводя итог, можно сказать, что разработчики могут выбрать подходящую технологию прокси-сервера в зависимости от конкретных потребностей и сценариев. В практических приложениях разумный выбор и использование динамических агентов может значительно повысить гибкость и удобство сопровождения кода.
В реальных проектах применение технологии динамического прокси может значительно повысить гибкость и масштабируемость кода. Будь то динамический прокси-сервер JDK или прокси-сервер Cglib, каждый из них имеет свои уникальные преимущества и применимые сценарии. Я надеюсь, что благодаря этой статье каждый получит более глубокое понимание этих двух прокси-технологий и сможет гибко использовать их в реальной разработке.
Какую технологию динамического прокси вы чаще используете в реальных проектах? Почему? С какими проблемами и трудностями вы столкнулись в процессе использования? Добро пожаловать, чтобы поделиться своим опытом и идеями в области сообщений, давайте обсуждать и учиться вместе!