spring IOC默认的组件都是单例,那对于非单例的组件,需要通过注解:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
那么当一个单例组件A,注入了非单例的组件B:
@Component
class A {
@Autowride
private B b;
public void printB() {
System.out.println(b.toString());
}
}
@Compnent
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
class B {
}
因为A是单例,所以注入的属性B相当于也是单例了。即:A中的B是固定不变的了,每次调用a.printB,输出b都是一样的。那么如果想要每次调用a.printB,输出b都不一样,就需要使用代理proxyMode
@Compnent
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
class B {
}
这样每次调用b.toString()的时候就会通过代理new一个b。这个文章解释的很好:
This is another way to inject a short-lived scoped bean into long-lived scoped bean. For example injecting a prototype bean into a singleton bean.
We need to inject a proxy object that exposes the same public interface as the original scoped object.
Spring uses CGLIB to create the proxy object.
The proxy object delegates method calls to the real object.
Each access of underlying prototype object causes a new object to be created.
Example
Prototype bean
package com.logicbig.example;
import java.time.LocalDateTime;
public class MyPrototypeBean {
private String dateTimeString = LocalDateTime.now().toString();
public String getDateTime() {
return dateTimeString;
}
}
Singleton bean
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());
}
}
Main class Configuring the Scoped Proxy for Prototype bean
package com.logicbig.example;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.*;
@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();
}
}
Output
Hi, the time is 2021-05-05T02:03:45.349
Hi, the time is 2021-05-05T02:03:46.372
In above example proxyMode=ScopedProxyMode.TARGET_CLASS
causes an AOP proxy to be injected at the target injection point. The default proxyMode is ScopedProxyMode.NO
.
Another possible value, ScopedProxyMode.INTERFACES
creates JDK dynamic proxy (instead of class based CGLIB proxy) which can only take the target bean's interface types.