简介
说明
本文介绍getBean()与@Autowired的对比。
ApplicationContext#getBean()与@Autowired的简要对比
项 | @Autowired | getBean() |
是否触发依赖注入 | 是。 如果注入的对象还未注册到容器,则会先注册它。 | 否。 如果注入的对象还未注册到容器,不会去注册它,只会获得一个null。 |
单例与多例 | 默认是单例。 | 默认是单例。 但可将bean加注解改为多例,此时getBean()获取即为多例。 |
单例与多例
注意:这里可能是因为@Controller注入比较特殊,只有这时将bean加注解改为多例,此时注入仍为单例。不过还需要验证。
@Autowired实例
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
ApplicationContext实例
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<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
一些特殊实例对象是存放在DefaultListableBeanFactory#resolvableDependencies变量中的,在容器启动时,如果发现需要注入这些特定的实例对象,就直接在该变量中获取。通过BeanFactory#getBean方法不会去DefaultListableBeanFactory#resolvableDependencies取,所以取不到它。
处理注入的代码:findAutowireCandidates //DefaultListableBeanFactory.java
for (Map.Entry<Class<?>, 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<T> requiredType) //DefaultListableBeanFactory.java
下边的程序都是DefaultListableBeanFactory中的
getBean(Class<T> requiredType)
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<Class<?>, 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<T> requiredType)
getBean(requiredType, (Object[]) null)
resolveBean(ResolvableType.forRawClass(requiredType), args, false)
// namedBean有两个属性:beanName(String类型),beanInstance(泛型,实例对象)
NamedBeanHolder<T> namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull)
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();
请先
!