Springboot学习记录2

55 阅读5分钟

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来解决。

  1. Primary:它是一个修改优先权的注解,其含义告诉Spring IoC容器,当发现有多个同样类型的Bean时,请优先使用我进行注入,于是再进行测试时会发现,系统将用我为你提供服务。但如果多个类都使用Primary注解,那么还是无法区分出使用哪个,所以我们要用Quelifier
  2. 它的配置项value需要一个字符串去定义,它将与@Autowired组合在一起,通过类型和名称一起找到Bean。我们知道Bean名称在Spring IoC容器中是唯一的标识,通过这个就可以消除歧义性了。
@Autowired
@Qualifier("dog") 
private Animal animal = null;