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

文章目录

  • Q1、如何让自动注入没有找到依赖Bean时不报错?
  • Q2、如何让自动注入找到多个依赖的Bean时不报错?
  • Q3、@Autowired注解有什么作用?
  • Q4、@Autowired和@Resource之间的区别
  • Q5、@Autowired注解自动装配的过程是怎样的?
  • Q6、@Configuration的作用及解析原理
  • Q7、@Bean的方法调用是怎么保证单例的?
  • Q8、要将一个第三方的类配成Bean有哪些方式?
  • Q9、为什么@ComponentScan不设置basePackage也会扫描?

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

答案:

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

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

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

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

@Primary
@Component
....

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; }

答案:

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

  • 先按类型注入,即byType
  • 如果类型匹配到了多个,则按名字注入,byName
  • 因此,不用额外提供属性的get和set方法
  • 搭配@Qualifier和@Primary解决多个同类型的Bean的问题
//在DI的地方指定bean的名字
@Service
public class UserService {

    @Autowired
    @Qualifier("user1")
    private IUser user;
}

//在Bean定义的地方加一个优先主要的声明
@Primary
@Service
public class User1 implements IUser{
    @Override
    public void say() {
    }
}

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第1张图片

在构造方法上用@Autowired

这种写法会报错:

public class TraceConfig {

	@Autowired
	private Info info;

    private final AppConfig appConfig;

    public TraceConfig(Info info) {
        this.appConfig = info;
    }

	//....
}

正确写法:

public class TraceConfig {

    private final AppConfig appConfig;

    @Autowired
    public TraceConfig(Info info) {
        this.appConfig = info;
    }

	//....
}

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

//构造器注入
public Class CarFactory{

	private Tank tank;

	public CarFactory(Tank tank) {  //按构造函数形参的类型去装配,byType
		this.tank = tank;  //按构造函数来自动装配
	}
}

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第2张图片

在普通方法上加Autowired注解

@Service
public class UserService {

    @Autowired
    public void init() {
       //逻辑
    }
}

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

在set方法上加Autowired注解

@Service
public class UserService {

    private UserDao user;

    @Autowired
    public void setUser(UserDao user) {
        this.user = user;
    }
}

@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);
    }
}

//普通方法的参数
@Service
public class UserService {

    public void test(@Autowired IUser user) {
       user.say();
    }
}

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

@Service
public class UserService {

    @Autowired
    private List<IUser> userList;
    //...

}

此时@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

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第3张图片

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

答案:

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

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第4张图片

关于解析原理:

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

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

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

答案:

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

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第5张图片

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

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

答案:

第一种:使用@Bean

@Configuration
public class MainConfig{
	@Bean
	public DruidDataSource dataSource(){
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setUrl("xxxx");
		return dataSource;
	}
}

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

@Import(DruidDataSource.class)
@Configuration
public class MainConfig{

}

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

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第6张图片

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

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第7张图片

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

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

答案:

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

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第8张图片

涉及的Spring解析的源码:

【Spring面试】六、@Autowired、@Configuration、第三方Bean的配置_第9张图片

你可能感兴趣的:(面试,spring,面试,java)