一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
前言
Guice是我这两天看其他文章才知道的,以为是Google这两年才开源的一个框架,他们文章也并没有说具体时间,但是,Guice第一个版本早在2014年就出来了,到现在最后一个版本是在1月多,所以莫慌,不必卷,但了解一下还是有好处的,简单说Guice是一个轻量级依赖注入框架,除了依赖注入,当然也可以用它编写Web应用,只不过这方面参考文章有些少,拿他做Web应用还不如Spring,如果你有两个生命可以浪费,但是用Guice做一些非Web应用也很不错,
引入
implementation("com.google.inject:guice:5.1.0")
复制代码
依赖注入
下面来看一个简单的依赖注入实例。
class User {
@Inject
lateinit var dog: Dog
fun print() {
println(dog.eat())
}
}
abstract class Dog {
abstract fun eat()
}
class BigDog : Dog() {
override fun eat() {
println("BigDog")
}
}
class SmallDog : Dog() {
override fun eat() {
println("SmallDog")
}
}
class MainModule : AbstractModule() {
override fun configure() {
bind(Dog::class.java).to(SmallDog::class.java)
}
}
fun main() {
val injector: Injector = Guice.createInjector(MainModule())
injector.getInstance(Dog::class.java).eat()
}
复制代码
这其中使用@Inject注入对象,和Spring中的@Autowired一样,getInstance方法用来获取对象实例,和Spring不同的是,默认情况下是多实例的,也就是每次调用getInstance都返回不同的对象,和Spring还不同的是,Spring只有被你标记@Service、@Component等注解后才可以从容器中获取,而Guice不用,也就是下面这段代码,只要Test能被访问到,就可以获取。
fun main() {
val injector: Injector = Guice.createInjector()
println(injector.getInstance(Test::class.java))
}
复制代码
而想要单列,在类上加入@Singleton注解即可,另外当对一个接口获取具体实例的时候,需要继承AbstractModule,重写configure方法,告诉Guice,某个接口具体的实例是哪一个类,比如上面,我们告诉Guice,Dog的实例是SmallDog。
还可以根据name注入,如下,通过annotatedWith(Names.named("bigDog"))
为对象指定一个名字,在需要的时候,通过@Named注解指定需要哪一个对象。
class User {
@Inject
@Named("smallDog")
lateinit var dog: Dog
}
abstract class Dog {
abstract fun eat()
}
class SmallDog : Dog() {
override fun eat() {
}
}
class BigDog : Dog() {
override fun eat() {
}
}
class TestModule : AbstractModule() {
override fun configure() {
super.configure()
bind(Dog::class.java).annotatedWith(Names.named("bigDog")).to(BigDog::class.java)
bind(Dog::class.java).annotatedWith(Names.named("smallDog")).to(SmallDog::class.java)
}
}
fun main() {
val injector: Injector = Guice.createInjector(TestModule())
println(injector.getInstance(User::class.java).dog)
}
复制代码
如果需要使用自己new出来的对象,可以调用toInstance。
bind(Dog::class.java).annotatedWith(Names.named("bigDog")).toInstance(BigDog())
复制代码
AOP
在 Guice中使用AOP也很简单,如下,bindInterceptor用来配置一个拦截器,并告诉Guice对标有AopAnn注解的方法进行拦截。
abstract class Dog {
abstract fun eat()
}
open class SmallDog : Dog() {
@AopAnn
override fun eat() {
println("SmallDog")
}
}
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
internal annotation class AopAnn {}
class AOPModule : AbstractModule() {
override fun configure() {
super.configure()
bind(Dog::class.java).to(SmallDog::class.java)
bindInterceptor(Matchers.any(), Matchers.annotatedWith(AopAnn::class.java), object : MethodInterceptor {
override fun invoke(invocation: MethodInvocation): Any? {
println("拦截"+invocation.method)
return invocation.proceed()
}
})
}
}
fun main() {
val injector: Injector = Guice.createInjector(AOPModule())
injector.getInstance(Dog::class.java).eat()
}
复制代码
输出如下:
拦截public void google.guice.SmallDog.eat()
SmallDog
复制代码
注意的是,Guice方法拦截是通过在运行时生成字节码来实现的,也就是通过重写方法,那么这就对类和方法有限制,类必须为非final,方法也必须为非final,因为在final修饰下无法实现继承、重写。
在Kotlin下,默认方法和类都是final的,记得要加上open。