0
点赞
收藏
分享

微信扫一扫

Spring--getBean()与@Autowired的对比


简介

说明

        本文介绍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();

举报

相关推荐

0 条评论