在以前的post中,描述了Quarkus构建过程以及在构建时间中如何帮助进行DI。在这篇文章中,我们讨论了代理创建的标准方法以及Quarkus是如何做到的。
CDI和拦截器
所有Quarkus应用程序都使用CDI - Contexts and Dependency Injection。它具有声明性创建拦截器的注释。结果,目标类创建具有Interceptor逻辑的代理。但是如何创建代理取决于实现。
如何创建代理
首先,让我们看拦截器的示例。 GreeterInterceptor
-Interceptor和WorkService
,ServiceImpl
和FinalService
-将被代理的bean类。
GreeterInterceptor
拦截器 - 拦截豆的打印问候
@Greeter
@Interceptor
public class GreeterInterceptor {
@AroundInvoke
public Object greet(InvocationContext ctx) throws Exception {
System.out.println("Hello from " + ctx.getTarget());
return ctx.proceed();
}
}
WorkService
bean-未实现任何接口
@Singleton
public class WorkService {
@Greeter
public void serve() {
System.out.println("I'm doing work");
}
}
ServiceImpl
bean-实现接口Service
@Singleton
public class ServiceImpl implements Service {
@Greeter
@Override
public void serve() {
System.out.println("I'm doing work");
}
}
FinalService
bean-不实现界面,具有 final 修饰符
@Singleton
public final class FinalService {
@Greeter
public void serve() {
System.out.println("I'm doing work");
}
}
动态代理
动态代理是在运行时创建的代理。有两种方法可以做到这一点:bytecode Estrenation and Java Dynamic Proxy Class API。
Java动态代理类API
使用此API,您可以像这样的代码创建代理
InvocationHandler handler = new MyInvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy logic
}
}
Service f = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class<?>[] { Service.class },
handler);
此API的主要限制,您只能为接口创建代理,因此在我们的示例中只能用于ServiceImpl
bean。
运行时字节代码生成
可以使用诸如cglib(但它们不支持JDK17+)或ByteBuddy的字节码生成LIB实现此方法。使用ByteCode Generation,您可以从我们的示例中为WorkService
和ServiceImpl
创建代理。但是,如果目标类是最终的FinalService
,则字节码的生成可能会有所帮助。
Quarkus的代理创建
与上面描述的情况不同,Quarkus知道在构建时间哪个类需要代理。因此,可以在构建时间生成而不会在运行时浪费资源,因此可以 static代理。
Quarkus也可以使用final
修饰符修改类的字节码,因此可以代理。结果,Quarkus可以为我们所有示例创建代理-WorkService
(普通类),ServiceImpl
(实现接口)和FinalService
(与final
修饰符类)。
概括
在此主题中,我们讨论了创建代理的主要方法 - 字节码生成和Java Dynamic代理类API。 Quarkus结合了字节码的生成,字节码转换和有关所有类的知识,以在构建时间生成代理。从而在运行时提供更好的表现,并为最终课程创建代理的可能性。