文章目录
- 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不要求必须存在,如果不存在也不抛出异常(一般不建议设置,可能会引发线上事故)
* <p>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() {
}
}
在构造方法上用@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; //按构造函数来自动装配
}
}
在普通方法上加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
Q6、@Configuration的作用及解析原理
答案:
- @Confiruration注解是用来代替xml配置方式下的spring.xml文件的(<bean>)
- 但没有@Configuration也可以配置@Bean
- 加了@Configuration注解会为配置类创建cglib动态代理,保证了配置类的内部方法之间依赖调用时都从容器中获取bean
关于解析原理:
- 在创建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方法的单例
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的另一种用法:
第三种
:通过扩展接口BeanDefinitionRegistryPostProcessor(创建BeanDefinition的扩展接口都可以用来操作Bean的生产)
Q9、为什么@ComponentScan不设置basePackage也会扫描?
正常用法中,@ComponentScan设置扫描包的地址,然后Spring去扫描包下所有类中带@Component注解的类,并注册为BeanDefinition
答案:
因为Spring在解析@ComponentScan的时候,拿到basePackage,如果没有设置,则将你的类所在的包路径做为扫描包的路径。
涉及的Spring解析的源码: