Spring(一)源码之@Configuration、@ComponentScan、@Bean的使用

最近在整理一些spring相关的知识,就顺带在这记录一下总结一下,给自己看也希望能帮助到一些正在学习以及一直在学习的伙伴。

本文内容记录一下@Configuration注解的使用以及它与xml文件是如何对应的。

自从spring3.0开始,@Configuration可以用于定义配置了,也就是替代了过去xml配置,被注解的类内部含有一个或多个Bean,这些方法会被AnnotationConfigApplicationContext或者AnnotationConfigWebApplicationContext加载扫描,并构建Bean从初始化spring容器,类似于加载xml文件ClassPathXmlApplicationContext类加载xml文件。

一、@Bean的使用:使用此注解,表示在容器中注册一个Bean,类型为方法返回值的类型,id默认为是方法名,也可以自行指定id,在xml中类似于一组<bean></bean>标签,举个简单例子:

 <bean id="person" class="bean.Person">
        <property name="name" value="张三"></property>
        <property name="age" value="18"></property>
 </bean>

xml的使用

首先创建一个类并在xml中注册一个Bean,id为person,class写出全路径,并定义两个属性name和age,

在过去我们通过ClassPathXmlApplicationContext来加载xml文件,根据指定bean的id通过getBean(id)来获取到Bean,例如:

public class MainTes {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Person bean = (Person) applicationContext.getBean("person");
        System.out.println("person:"+bean);
        //打印信息:person:Person{name='张三', age='18'}
    }
}

@Bean注解的使用:

我们需要自定义一个类@Configuration来指定为配置类等同于xml文件中的<beans></beans>,我们在类中注册Bean

//配置类==配置文件
@Configuration //告诉spring这是一个配置类
public class MainConfig {
    @Bean(name = "person")  //在容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id,也可以自己指定name属性为id类型
    public Person person(){
        return new Person("李四","16");
    }
}

通过AnnotationConfigApplicationContext类扫描加载

public class MainTes {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        //根据返回值类型来得到Bean
        Person bean = applicationContext.getBean(Person.class);
        //根据id来得到Bean
        Person bean1 = (Person) applicationContext.getBean("person");
        //获取bean的id,也就是@Bean(name="person")中name的值,没有的话就默认为方法名作为id。
        String[] str = applicationContext.getBeanNamesForType(Person.class);
        for (String st : str) {
            System.out.println("str:" + st);
        }
        System.out.println("bean:" + bean);
        System.out.println("bean1:" + bean1);
//        打印信息:
//        str:person
//        bean:Person{name='李四', age='16'}
//        bean1:Person{name='李四', age='16'}
    }
}

我们都能加载并得到容器中的Bean

也就是说

@Configuration等同于xml中的<beans></beans>

@Bean等同于xml中的<bean></bean>

二、包扫描组件:只要标注了@Controller、@Service、@Respository、@Component 都会被扫描到

在xml的配置中是这样<context:component-scan bse-package=""></context:component-scan>,一般我们在指定base-package的时候都是写包的根目录,因为它会自动扫描根目录 下所有的包,只要标注了以上四个注解的都会被扫描到。

 <context:component-scan base-package="bean"></context:component-scan>
    <bean id="person" class="bean.Person">
        <property name="name" value="张三"></property>
        <property name="age" value="18"></property>
 </bean>

在这我主要记录一下@ComponentScan的使用:

@ComponentScan等同于上面xml配置文件的扫描组件:会自动扫描包路径下面的所有@Controller、@Service、@Repository、@Component 的类

@ComponentScan里的属性:value指定扫描的包,includeFilters包含那些过滤,excludeFilters不包含那些过滤,useDefaultFilters默认的过滤规则是开启的,如果我们要自定义的话是要关闭的。其中@Filters是一个过滤器的接口。

value属性:点开spring定义的注解类ComponentScan我们可以发现value属性默认为一个String[]类型

value的值可以一般是扫描包名:

首先我分别创建一个dao,service,controller分别在三个不同包中,注意看下包名,我这为了方便观看,将三个类整合到了一起。

package dao;
import org.springframework.stereotype.Repository;
@Repository
public class TestDao {}

/*----------------------------------------------*/

package Controller;
import org.springframework.stereotype.Controller;
@Controller
public class TestController {}


/*----------------------------------------------*/

package service;
import org.springframework.stereotype.Service;
@Service
public class TestService {}

我这扫描Controller、dao,service注解

//配置类==配置文件
@Configuration //告诉spring这是一个配置类
//扫描策略
@ComponentScan(value = {"Controller","dao","service"}) //也可以根目录扫描的方式,会自动扫描目录下带有4个注解的包
public class MainConfig {}
public class AnnoTest {
    @SuppressWarnings("resource")
    @Test
    public void test01(){
        ApplicationContext applicationContext =  new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definition = applicationContext.getBeanDefinitionNames();
        for(String name : definition){
            System.out.println("name:"+name);
            //在打印的信息中我们可以看到上方标注了@Controller @Service @Repository的类名
        }
    }
}

excludeFilters:过滤器,定义了过滤规则,相当于<context:component-scan bse-package=""></context:component-scan>内的子标签<context:exclude-filter type="" expression=""/>主要的过滤规则为:

FilterType.ANNOTATION:按照注解

FilterType.ASSIGNABLE_TYPE:按照给定的类型

FilterType.ASPECTJ:使用ASPECTJ表达式

FilterType.REGEX:使用正则

FilterType.CUSTOM:使用自定义规则

例如:type为按照注解类型,value中指定了不被扫描的注解名称,我们这样可以在扫描的时候跳过带有这两个注解的类。

//配置类==配置文件
@Configuration //告诉spring这是一个配置类
//扫描策略
@ComponentScan(value = {"Controller","dao","service"},excludeFilters ={
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class,Service.class}), //按照注解来过滤
}) //也可以根目录扫描的方式,会自动扫描目录下带有4个注解的包
public class MainConfig {

}

我们查看spring定义注解ComponentScan我们可以找到Filter,我们可以发现spring中默认是注解方式的过滤规则,点开FilterType我们能发现spring定义几种过滤规则。

includeFilters:与excludeFilters作用相反,扫描时只扫描的包,但是需要注意的是,使用这个的时候需要指定userDefaultFilters = false把默认扫描规则为false,因为默认的时true不会生效。

@ComponentScan(value = {"Controller","dao","service"},includeFilters ={
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class,Service.class}), //按照注解来过滤
},useDefaultFilters = false) //也可以根目录扫描的方式,会自动扫描目录下带有4个注解的包
public class MainConfig {

}

FilterType.CUSTOM:自定义扫描规则,这个稍微重要点。

我们需要自定义一个Filter实现TypeFilter并重写match方法,并根据方法返回的布尔类型为true或false来判断是否过滤。

例如:指定Filter类型为自定义类型,值为自己定义的一个类

//配置类==配置文件
@Configuration //告诉spring这是一个配置类
//扫描策略
@ComponentScan(value = {"Controller","dao","service"},excludeFilters ={
        @ComponentScan.Filter(type = FilterType.CUSTOM,value = {MyFilter.class}) //由自定义的Filter返回的布尔值来过滤
})
public class MainConfig {

}
public class MyFilter implements TypeFilter {
    /**
     * @param metadataReader 读取到的当前正在扫描的类的信息
     * @param metadataReaderFactory  获取其他任何类的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类注解的信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前按正在扫描的类的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();
        String className = classMetadata.getClassName();
        System.out.println("---->"+className);
        if(className.contains("er")){
            return true;
        }
        return false;
    }
}
public class AnnoTest {
    @SuppressWarnings("resource")
    @Test
    public void test01(){
        ApplicationContext applicationContext =  new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definition = applicationContext.getBeanDefinitionNames();
        for(String name : definition){
            System.out.println("name:"+name);
        }
    }
}

打印信息:

---->Controller.TestController
---->dao.d.TesDao1
---->dao.d1.TesDao2
---->dao.TestDao
---->service.TestService

name:tesDao1
name:tesDao2
name:testDao

我们可以看到扫描的类有5个,但是只要标注了@Repository的没有被过滤,@Controller和@Service都被过滤掉了,因为他们包了"er"。

记录学习笔记算是强制让自己学习和鞭策自己,同时也希望能够帮助有需要的伙伴,对于笔记有什么错误的或者不足的希望大家能帮忙纠正一下。万分感谢!

发布了17 篇原创文章 · 获赞 18 · 访问量 1038

猜你喜欢

转载自blog.csdn.net/qq_40409260/article/details/103056959
今日推荐