参考:
https://blog.csdn.net/qq_25863845/article/details/123475147
https://www.bbsmax.com/A/pRdBa7E25n/
一、单例对象依赖原型对象
springboot中大部分都是单例,如果一个单例对象中注入了另一个原型对象,那么使用Autowired得到的还是一个单例对象。
原因是单例对象只有一次对象初始化的机会,所以它只能得到第一次注入的原型对象,后面无法再改。
如下:
//car是一个单例 @Component public class Car { @Autowired private Seat seat; public Seat getSeat() { return seat; } } @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Seat { }
Seat我们定义它是一个原型模式。
测试一下,看是否符合预期
@SpringBootTest(classes= MetaCenterApplication.class) class SeatFactoryTest { @Autowired private Car car; @Test void get() { Seat king = car.getSeat(); Seat king1 = car.getSeat(); System.out.println(king); System.out.println(king1); } }
输出:
cn.jinka.gcdp.metacenter.application.metadata.Seat@12ec5e73
cn.jinka.gcdp.metacenter.application.metadata.Seat@12ec5e73
显而易见,Seat也变成单例了,那怎么办?
还好springboot给我们提供了@LookUp
我们将Car改造一下
@Component public class Car { //这个地方返回null即可,spring会帮我们实现 @Lookup public Seat getSeat(){ return null; } }
重新测试执行一下,发现已经生成了两个不同的对象:
cn.jinka.gcdp.metacenter.application.metadata.Seat@689504b1
cn.jinka.gcdp.metacenter.application.metadata.Seat@bf2f2ce
二、原型对象有多个实现类
假如我们的Seat有多种类型,真皮座椅、绒布座椅等等,这个时候可以构造一个抽象工厂类专门生成椅子。
//抽象椅子 public abstract class Seat { } //真皮座椅 @Component("SeatA") @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class SeatA extends Seat{ } //绒布座椅 @Component("SeatB") @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class SeatB extends Seat{ }
椅子工厂,每次都给汽车生产最新的椅子,@Lookup的参数就是类上面@Component的name
//抽象工厂方法 @Component public abstract class SeatFactory { @Lookup("SeatA") protected abstract Seat getSeatA(); @Lookup("SeatB") protected abstract Seat getSeatB(); }
汽车类
@Component public class Car { @Autowired private SeatFactory seatFactory; public Seat getSeat(){ return seatFactory.getSeatA(); } }
测试类
@SpringBootTest(classes= MetaCenterApplication.class) class SeatFactoryTest { @Autowired private Car car; @Test void get() { Seat king = car.getSeat(); Seat king1 = car.getSeat(); System.out.println(king); System.out.println(king1); } }
输出:
cn.jinka.gcdp.metacenter.application.metadata.SeatA@4c343088
cn.jinka.gcdp.metacenter.application.metadata.SeatA@504ccf42
可以发现,我们通过@Lookup得到了SeatA类型的实例,而且每次生成的都是新的。
备注:
@Lookup底层的实现逻辑还是通过applicationContext.getBean(ClassB.class)这种方式实现的。
@Component public class ClassA implements ApplicationContextAware { private ApplicationContext applicationContext; public void printClass() { System.out.println("This is Class A: " + this); getClassB().printClass(); } public ClassB getClassB() { return applicationContext.getBean(ClassB.class); } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }