Ioc容器
Spring IoC容器是一个管理Bean的容器,在Spring的定义中,它要求所有的IoC容器都需要实现接口BeanFactory,它是一个顶级容器接口。 他的代码内容如下
public interface BeanFactory {
//前缀
String FACTORY_BEAN_PREFIX = "&";
//多个获取bean的方法
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
//是否包含bean
boolean containsBean(String name);
//bean是否是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//bean是否是原型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//是否类型匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
//获取bean的别名
String[] getAliases(String name);
}
这里面有很多获取bean的方法也就是getbean(),根据参数的不同,获取的方法也会有所不同。比如通过名字获取,通过类型获取。isSingleton方法用于判断bean是否是单例模式,在spring中bean默认是单例模式。与isSingleton方法相反的是isPrototype方法,如果它返回的是true,那么当我们使用getBean方法获取Bean的时候,Spring IoC容器就会创建一个新的Bean返回给调用者。
由于beanfactory还不够强大,所以又设计了一个更高级的接口ApplicationContext,他是beanfactory的子接口之一。在现实使用中大部分都是通过ApplicationContext来实现的。ApplicationContext还继承了多个接口所以实现的功能更多也更强大。
bean的获取
我们要将bean放入到ioc容器中才能使用。在深入浅出springbot一书中使用基于注解的方法来讲解的。
有一个基于注解的IOC容器:AnnotationConfigApplicationContext。它装配和获取bean的方法和springboot的方法如出一辙。
先定义一个实体类:
package com.springboot.chapter3.pojo;
public class User {
private Long id;
private String userName;
private String note;
/**setter and getter **/
}
然后又定义一个配置文件
@Configuration
public class AppConfig
{
@Bean(name = "user")
public User initUser() {
User user = new User();
user.setId(1L);
user.setUserName("user_name_1");
user.setNote("note_1");
return user;
}
}
@Configuration表示这是一个java配置类,spring会根据它来生成IOC容器去配置bean。
@Bean,表示将注解的方法的返回值装配到Ioc容器中,name属性bean的名称,没有设置则以方法名为bean的名称。
之后就可以使用AnnotationConfigApplicationContext来构建自己的IoC容器。
public class IoCTest {
private static Logger log = Logger.getLogger(IoCTest.class);
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
log.info(user.getId());
}
}
这里获取bean的方法就是将配置类加载进Ioc容器中,然后再通过Ioc容器获取bean。(这不是唯一的方法)
装配bean
从上面我们可以看到能使用@Bean注解将bean放入到Ioc容器中,但如果所有的bean都使用这种方法在实际的实现中是十分麻烦的,好在我们可以进行扫描获取bean装配到容器中,具体的扫描注解有:@Component和@ComponentScan。@Component是标明哪个类被扫描进入Spring IoC容器,而@ComponentScan则是标明采用何种策略去扫描装配Bean。
@Component("user")
public class User {
@Value("1")
private Long id;
@Value("user_name_1")
private String userName;
@Value("note_1")
private String note;
/**setter and getter **/
}
注解@Component表明这个类将被Spring IoC容器扫描装配,bean的名称就是括号内的字段,如果没有配置那么bean的名称就是类名(第一个字母小写)。@Value则是指定具体的值,使得Spring IoC给予对应的属性注入对应的值。
为了使该类被扫描到,我们还需要改造类AppConfig。
@Configuration
@ComponentScan
public class AppConfig { }
使用@ComponentScan,说明会进行扫描,但是它只会扫描类AppConfig所在的当前包和其子包,所以不加额外配置时要注意。
运行使用:
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
log.info(user.getId());
上面的情况下只能扫描本包和子包,在实际的使用中如果这样就可能将一个类迁移到不属于它的包中了,这就不太合理了,所以@ComponentScan可以自定义扫描路径。
@ComponentScan("com.springboot.chapter3.*")
@ComponentScan(basePackages = {"com.springboot.chapter3.pojo"})
@ComponentScan(basePackageClasses = {User.class})
当然,上面的使用可能会有误伤的情况,所以我们可以设置一些忽略的参数
@ComponentScan(basePackages = "com.springboot.chapter3.*",
//使标注了@Service的类将不被IoC容器扫描注入
excludeFilters = {@Filter(classes = {Service.class})})
依赖注入
依赖注入依靠@Autowired
其使用规则如下:首先它会根据类型找到对应的Bean,如果对应类型的Bean不是唯一的,那么它会根据其属性名称和Bean的名称进行匹配。如果匹配得上,就会使用该Bean;如果还无法匹配,就会抛出异常。@Autowired是一个默认必须找到对应Bean的注解,如果不能确定其标注属性一定会存在并且允许这个被标注的属性为null,那么你可以配置@Autowired属性required为false。
有多个注解的时候使用@Autowired就会有歧义,这时候我们就能使用@Primary和@Quelifier来解决。
- Primary:它是一个修改优先权的注解,其含义告诉Spring IoC容器,当发现有多个同样类型的Bean时,请优先使用我进行注入,于是再进行测试时会发现,系统将用我为你提供服务。但如果多个类都使用Primary注解,那么还是无法区分出使用哪个,所以我们要用Quelifier
- 它的配置项value需要一个字符串去定义,它将与@Autowired组合在一起,通过类型和名称一起找到Bean。我们知道Bean名称在Spring IoC容器中是唯一的标识,通过这个就可以消除歧义性了。
@Autowired
@Qualifier("dog")
private Animal animal = null;