所有分类
  • 所有分类
  • 未分类

Spring-getBean()与@Autowired的对比

简介

说明

本文介绍getBean()与@Autowired的对比。

ApplicationContext#getBean()与@Autowired的简要对比

@AutowiredgetBean()
是否触发依赖注入是。 如果注入的对象还未注册到容器,则会先注册它。否。 如果注入的对象还未注册到容器,不会去注册它,只会获得一个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(); ​

0

评论2

请先

  1. @Autowired 是否触发依赖注入,引入service层为什么也会有null的时候
    珠光2023 2024-09-12 0
    • @Autowired会出发依赖注入。引入Service正常不会有null,如果有null,大概率是生命周期有问题,service可能被干预了,可以把代码贴出来看看
      自学精灵 2024-09-13 0
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录