本文内容记录一下@Configuration注解的使用以及它与xml文件是如何对应的。
自从spring3.0开始,@Configuration可以用于定义配置了,也就是替代了过去xml配置,被注解的类内部含有一个或多个Bean,这些方法会被AnnotationConfigApplicationContext或者AnnotationConfigWebApplicationContext加载扫描,并构建Bean从初始化spring容器,类似于加载xml文件ClassPathXmlApplicationContext类加载xml文件。
一、@Bean的使用:使用此注解,表示在容器中注册一个Bean,类型为方法返回值的类型,id默认为是方法名,也可以自行指定id,在xml中类似于一组标签,举个简单例子: 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文件中的,我们在类中注册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中的
@Bean等同于xml中的
二、包扫描组件:只要标注了@Controller、@Service、@Respository、@Component 都会被扫描到在xml的配置中是这样,一般我们在指定base-package的时候都是写包的根目录,因为它会自动扫描根目录 下所有的包,只要标注了以上四个注解的都会被扫描到。
在这我主要记录一下@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:过滤器,定义了过滤规则,相当于内的子标签主要的过滤规则为:
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"。
记录学习笔记算是强制让自己学习和鞭策自己,同时也希望能够帮助有需要的伙伴,对于笔记有什么错误的或者不足的希望大家能帮忙纠正一下。万分感谢!