答案:
@Autowired(required = false) //默认为true
private Role role;
...
error:expected single matching bean but found 2....
给优先使用的Bean加注解@Primary
@Primary
@Component
....
// 可以标注在构造器、方法、参数、字段、注解类型(做为元注解)上
@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)的方式之一。特性:
搭配@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对象收集到集合中。
答案:
@Autowired是通过Bean的后置处理器进行解析的,在创建一个Spring上下文的时候在构造函数中进行注册AutowiredAnnotationBeanPostProcessor
,然后在Bean的创建过程中进行解析:
实例化后进行预解析
,即解析@Autowired标注的属性、方法,比如把属性的类型、名称、所在的类等元数据缓存起来在属性注入这一步进行真正的解析
,即拿到上面缓存的元数据,去IoC容器中查找并返回注入在根据元数据去容器中查找时:
答案:
关于解析原理:
调用ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry进行解析配置
(解析配置类说白就是去解析各种注解(@Bean @Confiquration@lmport @Component… 就是注BeanDefinition)ConfigurationClassPostProcessor.postProcessBeanFactory
去创建cglib动态代理//同一个问题:
@Configuration加与不加的区别是什么?
答案:
既然是第三方的类,那你总不能去他们代码中加一个@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的生产)
正常用法中,@ComponentScan设置扫描包的地址,然后Spring去扫描包下所有类中带@Component注解的类,并注册为BeanDefinition
答案:
因为Spring在解析@ComponentScan的时候,拿到basePackage,如果没有设置,则将你的类所在的包路径做为扫描包的路径。
涉及的Spring解析的源码: