我们在编写代码时,经常用到依赖注入。在Spring中经常看到@Bean、@Service之类的注解,目的是将类转化为Bean进行有效的管理。
而经过beanFactory管理的Bean可以通过注解注入到别的实现类中。从此,我们不用关心Bean从何处来,要到何处去,如何创建,怎么销毁,上下文怎么获取等等。
Bean
主流框架大多都有这个概念。
比如我们业务实现类、拦截器、配置类、过滤器都基本都是基于Bean的概念。比如Quarkus的Bean,虽然有多个注解,用于区分不同的作用域,但是都不需要我们关心Bean的创建、注入等等细节,Quarkus简化了Bean的发现,但是Quarkus 鼓励用户不要在 bean 中使用私有成员。
Bean依赖注入
Bean被有效的管理了,那么实现依赖注入将是一件非常简单的事情。
@ApplicationScoped
public class BrandInfoRepository extends ResourceRepository<BrandInfo> {
}
@ApplicationScoped 是最常用的注解,将Bean交给容器管理。
注入的方式比较多,但是在注入时,Quarkus不推荐使用private,一般可以用package-private修饰符。
@ApplicationScoped
public class BrandInfoResource extends BaseResource {
@Inject
BrandInfoRepository brandInfoRepository;
}
这也是比较推荐的方式,通过@Inject进行注入。
另外也可以通过构造函数进行注入,这时候就不需要@Inject注解了,但是这时候修饰符可以使用private。
@ApplicationScoped
public class BrandInfoResource extends BaseResource {
private BrandInfoRepository brandInfoRepository;
public BrandInfoResource(BrandInfoRepository brandInfoRepository) {
this.brandInfoRepository = brandInfoRepository;
}
}
Quarkus依赖注入的方式其实比较多的,但是一般常用的还是@Inject注解的方式。
注解
@ApplicationScoped
javax.enterprise.context.ApplicationScoped是我们最经常用到的注解。被注解的Bean在应用系统中是单例的,同时是惰性创建的。
@Target({ TYPE, METHOD, FIELD })
@Retention(RUNTIME)
@Documented
@NormalScope
@Inherited
public @interface ApplicationScoped {
/**
* Supports inline instantiation of the {@link ApplicationScoped} annotation.
*
* @author Martin Kouba
* @since 2.0
*/
public final static class Literal extends AnnotationLiteral<ApplicationScoped> implements ApplicationScoped {
public static final Literal INSTANCE = new Literal();
private static final long serialVersionUID = 1L;
}
}
@Singleton
javax.inject.Singleton也是常用的注解,与ApplicationScoped的区别是立即创建了实例。
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
@RequestScoped
javax.enterprise.context.RequestScoped的作用域限制在了当前请求。
@Target({ TYPE, METHOD, FIELD })
@Retention(RUNTIME)
@Documented
@NormalScope
@Inherited
public @interface RequestScoped {
/**
* Supports inline instantiation of the {@link RequestScoped} annotation.
*
* @author Martin Kouba
* @since 2.0
*/
public final static class Literal extends AnnotationLiteral<RequestScoped> implements RequestScoped {
public static final Literal INSTANCE = new Literal();
private static final long serialVersionUID = 1L;
}
}
@Dependent
javax.enterprise.context.Dependent这些实例不共享,并且每个注入点都会生成一个新的依赖Bean实例。
bean的生命周期与注入它的bean绑定,将与注入它的bean一起创建和销毁它。
@Target({ METHOD, TYPE, FIELD })
@Retention(RUNTIME)
@Documented
@Scope
@Inherited
public @interface Dependent {
/**
* Supports inline instantiation of the {@link Dependent} annotation.
*
* @author Martin Kouba
* @since 2.0
*/
public final static class Literal extends AnnotationLiteral<Dependent> implements Dependent {
public static final Literal INSTANCE = new Literal();
private static final long serialVersionUID = 1L;
}
}
@SessionScoped
javax.enterprise.context.SessionScoped配合quarkus-undertow使用的,作用域是当前Session。
选用合适的注解
主要是针对ApplicationScoped和Singleton,ApplicationScoped是延迟创建的,而Singleton是立即创建实例。
Singleton通常有更好的性能,因为是直接调用的,省去了Proxy,自然也不需要将上下文委托给当前实例的Proxy。
但是Singleton不能通过QuarkusMock来模拟bean。
一般来说,除非有充分的理由使用ApplicationScoped,否则我们建议默认情况下坚持使用Singleton,以获取更好的性能。