@Configuration 注解的用途

why

Spring实战中说:

默认情况下,Spring中的bean都是单例的,我们并没有必要创建第二个完全相同(甚至可能不同,由@Bean注解的方法提供)的Bean实例。


......

借助JavaConfig实现注入

看起来,CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。
因为sgtPeppers()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。

@Configuration

现在我们知道为什么有以下几个规则的原因了:

  1. @Configuration注解的类都必须是open类(类可以隐式地子类化,并且不能是最终的);
  2. @Configuration中的@Bean方法必须可重写;
  3. 为什么有@Configuration注解,明明@Component一样可以配置Bean;

因为@Bean方法会被代理拦截!

@Configuration
open class A : InitializingBean, ApplicationContextAware {
    @Bean open fun getStr(): String { // 该方法实际只会被调用一次
        println("@Bean方法被调用")
        return "Hello"
    }

    var ctx: ApplicationContext? = null
    override fun afterPropertiesSet() {
        var value = ctx!!.getBean(String::class.java)
        println("bean = $value")
        value = getStr() // getStr被代理!
        println("bean = $value")
        value = getStr()
        println("bean = $value")
    }

    override fun setApplicationContext(applicationContext: ApplicationContext) {
        this.ctx = applicationContext
    }
}

输出是:

@Bean方法被调用
bean = Hello
bean = Hello
bean = Hello

@Bean方法确实只被调用了一次,实在是匪夷所思。

测试

package test

import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import java.util.*

@RunWith(SpringJUnit4ClassRunner::class)
@ContextConfiguration(classes = [TestConfigBean::class])
@ComponentScan
class TestConfigBean {
    @Autowired lateinit var ctx: ApplicationContext

    @Test fun `时间戳正确性`() {
        val ta = currentTimeStamp()
        val tb = currentTimeStamp()
        println(ta)
        println(tb)
        Assert.assertNotEquals(ta, tb)
    }

    @Test fun `@Bean会被代理`() {
        val beanA = ctx.getBean(BeanA::class.java)
        println(beanA.productBeanA())
        println(beanA.productBeanA())
        Assert.assertEquals(beanA.productBeanA(), beanA.productBeanA())
    }
}

@Configuration    // <------- 此处如果换成@Component或者@Service,测试都会失败!说明只有使用@Configuration注解的类才会被代理
open class BeanA {
    @Bean
    open fun productBeanA(): TimeStamp {
        println("only once!")
        return currentTimeStamp()
    }
}

data class TimeStamp(val value: Long)
fun currentTimeStamp(): TimeStamp {
    Thread.sleep(1)
    return TimeStamp(Date().time)
}
posted @ 2021-03-09 16:46  develon  阅读(500)  评论(0编辑  收藏  举报