Spring注解-IOC(一)

基于xml配置文件的Spring运用:

  传统的Spring做法是使用xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:
  1、如果所有的内容都配置在xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。
  2、在开发中在.java文件和xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。

  //获取配置文件生成的IOC容器
  ApplicationContext applicationContext = 
      new ClassPathXmlApplicationContext("beans.xml");

  为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

基于注解的Spring运用:

  @Configuration: 告诉Spring这是一个配置类,替代传统的xml配置文件,是配置类的最基本组件。在配置类中配合@Import、@Conditional、@Bean等等注解可以向容器中自定义的添加各类组件。

  //获取配置类生成的IOC容器
  ApplicationContext applicationContext = 
      new AnnotationConfigApplicationContext(MainConfig.class);

  @Import: 快速导入组件,包括配置类。
    1.Class : id默认是组件的全类名
    2.ImportSelector :返回需要导入的全类名数组
    3.ImportBeanDefinitionRegistrar :bean的定义注册器

  代码示例:
  1.定义三个将要被@Import导入的类

  public class Red extends Color{

  }
  /**
   * 自定义返回需要导入的组件
   */
  public class MySelector implements ImportSelector {
      //返回值就是要导入到容器中的组件全类名数组,
      //AnnotationMetadata:当前标注@Import注解的配置类的所有注解信息
      public String[] selectImports(AnnotationMetadata importingClassMetadata) {
          return new String[]{
              "com.atguigu.bean.ioc.Blue", "com.atguigu.bean.ioc.Yellow"
          };
      }
  }
    /**
     * 实现ImportBeanDefinitionRegistrar接口,使其拥有注册bean的功能
     * AnnotationMetadata:当前标注@Import注解的配置类的所有注解信息
     * BeanDefinitionRegistry:配置类对应的IOC容器注册器
     */
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            registry.registerBeanDefinition("yellow",new RootBeanDefinition(Yellow.class));
        }
    }
Spring注解-IOC(一)_第1张图片
AnnotationMetadata:当前标注@Import注解的配置类的所有注解信息

Spring注解-IOC(一)_第2张图片
BeanDefinitionRegistry :bean的定义注册器

  2.定义配置类

  @Configuration
  @Import({Red.class, MySelector.class, MyImportBeanDefinitionRegistrar.class})
  public class MainConfig {
 
  }

  3.测试

    @Test
    public void testScope(){
        ApplicationContext applicationContext = 
                 new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beans = applicationContext.getBeanDefinitionNames();
        for (String bean : beans) {
            System.out.println(bean);
        }
    }

  @ImportResource: 向配置类中导入xml配置文件。

  @ComponentScan: 代替配置文件中的,定义扫描的路径从中找出标识了需要装配的类自动装配到IOC容器中。可以使用 includeFilters、excludeFilters 作为过滤规则。该注解是@Controller,@Service,@Repository,@Component注解使用的基础。

  @Bean:注入组件,默认的组件名为首字母小写的类的名称。代替配置文件中的。@Bean标注的方法,IOC容器在创建对象的时候,参数直接从IOC容器中获取。

  @Scope:
    singleton:单实例,容器启动的时候直接创建单个对象放到IOC容器中。
    prototype:多实例,每次获取的时候才会创建对象。

  @Lazy:懒加载,针对单实例bean,容器启动不默认创建对象,第一次获取bean的时候才创建对象并初始化

  @Conditional:可标注在类上或者方法上,按照一定条件进行判断,满足条件给容器中注册bean,否则不对类或者方法做处理。此注解在Springboot的自动配置中有大量应用。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    /**
     * All {@link Condition}s that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class[] value();
}

  从代码中可以看到,使用@Conditional 注解,需要传入一个Class数组,并且数组内元素需要实现Condition接口。
  如何运用呢?首先我们自定义两个Condition接口的实现类以及一个需要被按条件注入的bean组件。

  • 定义实现类
public class WindowCondition implements Condition {
    /**
     * ConditionContext: 上下文环境,包含容器的环境信息、注册器、资源加载器
     * AnnotatedTypeMetadata: 当前标注了@Condition注解的注释信息
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        ClassLoader classLoader = context.getClassLoader();
        Environment environment = context.getEnvironment();
        BeanDefinitionRegistry registry = context.getRegistry();
        ResourceLoader resourceLoader = context.getResourceLoader();
        System.out.println(beanFactory);
        System.out.println(classLoader);
        System.out.println(environment);
        System.out.println(registry);
        System.out.println(resourceLoader);
        //判断是否window系统
        if (environment.getProperty("os").contains("Window")){
            return true;
        }
        return false;
    }
}
public class LinuxCondition implements Condition {

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        String os = environment.getProperty("os");
        if (os.contains("linux")){
            return true;
        }
        return false;
    }
}
  • 定义将被按条件注册的实体bean
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
    private Integer deptNo;
    private String deptName;
}
  • 然后在配置类运用它。
@Configuration
@Import({Color.class, Red.class, MySelector.class, MyImportBeanDefinitionRegistrar.class})
@Conditional(WindowCondition.class)  //如果不满足条件,则配置类失效
@ComponentScan(value = "com.atguigu.ioc"
    ,includeFilters ={@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class,Repository.class})}
    ,useDefaultFilters = false)
public class MainConfig {

    /**
     * @Scope:单实例:容器启动的时候直接创建单个对象放到IOC容器中。多实例:每次获取的时候才会创建对象。
     * @Bean:给容器注册一个bean,类型为返回值类型,id默认是方法名,默认单例的。
     * @Lazy:懒加载,针对单实例bean,容器启动不默认创建对象,第一次获取bean的时候才创建对象并初始化
     */
    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    @Lazy
    public Person person(){
        return new Person("李四",30,"设计师");
    }

    /**
     * @Conditional: 按照一定条件进行判断,满足条件给容器中注册bean
     */

    @Bean("window")
    public Dept dept01(){
        return new Dept(1001,"设计部");
    }

    @Bean("linux")  //@Bean标注的方法,IOC容器在创建对象的时候,参数直接从IOC容器中获取
    @Conditional(LinuxCondition.class)
    public Dept dept02(){
        return new Dept(1002,"开发部");
    }
}

  @PropertySource:加载指定配置文件。一般和@Value配合使用为IOC容器中的bean组件属性赋值。@PropertySource相当于在xml文件中配置:;@Value相当于

  • 在类路径下定义一个配置文件,score.properties
score.stuId=20100514
score.score=98
  • 在组件中,定义Score组件
@Data
@NoArgsConstructor
@AllArgsConstructor
@PropertySource("classpath:score.properties")  //加载配置文件
public class Score {

    @Value("${score.stuId}")    //通过el表达式为组件赋值
    private Integer stuId;

    @Value("${score.score}")
    private Integer score;
}
  • 定义配置文件,注入Score组件
@Configuration
@Import(Score.class)
public class MainConfigOfPropertyValue {

}
  • 测试
@Test
    public void testValue2(){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfPropertyValue.class);
        Score bean = applicationContext.getBean(Score.class);
        System.out.println(bean );      //Score(stuId=20100514, score=98)
        ConfigurableEnvironment environment = 
            ((AnnotationConfigApplicationContext) applicationContext).getEnvironment();
        String property = environment.getProperty("score.score");
        System.out.println(property);    //98
    }

你可能感兴趣的:(Spring注解-IOC(一))