• 【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置


    Q1、如何让自动注入没有找到依赖Bean时不报错?

    答案:

    @Autowired(required = false)   //默认为true
    private Role role;
    ...
    
    • 1
    • 2
    • 3

    Q2、如何让自动注入找到多个依赖的Bean时不报错?

    error:expected single matching bean but found 2....
    
    • 1

    给优先使用的Bean加注解@Primary

    @Primary
    @Component
    ....
    
    • 1
    • 2
    • 3

    Q3、@Autowired注解有什么作用?

    // 可以标注在构造器、方法、参数、字段、注解类型(做为元注解)上
    @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    // 运行时注解
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Autowired {
    
    	/**
    	 * Declares whether the annotated dependency is required.
    	 * 声明该注解标注的依赖是否需要一定存在于Spring容器中
    	 * 				true为必须存在,如果不存在的话就抛出NoSuchBeanDefinitionException异常
    	 *				false不要求必须存在,如果不存在也不抛出异常(一般不建议设置,可能会引发线上事故)
    	 * 

    Defaults to {@code true}. */ boolean required() default true; }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    答案:

    Spring依赖注入(DI)的方式之一。特性:

    • 先按类型注入,即byType
    • 如果类型匹配到了多个,则按名字注入,byName
    • 因此,不用额外提供属性的get和set方法
    • 搭配@Qualifier和@Primary解决多个同类型的Bean的问题
    //在DI的地方指定bean的名字
    @Service
    public class UserService {
    
        @Autowired
        @Qualifier("user1")
        private IUser user;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //在Bean定义的地方加一个优先主要的声明
    @Primary
    @Service
    public class User1 implements IUser{
        @Override
        public void say() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    在构造方法上用@Autowired

    这种写法会报错:

    public class TraceConfig {
    
    	@Autowired
    	private Info info;
    
        private final AppConfig appConfig;
    
        public TraceConfig(Info info) {
            this.appConfig = info;
        }
    
    	//....
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    正确写法:

    public class TraceConfig {
    
        private final AppConfig appConfig;
    
        @Autowired
        public TraceConfig(Info info) {
            this.appConfig = info;
        }
    
    	//....
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第一种写法报错的原因是加载的顺序问题,@Autowired写在变量上的注入,要等到变量所在类完全加载完才注入,因此变量appConfig的加载要早于Info这个Bean,给appConfig赋值时,还没有注入。最后,在构造器上加Autowired注解,实际上还是使用了Autowired装配方式,并非构造器装配。

    //构造器注入
    public Class CarFactory{
    
    	private Tank tank;
    
    	public CarFactory(Tank tank) {  //按构造函数形参的类型去装配,byType
    		this.tank = tank;  //按构造函数来自动装配
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    在普通方法上加Autowired注解

    @Service
    public class UserService {
    
        @Autowired
        public void init() {
           //逻辑
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    spring会在项目启动的过程中,自动调用一次加了@Autowired注解的方法,我们可以在该方法做一些初始化的工作

    在set方法上加Autowired注解

    @Service
    public class UserService {
    
        private UserDao user;
    
        @Autowired
        public void setUser(UserDao user) {
            this.user = user;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    @Autowired标注可以放在成员变量上,也可以放在成员变量的set方法上。前者,Spring会直接将UserDao类型的唯一一个bean赋值给userDao这个成员变量;后者,Spring会调用setUserDao方法来将UserDao类型的唯一一个bean装配到userDao这个属性。

    @Autowired用在方法的参数上

    //构造方法的参数
    @Service
    public class UserService {
    
        private IUser user;
    
        public UserService(@Autowired IUser user) {
            this.user = user;
            System.out.println("user:" + user);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //普通方法的参数
    @Service
    public class UserService {
    
        public void test(@Autowired IUser user) {
           user.say();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    @Autowired注入一个同类型所有Bean的list

    @Service
    public class UserService {
    
        @Autowired
        private List<IUser> userList;
        //...
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    此时@Autowired会自动把相同类型的IUser对象收集到集合中。

    Q4、@Autowired和@Resource之间的区别

    答案:

    • @Autowired是Spring提供的,@Resource是JDK提供的,不与框架强绑定
    • @Autowired是先按类型匹配,匹配到多个则按名字;@Resource默认按照名字去匹配,没匹配到则按类型去匹配

    Q5、@Autowired注解自动装配的过程是怎样的?

    @Autowired是通过Bean的后置处理器进行解析的,在创建一个Spring上下文的时候在构造函数中进行注册AutowiredAnnotationBeanPostProcessor,然后在Bean的创建过程中进行解析:

    • 实例化后进行预解析,即解析@Autowired标注的属性、方法,比如把属性的类型、名称、所在的类等元数据缓存起来
    • 在属性注入这一步进行真正的解析,即拿到上面缓存的元数据,去IoC容器中查找并返回注入

    在根据元数据去容器中查找时:

    • 只找到一个,则将这个Bean装配给@Autowired指定的属性
    • 找到不止一个,则根据名称来查找
    • 一个也没找到,则throw exception,除非required=false

    在这里插入图片描述

    Q6、@Configuration的作用及解析原理

    答案:

    • @Confiruration注解是用来代替xml配置方式下的spring.xml文件的()
    • 但没有@Configuration也可以配置@Bean
    • 加了@Configuration注解会为配置类创建cglib动态代理,保证了配置类的内部方法之间依赖调用时都从容器中获取bean

    在这里插入图片描述

    关于解析原理:

    • 在创建Spring上下文的时候会注册一个解析配置处理器ConfigurationClassPostProcessor(实现BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor)
    • 在调用invokeBeanFactoyPostProcessor,就会去调用ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry进行解析配置(解析配置类说白就是去解析各种注解(@Bean @Confiquration@lmport @Component… 就是注BeanDefinition)
    • ConfigurationClassPostProcessor.postProcessBeanFactory去创建cglib动态代理

    Q7、@Bean的方法调用是怎么保证单例的?

    //同一个问题:
    @Configuration加与不加的区别是什么?
    
    • 1
    • 2

    答案:

    • 如果希望@Bean的方法返回是对象是单例,需要在类上面加上@Configuration
    • Spring 底层会为@Configuration(会在invokeBeanFactoryPostProcessor 通过内置BeanFactoryPostProcessor)生成CGLIB动态代理
    • 当@Bean方法进行互调时,则会通过CGLIB进行增强,通过调用的方法名作为Bean的名称去IoC容器中获取,进而保证了@Bean方法的单例

    在这里插入图片描述

    Q8、要将一个第三方的类配成Bean有哪些方式?

    既然是第三方的类,那你总不能去他们代码中加一个@Component的注解,所以:

    答案:

    第一种:使用@Bean

    @Configuration
    public class MainConfig{
    	@Bean
    	public DruidDataSource dataSource(){
    		DruidDataSource dataSource = new DruidDataSource();
    		dataSource.setUrl("xxxx");
    		return dataSource;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    第二种:使用@Import,但使用这种方式是不能像上面的@Bean一样干预这个对象的创建过程的,因为Spring直接反射+new Instance

    @Import(DruidDataSource.class)
    @Configuration
    public class MainConfig{
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果想操作Bean的创建,可以通过用@Import的另一种用法:

    在这里插入图片描述

    第三种:通过扩展接口BeanDefinitionRegistryPostProcessor(创建BeanDefinition的扩展接口都可以用来操作Bean的生产)

    在这里插入图片描述

    Q9、为什么@ComponentScan不设置basePackage也会扫描?

    正常用法中,@ComponentScan设置扫描包的地址,然后Spring去扫描包下所有类中带@Component注解的类,并注册为BeanDefinition

    答案:

    因为Spring在解析@ComponentScan的时候,拿到basePackage,如果没有设置,则将你的类所在的包路径做为扫描包的路径。

    在这里插入图片描述

    涉及的Spring解析的源码:

    在这里插入图片描述

  • 相关阅读:
    C语言中,“>>=”;“<<=”;“&=”;“|=”分别代表什么
    【VASP】KPOINTS文件介绍
    IPWorks S/MIME Delphi Edition
    EN 1935建筑五金.单轴铰链—CE认证
    java中的泛型
    git入门
    Nginx基本概念
    2.1.C++项目:网络版五子棋对战之前置知识
    低代码(low-code)又又又出圈了
    Spring Boot入门教程
  • 原文地址:https://blog.csdn.net/llg___/article/details/132804792