1、目标
本文的主要目标是学习Spring源码中Aware接口、初始化和销毁方法、@Scope注解、@Primary注解的使用
2、Aware接口
@Component
public class MyBeanAware implements BeanNameAware, ApplicationContextAware {
@Override
public void setBeanName(String name) {
System.out.println("beanName = " + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("applicationContext = " + applicationContext);
}
}
实现BeanNameAware接口,可以重写setBeanName方法
实现ApplicationContextAware接口,可以重写setApplicationContext方法
3、初始化和销毁方法
初始化方法有3种方式,依次的顺序是@PostConstruct注解指定初始化方法、实现InitializingBean接口并重写afterPropertiesSet方法、@Bean注解上指定initMethod=xxx
销毁方法有3种方式,依次的顺序是@PreDestroy注解指定销毁方法、实现DisposableBean接口并重写destroy方法、@Bean注解上指定destroyMethod=xxx
4、@Scope注解
Scope有单例singleton、多例prototype、request、session、application
单例对象中包含多例对象的属性,多次获取这个属性是是单例的吗还是多例?
结论:还是单例的,因为只会依赖注入1次多例对象
@Component
public class A {
@Autowired
private B b;
public B getB() {
return b;
}
}
A对象是单例的
@Scope(value = "prototype")
@Component
public class B {
}
B对象设置多例的,即@Scope(value=“prototype”)
@SpringBootApplication
public class Demo02Application {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo02Application.class);
A a = applicationContext.getBean("a", A.class);
System.out.println(a.getB().getClass());
System.out.println(a.getB());
System.out.println(a.getB());
System.out.println(a.getB());
}
}
多个获取A对象中的属性B对象,A对象是单例对象,B对象是多例的,但是B对象只会依赖注入1次,因此多次获取B对象仍然是同一个对象
多次获取的B对象是同一个对象,说明只会依赖注入1次
如果希望属性是多例对象的话,多次获取可以得到多个不同的对象怎么办?
① @Lazy注解:@Lazy注解会创建属性的CGLIB动态代理对象,因此每次获取这个多例对象的时候动态代理对象会创建一个新的对象
@Component
public class A {
@Lazy
@Autowired
private B b;
public B getB() {
return b;
}
}
A对象是单例对象,依赖注入的B对象是多例对象,因此用@Lazy注解进行懒加载,会创建一个CGLIB动态代理对象,每次获取实际的B对象会创建一个新的B对象
B对象是CGLIB动态代理对象,每次获取B对象都会创建一个新的B对象
② 在被代理对象上的@Scope注解上添加proxyMode是TARGET_CLASS,表示会使用CGLIB动态代理对象创建一个新的对象
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Component
public class B {
}
被代理对象B对象指定proxyMode=ScopedProxyMode.TARGET_CLASS
创建了CGLIB动态代理对象,每次获取B对象都会创建一个新的B对象
③ ObjectFactory工厂的getObject方法可以创建多个对象
@Component
public class A {
@Autowired
private ObjectFactory<B> b;
public B getB() {
return b.getObject();
}
}
依赖注入ObjectFactory工厂对象,通过getObject方法可以创建B对象
创建CGLIB动态代理对象,多次获取B对象可以得到多个B对象
④ ApplicationContext容器的getBean方法可以获取多例对象的多个对象
@Component
public class A {
@Autowired
private ApplicationContext applicationContext;
public B getB() {
return applicationContext.getBean(B.class);
}
}
依赖注入容器对象ApplicationContext对象,通过getBean方法多次获取B对象
创建CGLIB动态代理对象,每次获取B对象可以创建一个新的B对象
5、@Primary注解
@Primary 是 Spring Framework 中的一个注解,用于标记某个 Bean 作为候选 Bean 时的首选项。它的主要作用是在存在多个符合条件的 Bean 时,指定一个 Bean 为首选 Bean
如果只有一个 Bean 实现了接口或类,Spring 会自动选择这个 Bean,而不需要 @Primary
如果没有 @Primary 注解,并且有多个候选 Bean,那么在自动注入时会导致NoUniqueBeanDefinitionException 异常,Spring 会提示有多个 Bean 可用
public interface PaymentService {
void processPayment();
}
@Component
public class CreditCardPaymentService implements PaymentService {
@Override
public void processPayment() {
// Credit Card payment processing
}
}
@Component
@Primary
public class PayPalPaymentService implements PaymentService {
@Override
public void processPayment() {
// PayPal payment processing
}
}
PaymentProcessor 会自动注入 PayPalPaymentService,因为它被标记为 @Primary
@Service
@RequireArgsConstructor
public class PaymentProcessor {
private final PaymentService paymentService;
@Autowired
public PaymentProcessor(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void process() {
paymentService.processPayment();
}
}
PaymentProcessor 会自动注入 PayPalPaymentService,因为它被标记为 @Primary