preface

Context and Dependency Injection (CDI), Contexts and Dependency Injection (Contexts and Dependency Injection), is a standard specification for J2EE6, which could be used to regulate context Dependency Injection (CDI).

1. What are beans?

A bean is a Container-managed object that supports basic services such as dependency injection, lifecycle callbacks, and interceptors.

2. Introduction to Container-managed

In short, you don’t have to directly control the life cycle of an object instance. Instead, you can influence the lifecycle in declarative ways (such as annotations, configurations, and so on). A container is the environment in which the application runs. It creates and destroys an instance of a bean, associates the instance with the specified context, and then injects it into other beans.

Developers can focus on business logic without having to figure out “where and how” to get a fully initialized component with all dependencies.

(IoC) programming principle: Dependency injection is an IoC implementation technology.

3. Examples of beans

There are many types of beans, the most common of which are type-based beans

`import javax.inject.Inject; ` `import javax.enterprise.context.ApplicationScoped; ` `import org.eclipse.microprofile.metrics.annotation.Counted; @applicationScoped //1 a scope annotation. It tells the container the context in which the Bean instance is associated. In this special case, a bean instance is created for the application and the Translator is injected by all other beans. ' 'public class Translator {'' @inject // A field injection point. This tells the Translator to rely on Dictionarybean containers. If there are no matching beans, the build will fail. ` `Dictionary dictionary; Counted //3 This is an interceptor binding annotation. In this case, the comments come from MicroProfile Metrics. The correlation interceptor intercepts the call and updates the correlation metric. ` `String translate(String sentence) {` `// ... ` ` `} ` `}Copy the code

How does dependency resolution work?

In CDI, the process of matching beans to injection points is type-safe. Each bean declares a set of bean types. In the example above, Translatorbean has two bean types: Translator and java.lang.object. Subsequently, if the Bean has a Bean type that matches the _ required type _ and has all _ required qualifiers _, the Bean can be assigned to the injection point.

Multiple beans declare the same type?

There is a simple rule: a bean must be assigned exactly to an injection point, or the build will fail. If there is no assignable, the build will fail UnsatisfiedResolutionException. If it can be assigned multiple AmbiguousResolutionException build will be failure. This is useful because the application will quickly fail as long as the container can’t find an explicit dependency on any injection points.

Can use programming search through javax.mail. Enterprise. Inject. The Instance to solve the ambiguity of the runtime, even iterates through all the bean implementation of a given type:

`public class Translator {` `@Inject` `Instance<Dictionary> dictionaries; //1 This injection point does not result in ambiguous dependencies even if there are multiple beans implementing the Dictionary type. ` `String translate(String sentence) {` `for (Dictionary dict : Dictionaries) {/ / 2 javax.mail. Enterprise. Inject. The Instance Iterable. ` ` / /... ` `} ` `} ` ` `}Copy the code

Setters and constructors can be used for injection

In fact, in CDI, “setter injection” has been replaced by a more powerful initialization method. Initializers can accept multiple arguments without having to follow JavaBean naming conventions.

Examples of initialization and constructor injection

`@ApplicationScoped` `public class Translator {` `private final TranslatorHelper helper; ' 'Translator(TranslatorHelper helper) {//1 This is a constructor injection. In fact, this code does not work in a regular CDI implementation, where a bean with a normal scope must always declare a no-args constructor, and the bean constructor must use the annotation @inject. However, in Quarkus, we detected the absence of the no-args constructor and "added" it directly to the bytecode. Inject If only one constructor exists, do not add it. ` `this.helper = helper; ' '} ' '@inject //2 The initialization method must be annotated @inject. Void setDeps(Dictionary DIC, LocalizationService locService) {//3 The initializer can take multiple arguments - each of which is an injection point. ` ` /... ` ` `} ` `}Copy the code

7. About qualifiers

Qualifier, an annotation, is an annotation type that helps containers distinguish between beans that implement the same thing. If a bean has all the required qualifiers, it can be assigned to an injection point. If you do not declare any qualifiers at the injection point, @default is used

The Qualifier type is a Java annotation defined as @Retention (RUNTIME) and annotated with the @javax.inject.Qualifier meta-annotation.

The sample

`@Qualifier`
`@Retention(RUNTIME)`
`@Target({METHOD, FIELD, PARAMETER, TYPE})`
`public @interface Superior {}`
Copy the code

Declare a Qualifier for a Bean by annotating a Bean class or producer method or field with a Qualifier:

Example of a Bean with a custom qualifier

'@superior //1@Superior is a qualifier comment. ` `@ApplicationScoped` `public class SuperiorTranslator extends Translator {` `String translate(String sentence) {` `// . ` ` `} ` `}Copy the code

This bean can be assigned to @Inject @Superior Translator, @Inject @Superior Translator, but not to @Inject Translator. The reason is that the @Inject Translator is automatically converted to the @Inject @default Translator during type-safe resolution. And since we SuperiorTranslator did not declare @default that only raw translatorbeans are allocatable.

What is the scope of a Bean

The scope of a bean determines the life cycle of its instance, that is, when and where instances are created and destroyed. (Each bean has an exact scope)

9. What scopes do beans have in Quarkus

Can use all specifications mentioned in the built-in scope (except) javax.mail. Enterprise. Context. ConversationScoped.

annotations

describe

@javax.enterprise.context.ApplicationScoped

A single bean instance is used for the application and is shared across all injection points. Instances are created lazily, that is, once a method is invoked on the client proxy.

@javax.inject.Singleton

Just like @applicationScoped except that it doesn’t use any client proxies. This instance is created when the injection resolves to the injection point of the @Singleton bean.

@javax.enterprise.context.RequestScoped

The Bean instance is associated with the current _ request _ (usually an HTTP request).

@javax.enterprise.context.Dependent

This is a pseudo-scope. These instances are not shared, and each injection point generates a new dependent Bean instance. The life cycle of a dependent bean is bound to the bean that injects it – it is created and destroyed along with the bean that injects it.

@javax.enterprise.context.SessionScoped

The range consists of a javax.mail. Servlet. HTTP. Support the HttpSession object. Only available with the Quarkus-Undertow extension.

  • The Quarkus extension can provide additional custom scopes. Quarkus – for example, narayana – jta provides javax.mail. Transaction. TransactionScoped.

@ApplicationScoped and @Singleton look very similar. Which one should I choose?

Depending on: an @Singleton bean has no client proxy and is therefore an instance _ eagerly created _ when the bean is injected. In contrast, an instance of the @ApplicationScoped bean is _ delayed-created _, that is, the first time a method is called on the injected instance.

In addition, the client proxy only delegates method calls, so @ApplicationScoped should never read/write directly to the fields of the injected Bean. Injected fields can be safely read/written by @Singleton.

@Singleton should have better performance because there are no indirect effects (no agents delegated from context to the current instance).

On the other hand, @Singleton cannot simulate beans using QuarkusMock.

The @ApplicationScopedBean can also be destroyed and recreated at run time. The existing injection point works fine because the injected agent delegates to the current instance.

Therefore, @ApplicationScoped is recommended to stick with @Singleton by default unless there is a good reason to use it.

11, Client Proxies concept

The client proxy is basically an object that delegates all method calls to the target Bean instance. This is an implementation of IO. Quarkus. Arc. ClientProxy structure and expand the container bean class. It implements the IO. Quarkus. Arc. ClientProxy and inherited the bean class. The client proxy only delegates method calls. Therefore, fields of normal scoped beans cannot be read or written, otherwise out-of-context or stale data will be used.)

Sample client proxy generated

`@ApplicationScoped` `class Translator {` `String translate(String sentence) {` `// ... ` `}` `}` `// The client proxy class is generated and looks like... Class Translator_ClientProxy extends Translator {//1Translator_ClientProxy always injects this instance, rather than directly referencing the bean's context instance Translator. ` `String translate(String sentence) {` `// Find the correct translator instance... ` `Translator translator = getTranslatorInstanceFromTheApplicationContext(); ` `// And delegate the method invocation... ` `return translator.translate(sentence); ` ` `} ` `}Copy the code

Customer agents can:

  • Lazy instantiation – Instances are created as soon as a method is invoked on the proxy.
  • You can inject a “narrower” bean into a “wider” bean (for example, you can inject an @RequestScoped bean into an @ApplicationScoped bean).
  • Circular dependencies in the dependency diagram. Having cyclic dependencies usually indicates that redesign should be considered, but sometimes it is unavoidable.
  • Beans can be destroyed manually, and directly injected references will result in stale bean instances.

The type of the bean

  • Class bean
  • Producer methods
  • Producer fields
  • Synwide-footing (composite) beans // Generally provided by extensions

Producer methods and fields are useful if you need additional control over bean instantiation. When integrating third-party libraries, it is also useful to have no control over the class source and possibly not be able to add additional annotations.

Producer example

'@applicationScoped' 'public class Producers {' @Produces //1 Container analyzes field annotations to build Bean metadata. This type is used to build a set of bean types. In this case, it will be double and java.lang.Object. No scoped annotations are declared, so the default is @dependent. ` `double pi = Math.PI; //2 This field is read by the container when the bean instance is created. The container analyzes method annotations to build Bean metadata. The return type is used to create a set of bean species. In this case, these would be List<String>, Collection<String>, Iterable<String> and java.lang.object. No scoped annotations are declared, so the default is @dependent. ` `List<String> names() {` `List<String> names = new ArrayList<>(); ` `names.add("Andy"); ` `names.add("Adalbert"); ` `names.add("Joachim"); ` `return names; // When creating bean instances, the container calls this method @applicationScoped public class Consumer {' @inject ' 'double PI; ` `@Inject` `List<String> names; ` ` / /... ` ` `}Copy the code

More information about producers. You can declare qualifiers, inject dependencies into producer method parameters, and so on.

Note – Life cycle callbacks

Bean classes can declare life cycle @postConstruct and @predestroy callbacks:

Life cycle callback example

`import javax.annotation.PostConstruct; ` `import javax.annotation.PreDestroy; "@applicationScoped" public class Translator {" @postconstruct //1 is called before the bean instance joins the service, where it is safe to perform initialization. ` `void init() {` `// ... '} '@predestroy //2 is called before the bean instance is destroyed, where it is safe to do some cleanup {' //... ` ` `} ` `}Copy the code

It is best to keep the logic “side-effect free” in the callback function, that is, to avoid calling other beans in the callback function.

Annotations – interceptors

Interceptors are used to separate cross-domain concerns from business logic. There is a separate specification -Java Interceptors- that defines the basic programming model and semantics.

Simple interceptor example

`import javax.interceptor.Interceptor; ` `import javax.annotation.Priority; ' '@logged //1 This is an interceptor binding annotation used to bind our interceptor to the bean. Simply annotate a bean class @logged. ' '@priority (2020) //2Priority Enables interceptors and affects the order of interceptors. Interceptors with smaller priority values are first called. @interceptor //3 Marks the Interceptor component. Public class LoggingInterceptor {' @inject //4 Interceptor instances may be the target of dependency injection. ` `Logger logger; @aroundInvoke //5AroundInvoke denotes a method to insert a business method. ` `Object logInvocation(InvocationContext context) {` `// ... log before` `Object ret = context.proceed(); Enter the next interceptor in the interceptor chain, or invoke the intercepted business method. ` ` / /... log after` `return ret; ` ` `} ` `}Copy the code

Instances of interceptors are objects related to the Bean instances they intercept; that is, a new interceptor instance is created for each intercepted Bean.

30, Observers and Observers

Beans can also generate and consume events, interacting in a completely separate way. Any Java object can act as an event payload. The optional qualifier acts as a topic selector.

Simple event example

`class TaskCompleted {` `// ... ` `}` `@ApplicationScoped` `class ComplicatedService {` `@Inject` `Event<TaskCompleted> event; / / 1 javax.mail. Enterprise. Event. The event is used to trigger events. ` `void doSomething() {` `// ... ` `event.fire(new TaskCompleted()); //2 Synchronizes events. ` `}` `}` `@ApplicationScoped` `class Logger {` `void onTaskCompleted(@Observes TaskCompleted task) { //3TaskCompleted notifying this method when an event is triggered. ` ` / /... log the task` `}` `}`Copy the code