轻量级依赖注入、AOP库Guice示例

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

前言

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。

- END -


原文始发于微信公众号(十四个字节):轻量级依赖注入、AOP库Guice示例