原文网址:Spring--getBean()与@Autowired的对比_IT利刃出鞘的博客-CSDN博客
说明
本文介绍getBean()与@Autowired的对比。
ApplicationContext#getBean()与@Autowired的简要对比
项 | @Autowired | getBean() |
是否触发依赖注入 | 是。 如果注入的对象还未注册到容器,则会先注册它。 | 否。 如果注入的对象还未注册到容器,不会去注册它,只会获得一个null。 |
单例与多例 | 默认是单例。 就算是将bean加注解改为多例,此时注入仍为单例。 | 默认是单例。 但可将bean加注解改为多例,此时getBean()获取即为多例。 |
注意:这里可能是因为@Controller注入比较特殊,只有这时将bean加注解改为多例,此时注入仍为单例。不过还需要验证。
- package com.example.controller;
-
- import com.example.service.UserService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- @RequestMapping("/hello")
- public class HelloController {
- @Autowired
- private UserService userService;
-
- @GetMapping("/test1")
- public String test1() {
- return userService.toString();
- }
-
- @GetMapping("/test2")
- public String test2() {
- return userService.toString();
- }
- }
- package com.example.service;
-
- import org.springframework.stereotype.Component;
-
- @Component
- public class UserService {
- }
测试(都是单例的)
http://localhost:8080/test1 结果:com.example.service.UserService@27a70937
http://localhost:8080/test2 结果:com.example.service.UserService@27a70937
再次http://localhost:8080/test2 结果:com.example.service.UserService@27a70937
将UserService改为多例
- package com.example.service;
-
- import org.springframework.beans.factory.config.ConfigurableBeanFactory;
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Component;
-
- @Component
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- public class UserService {
- }
测试(还是单例的)
http://localhost:8080/test1 结果:com.example.service.UserService@12d4c297
http://localhost:8080/test2 结果:com.example.service.UserService@12d4c297
再次http://localhost:8080/test2 结果:com.example.service.UserService@12d4c297
- package com.example.controller;
-
- import com.example.service.UserService;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class HelloController implements ApplicationContextAware {
- private ApplicationContext applicationContext;
-
- @GetMapping("/test1")
- public String test1() {
- UserService userService = applicationContext.getBean(UserService.class);
- return userService.toString();
- }
-
- @GetMapping("/test2")
- public String test2() {
- UserService userService = applicationContext.getBean(UserService.class);
- return userService.toString();
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
- }
- package com.example.service;
-
- import org.springframework.stereotype.Component;
-
- @Component
- public class UserService {
- }
测试(都是单例的)
http://localhost:8080/test1 结果:com.example.service.UserService@27a70937
http://localhost:8080/test2 结果:com.example.service.UserService@27a70937
再次http://localhost:8080/test2 结果:com.example.service.UserService@27a70937
- package com.example.service;
-
- import org.springframework.beans.factory.config.ConfigurableBeanFactory;
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Component;
-
- @Component
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- public class UserService {
- }
将UserService改为多例
- package com.example.service;
-
- import org.springframework.beans.factory.config.ConfigurableBeanFactory;
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Component;
-
- @Component
- @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
- public class UserService {
- }
测试(是多例了)
http://localhost:8080/test1 结果:com.example.service.UserService@393b1c
http://localhost:8080/test2 结果:com.example.service.UserService@23c2e5c2
再次http://localhost:8080/test2 结果:com.example.service.UserService@5e743c55
结论
@Autowired可以触发依赖注入,ApplicationContext.getBean()不能触发依赖注入。
也就是说:如果要获取的那个Bean还不存在,@Autowired方式可以去转而实例化那个对象,从而能获取Bean;ApplicationContext.getBean()不能转而去实例化对象,只能获得null。
目标:在Controller中注入ApplicationEventPublisher(Spring的事件发布器)。
本处使用@Autowired是可以获取的,但使用ApplicationContext.getBean()就不可以。
@Autowired注入的方式
- package com.example.controller;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.ApplicationEventPublisher;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class HelloController {
- @Autowired
- private ApplicationEventPublisher applicationEventPublisher;
-
- @GetMapping("/test1")
- public String test1() {
- System.out.println(applicationEventPublisher);
- return "test1 success";
- }
- }
访问:http://localhost:8080/test1
结果(成功注入)
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@5bf0fe62, started on Thu Mar 18 19:10:33 CST 2021
ApplicationContext.getBean()方式
- package com.example.controller;
-
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.context.ApplicationEventPublisher;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class HelloController implements ApplicationContextAware {
- private ApplicationEventPublisher applicationEventPublisher;
-
- @GetMapping("/test1")
- public String test1() {
- System.out.println(applicationEventPublisher);
- return "test1 success";
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- applicationEventPublisher = applicationContext.getBean(ApplicationEventPublisher.class);
- }
- }
启动时就报错(找不到这个bean)
- Description:
-
- A component required a bean of type 'org.springframework.context.ApplicationEventPublisher' that could not be found.
-
-
- Action:
-
- Consider defining a bean of type 'org.springframework.context.ApplicationEventPublisher' in your configuration.
refresh()// AbstractApplicationContext
prepareBeanFactory //AbstractApplicationContext
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// DefaultListableBeanFactory类(实现了ConfigurableListableBeanFactory接口)
registerResolvableDependency(Class> dependencyType, @Nullable Object autowiredValue)
resolvableDependencies.put(dependencyType, autowiredValue); //DefaultListableBeanFactory类
private final Map
一些特殊实例对象是存放在DefaultListableBeanFactory#resolvableDependencies变量中的,在容器启动时,如果发现需要注入这些特定的实例对象,就直接在该变量中获取。通过BeanFactory#getBean方法不会去DefaultListableBeanFactory#resolvableDependencies取,所以取不到它。
处理注入的代码:findAutowireCandidates //DefaultListableBeanFactory.java
- for (Map.Entry
, Object> classObjectEntry : this.resolvableDependencies.entrySet()) { - Class> autowiringType = classObjectEntry.getKey();
- if (autowiringType.isAssignableFrom(requiredType)) {
- Object autowiringValue = classObjectEntry.getValue();
- autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
- if (requiredType.isInstance(autowiringValue)) {
- result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
- break;
- }
- }
- }
编写程序复现
- package com.example.tmp;
-
- import org.springframework.beans.BeansException;
- import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
- import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
- import org.springframework.stereotype.Component;
-
- @Component
- public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
- @Override
- public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
- beanFactory.registerResolvableDependency(MyBean.class, new MyBean());
- }
- }
- package com.example.tmp;
-
- public class MyBean {
- }
使用@Autowired注入
- package com.example.controller;
-
- import com.example.tmp.MyBean;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class HelloController {
- @Autowired
- private MyBean myBean;
-
- @GetMapping("/test1")
- public String test1() {
- System.out.println(myBean);
- return "test1 success";
- }
-
- }
访问:http://localhost:8080/test1
后台结果(成功注入)
com.example.tmp.MyBean@f08fdce
使用ApplicationContext#getBean()
- package com.example.controller;
-
- import com.example.tmp.MyBean;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class HelloController implements ApplicationContextAware {
- private MyBean myBean;
-
- @GetMapping("/test1")
- public String test1() {
- System.out.println(myBean);
- return "test1 success";
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- myBean = applicationContext.getBean(MyBean.class);
- }
- }
启动即报错(无法获得bean)
- Description:
-
- A component required a bean of type 'com.example.tmp.MyBean' that could not be found.
-
-
- Action:
-
- Consider defining a bean of type 'com.example.tmp.MyBean' in your configuration.
追踪1:追踪“编写程序复现”的getBean
getBean(MyBean.class)
getBeanFactory().getBean(requiredType) //AbstractApplicationContext.class
getBean(Class
下边的程序都是DefaultListableBeanFactory中的
getBean(Class
getBean(requiredType, (Object[]) null)
resolveBean(ResolvableType.forRawClass(requiredType), args, false)
resolveNamedBean(requiredType, args, nonUniqueAsNull)
getBeanNamesForType(requiredType);
getBeanNamesForType(type, true, true)
getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit)
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
追踪2:正常的bean通过ApplicationContext.getBean()获取
把上边的程序稍微改造:
- package com.example.tmp;
-
- import org.springframework.stereotype.Component;
-
- @Component
- public class MyBean {
- }
- package com.example.controller;
-
- import com.example.tmp.MyBean;
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- @RestController
- public class HelloController implements ApplicationContextAware {
- private MyBean myBean;
-
- @GetMapping("/test1")
- public String test1() {
- System.out.println(myBean);
- return "test1 success";
- }
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- myBean = applicationContext.getBean(MyBean.class);
- }
- }
追踪它,跟上边一样的,也是追踪到了DefaultListableBeanFactory#getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit),但这是数据有所变化
- public String[] getBeanNamesForType(@Nullable Class> type, boolean includeNonSingletons, boolean allowEagerInit) {
- if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
- return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
- }
- // includeNonSingletons为true,会使用this.allBeanNamesByType
- //此时,cache一项都没有
- Map
, String[]> cache = - (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
- // type:“class com.example.tmp.MyBean”,此步resolvedBeanNames为null
- String[] resolvedBeanNames = cache.get(type);
- // 此判断为false,不会进入
- if (resolvedBeanNames != null) {
- return resolvedBeanNames;
- }
- //通过type获得bean的名字。结果为:myBean
- resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
- if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
- cache.put(type, resolvedBeanNames);
- }
- return resolvedBeanNames;
- }
与“追踪1:追踪“编写程序复现”的getBean”的区别在于:doGetBeanNamesForType获取到了bean的名字(也就是说,找到了bean)
总体流程
getBean(Class
getBean(requiredType, (Object[]) null)
resolveBean(ResolvableType.forRawClass(requiredType), args, false)
// namedBean有两个属性:beanName(String类型),beanInstance(泛型,实例对象)
NamedBeanHolder
String[] candidateNames = getBeanNamesForType(requiredType);
getBeanNamesForType(type, true, true)
getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit)
resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
// 调用到AbstractBeanFactory#getBean
return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
// 获得MyBean实例对象
namedBean.getBeanInstance();