Spring学习-注解驱动

今天开始总结Spring的注解驱动部分。

1、首先咱们使用Spring的时候大多数是采用XML方式配置进行容器的Bean的管理。今天咱们看看基于Java类的方式创建容器并加载需要的组件。

1.1、首先咱们回顾下咱们的之前的使用XML方式创建容器并进行组件的加载。

首先我们需要一个XML的配置,代码如下:






  

  

      

      

  

咱们上面定一个简单的XML用户组件的管理,然后咱们可以使用:ClassPathXmlApplicationContext进行容器的创建,加载这些需要注册的组件。

   public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Person bean = (Person) applicationContext.getBean("person");
        System.out.println(bean.getAge() + ":" + bean.getName());

    }

下面咱们运行这段代码看看执行结果:


image.png

这就是咱们采用习惯的XML方式管里Bean组件,很简单。

咱们下面看下不同的创建容器的方式和组件的加载方式,因为目前的大火的SpringBoot和SpringCloud里面大量使用非XML组件管里方式管里组件。

1.@ComponentScan + @Bean的方式注册和管里组件

首先咱们看下如何使用扫面 + 注解的方式注册和管里组件@ComponentScan + @Bean,注解@ComponentScan是用来进行包扫面,@Bean 就是定义咱们需要加载的组件。Java创建容器管里类:

@ComponentScan(value = "com.annotation.config.mainConfig")
public class MainConfig {

    @Bean
    public Person person() {
        return new Person("张三", 19);
    }
}
  @Test
    public void test01() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        final String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }
    }

以上代码我只是在注册类中定义了扫面包的路径,默认的扫描路径是配置类所在的包下。然后定义了一个组件@Bean。咱们执行下代码看下效果。


image.png

咱们可以看到容器启动了并加载了两个组件进去,一个是配置类。另一个就是咱们定义的组件。
上面咱们采用@ComentPonentScan 进行包的扫面,在Java8之前的版本中可以采用@ComponentScans进行多个扫描策略的添加。Java8之后可以直接支持直接多个扫描策略的添加。下面咱们一起看看。

2.@ComponentScan配置多个扫描策略

2.1 excludeFilters 排除某个不需要加载的组件:

@ComponentScan(value = "com.annotation.config.mainConfig", excludeFilters = {   //扫描排除某个类型
    @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
})

只需要在使用@ComponentScan的时候配置excludeFilters就可以了,他是ComponentScan的内部注解。需要的参数有两个,一个是Type,另一个是排除的类型行。看下面的源码。


image.png

我们进入到源码可以看到Type的几种类型:

  • FilterType.ANNOTATION : 按照注解的方式扫描包规则
  • FilterType.ASSIGNABLE_TYPE : 按照给定的类型进行包扫描
  • FilterType.ASPECTJ : 使用Aspectj 表达式进行包扫描(不常用)
  • FilterType.REGEX : 正则表达式
  • FilterType.CUSTOM : 使用自定义规则(需要自定义实现类TypeFilter)
    大家可以自己试试这个方法。

3.includeFilters只扫面某个包

@ComponentScan(value = "com.annotation",  //扫描包含某个类型
        includeFilters = {
    @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})},
        useDefaultFilters = false
)

在使用只扫面某个包的时候需要注意的是useDefaultFilters这个参数需要设置成false。就是给容器说采用我们自定义的包扫描规则。

4.自定义包扫描规则。

就是上面的Type类型中的最后一个,* FilterType.CUSTOM : 使用自定义规则(需要自定义实现类TypeFilter)自定义包扫描规则。代码也很简单,但是需要咱们实现TypeFilter接口。我们一起看下:

@ComponentScans(value = {  //扫描更具自定义类型进行扫描
        @ComponentScan(value = "com.annotation",
                includeFilters = {
//                        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
//                        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookDao.class}),
                        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
                },
                useDefaultFilters = false
        )
}
)

我在上面的代码里设置了多个包扫描的规则,只加载的包中就是按照我自定义的包扫描规则来进行包的扫描。其中MyTypeFilter的实现是:

public class MyTypeFilter implements TypeFilter {

    /**
     * @param metadataReader        读取到的当前类
     * @param metadataReaderFactory 一个工厂可以获取其他的类信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类注解信息
        final AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前正在扫描的类的类信息
        final ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当天类资源 (类路劲)
        final Resource resource = metadataReader.getResource();

        final String className = classMetadata.getClassName();
        System.out.println("--->" + className);

        if (className.contains("er")) {
            return true;
        }
        return false;
    }
}

他需要实现一个math方法。他有两个参数,第一个是读取当前类的信息。可以获取到扫描包下类的所有的详细信息。想类的注解信息,类本身信息、类路劲等等。我在实现中是设置只加载包含"er"的组件。第二个参数是可以读取到其他的类信息,大家可以跟进去debug下看看具体的包含的信息。

2.@Configuration 和 @Bean给容器注册组件。

前面咱们Configuration是Spring 3.0以后出现的注解驱动,他主要的目的就是创建一个Spring的容器。配合@Bean就可以将我们需要的组件注册到容器中。在创建容器的时候可以使用AnnotationConfigApplicationContext 或者采用 AnnotationConfigWebApplicationContext创建容器并采用@Bean初始化的组件。代码我就不贴出来了,下面的例子中都是采用@Configuration+@Bean实现容器创建和Bean的管理的。

3.Bean的Scope

下面咱们看下Bean的作用域的设置,大家都知道咱们Spring的容器中的组件默认都是単例的。容器只在创建Bean的时候注册到容器中一个实例,后面的使用都是直接拿这个实例的引用。其实容器在启动的时候创建注册Bean的时候维护了一个CurrentHashMap,注册的Bean就放进这map 中,使用的时候直接从这个CurrentHashMap 中拿就行了。具体的实现大家可以去看看源码实现。

@Configuration
public class MainConfig2 {

    /**
     * * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
     *
     * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE  SCOPE_PROTOTYPE = "prototype";
     * @see ConfigurableBeanFactory #SCOPE_SINGLETON  SCOPE_SINGLETON = "singleton";
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
     * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
     *
     * 通过源码可以发现 singleton 其实是在currentHashMap 中使用 synchronized 加锁进行注册一个实例保证単例的
     * 默认容器启动的时候创建对象
     */
    @Scope(scopeName = "prototype")
    @Bean
    public Person person() {
        System.out.println("----> 进行实例的创建");
        return new Person(20, "lly");
    }

}

@Scope(scopeName = "prototype")其实就一行简单的代码。你可以看看他的源码,有两种写法:另一种写法是:@Scope( "prototype")其实两者是一样的。需要注意的一点就是,Scope作用域,単例的时候是容器创建的时候就初始化好了这个Bean。而多实例的作用域下,不会,只是在真正使用的时候才初始化这个bean ,每次都是新创建的一个Bean。単例在容器销毁的时候就会销毁。但是多例容器只管创建以后其他的事情他就不管了。

4.Bean-Lazy,Bean的懒加载

@Configuration
public class MainConfigLazy {

    /**
     * 懒加载,实例在第一次使用的时候才加载在容器里,适用于単例的模式下,就是容器的默认的创建scope
     *
     * @return
     */
    @Lazy
    @Bean
    public Person person1() {
        System.out.println("lazy ----> 进行实例的创建");
        return new Person(20, "lly");
    }
}

懒加载也很简单,只是一个注解的使用。在真正使用的时候才创建。
今天咱们就先看到这总结一下啦:
1、XML方式创建容器加载组件
2、Java,扫描 + 注解的方式加载组件
3、@ComponentScan 下加载组件的几种方式:
1.默认扫描的是加载类的包下的所有注解
2.排除某些不需要加载的类
3. 加载某些特定的类,但是需要注释掉默认的方式
4.主动加载某些类,执行自定义的TypeFilter,需要实现TypeFilter

4、@Configuration配置类中@Bean使用创建组建的时候指定@Scope可以指定组件的作用域
5、组件的懒加载:@Lazy

你可能感兴趣的:(Spring学习-注解驱动)