在Spring框架中,Bean的作用域是一个非常重要的概念。我们经常需要将一个短生命周期的Bean注入到一个长生命周期的Bean中,比如将一个Prototype作用域的Bean注入到Singleton作用域的Bean中。这种情况下,Spring提供了一种优雅的解决方案,即使用作用域代理(Scoped Proxy)。本文将通过一个具体的实例,详细讲解如何实现这种依赖注入。
- 作用域代理的原理
在Spring中,Prototype作用域的Bean每次请求都会创建一个新的实例,而Singleton作用域的Bean在整个应用生命周期中只有一个实例。当我们尝试将Prototype Bean注入到Singleton Bean中时,Spring会通过CGLIB生成一个代理对象。这个代理对象与原始的Prototype Bean具有相同的公共接口,并且每次调用方法时都会委托给一个新的Prototype Bean实例。 - 示例代码
以下是一个完整的示例,展示如何将Prototype Bean注入到Singleton Bean中。
2.1 Prototype Bean
首先定义一个Prototype作用域的Bean,用于记录当前时间。
java复制
package com.logicbig.example;
import java.time.LocalDateTime;
public class MyPrototypeBean {
private String dateTimeString = LocalDateTime.now().toString();
public String getDateTime() {
return dateTimeString;
}
}
2.2 Singleton Bean
接下来定义一个Singleton作用域的Bean,它将依赖于Prototype Bean。
java复制
package com.logicbig.example;
import org.springframework.beans.factory.annotation.Autowired;
public class MySingletonBean {
@Autowired
private MyPrototypeBean prototypeBean;
public void showMessage() {
System.out.println("Hi, the time is " + prototypeBean.getDateTime());
}
}
2.3 配置类
在配置类中,我们通过@Scope注解和proxyMode属性,将Prototype Bean的作用域设置为代理模式。
java复制
package com.logicbig.example;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
@Configuration
public class AppConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE,
proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyPrototypeBean prototypeBean() {
return new MyPrototypeBean();
}
@Bean
public MySingletonBean singletonBean() {
return new MySingletonBean();
}
public static void main(String[] args) throws InterruptedException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
MySingletonBean bean = context.getBean(MySingletonBean.class);
bean.showMessage();
Thread.sleep(1000);
bean.showMessage();
}
}
2.4 输出结果
运行上述代码,输出如下:
复制
Hi, the time is 2021-05-05T02:03:45.349
Hi, the time is 2021-05-05T02:03:46.372
可以看到,每次调用showMessage方法时,Prototype Bean都会创建一个新的实例,时间戳也随之更新。
3. 代理模式的选择
在@Scope注解中,proxyMode属性有以下两种选择:
ScopedProxyMode.TARGET_CLASS:使用CGLIB生成类代理,适用于没有接口的类。
ScopedProxyMode.INTERFACES:使用JDK动态代理,只能代理接口类型。
默认情况下,proxyMode为ScopedProxyMode.NO,即不使用代理。如果需要注入Prototype Bean到Singleton Bean中,必须明确指定代理模式。
4. 总结
通过作用域代理,Spring允许我们将Prototype Bean注入到Singleton Bean中,解决了不同生命周期Bean之间的依赖问题。这种方式在实际开发中非常有用,尤其是在需要动态创建Bean实例的场景中。希望本文的示例能帮助你更好地理解和使用Spring的这一特性。