Static proxies in Quarkus
Tags: quarkus, cdi, java, bytecode
Published: 2023-03-11 23:10:36

In previous [post](https://dev.to/nutrymaco/how-quarkus-use-build-time-to-start-your-application-faster-50n), Quarkus build process was described and how it helps to do DI in build time. In this post we discuss standard approaches for proxy creation and how Quarkus does it. 

## CDI and interceptors

All Quarkus applications use [CDI - Contexts and Dependency Injection](https://jakarta.ee/specifications/cdi/). It has annotation for declarative creating interceptor. As a result for target class creating proxy with interceptor logic. But how proxy is created depends on implementation. 

## How proxy can be created

First, let’s see example of interceptor. `GreeterInterceptor` - interceptor and `WorkService`, `ServiceImpl`, and `FinalService` - bean classes which will be proxied.

`GreeterInterceptor` interceptor - printing greeting from intercepted bean

```java
@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 - not implementing any interfaces

```java
@Singleton
public class WorkService {

	@Greeter
	public void serve() {
		System.out.println("I'm doing work");
	}

}
```

`ServiceImpl` bean - implement interface `Service`

```java
@Singleton
public class ServiceImpl implements Service {

	@Greeter
	@Override
	public void serve() {
		System.out.println("I'm doing work");
	}

}
```

`FinalService` bean - not implementing interfaces, has **final** modifier

```java
@Singleton
public final class FinalService {

	@Greeter
	public void serve() {
		System.out.println("I'm doing work");
	}

}
```

### Dynamic proxy

Dynamic proxy is a proxy created at runtime. There is 2 way to do this: bytecode generation and [Java Dynamic Proxy Class API](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Proxy.html). 

#### Java Dynamic Proxy Class API

With this API you can create proxy via code like this

```java
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);
```

Main limitation of this API, that you can only create proxy for interfaces, so it can only be used for `ServiceImpl` bean in our examples. 

#### Runtime bytecode generation

This approach can be implemented using bytecode generation libs such as [cglib](https://github.com/cglib/cglib) (but they do not support JDK17+) or [ByteBuddy](https://bytebuddy.net/#/). With bytecode generation you can create proxy for `WorkService` and `ServiceImpl` from our examples. But bytecode generation can’t help if target class is final like `FinalService`. 

### Proxy creation in Quarkus

Unlike in cases described above Quarkus knows which class need proxy at build time. So bytecode for proxy can be **generated at build time** without wasting resources at runtime, so it called a **static proxy**. 

Also Quarkus can modify bytecode of classes with `final` modifier, so they can be proxied. As a result Quarkus can create proxy for all our examples - `WorkService` (ordinary class), `ServiceImpl` (implementing interface) and `FinalService` (class with `final` modifier). 

## Summary

In this topic we discussed main approaches for creating proxy - bytecode generation and Java Dynamic Proxy Class API. Quarkus combines bytecode generation, bytecode transformation and knowledge about all classes to generate proxy at build time. Thereby giving better perfomance at runtime and possibility for creating proxy for final classes.