简介
说明
本文介绍getBean()与@Autowired的对比。
ApplicationContext#getBean()与@Autowired的简要对比
项 | @Autowired | getBean() |
单例与多例 | 默认是单例。 就算是将bean加注解改为多例,此时注入仍为单例。 | 默认是单例。 但可将bean加注解改为多例,此时getBean()获取即为多例。 |
范围 | 能获得的bean范围大于getBean()的 | 少于@Autowired的 |
单例与多例
注意:这里可能是因为@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 注入成功,GetBean 方法却获取不到?!
结论
@Autowired能获得的bean范围大于ApplicationContext.getBean()的 。
问题复现
假设,想在Controller中直接使用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
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();