Spring中给IoC容器注入bean的几种方法(一)

您所在的位置:网站首页 ioc的几种注入方式 Spring中给IoC容器注入bean的几种方法(一)

Spring中给IoC容器注入bean的几种方法(一)

2023-11-23 23:02| 来源: 网络整理| 查看: 265

  目前来说,java编程离不开spring,spring最基本的功能便是IoC容器功能,所以spring提供了众多的方法可以将自己写的bean对象注入到容器中,由容器来进行管理。本文总结了用注解将一个bean注入到容器中的常用的方法,用示例的方式来演示其具体的使用方式和效果。

1、@Bean注解直接导入单个类 1.1 @Bean注解的默认使用方式

  @Bean注解需要写在由 @Configuration注解标注的配置类中的方法上,可以将该方法创建的类注入到IoC容器中,默认容器中的key是方法名称,也可以通过在@Bean中增加参数的方式改变注入到容器中的key,示例如下:

我们新建一个Person类来演示需要注入的Bean,其中有一个name属性 public class Person { private String name; public Person(String name) { System.out.println("创建person对象,name=" + name); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 创建一个BeanConfig类,用来作为@Bean注解注入spring的配置类,并在其中用@Bean注解注入person对象 @Configuration public class BeanConfig { @Bean public Person person() { return new Person("common"); } } 编写一个测试类BeanConfigTest ,用来测试@Bean注解的使用 public class BeanConfigTest { @Test public void testBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println("-------------------容器初始化完成------------------"); Person person = (Person) context.getBean("person"); System.out.println(person.getName()); } }

测试结果如下:

创建person对象,name=common -------------------容器初始化完成------------------ common 1.2 @Bean配合@Lazy注解实现bean的懒加载

  在spring中,默认bean都是在spring容器启动的时候就已经加载好的,就像上面1.1中的示例中,在容器初始化完成之前就已经调用bean的构造方法将bean创建好放到IoC容器当中了,如果要实现在spring启动的时候不去初始化bean,而是在调用getBean()方法的时候再去创建bean的功能,就叫做bean的懒加载,可以在加载bean的类或者@Bean标注的方法上用@Lazy注解来实现该功能,代码如下:

在BeanConfig中增加一个方法,注入一个新的Person对象 @Bean("lazyPerson") @Lazy public Person lazy() { return new Person("lazy"); } 在BeanConfigTest中编写一个测试方法 @Test public void testLazyBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println("-------------------容器初始化完成------------------"); Person person = (Person) context.getBean("lazyPerson"); System.out.println(person.getName()); }

测试结果如下:

-------------------容器初始化完成------------------ 创建person对象,name=lazy lazy

  和1.1中的测试结果对比我们可以看到,加了@Lazy注解注入的person对象在调用构造方法是在容器初始化之后才创建的

1.3 @Bean配合@Scope注解实现类的作用范围控制

  在Spring中,类默认都是单例的,如果想要将一个bean在Spring中不是单例的,就需要用到@Scope注解来完成。@Scope注解的可选参数和说明如下:

prototype :原型bean,每次使用都会创建一个新的beansingleton :单例bean,spring的默认,在整个容器中只会创建一次request :主要应用于web模块,同一次请求只创建一个实例session 主要应用于web模块,同一个session只创建一个实例 写一个原型的代码示例:在BeanConfig类中新增一个带有@Scope注解大注入方法 @Bean("prototype") @Scope("prototype") public Person prototype() { return new Person("prototype"); } 在BeanConfigTest中新增一个测试方法 @Test public void testPrototypeBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println("-------------------容器初始化完成------------------"); Person person1 = (Person) context.getBean("prototype"); Person person2 = (Person) context.getBean("prototype"); System.out.println(person1 == person2); }

测试结果:

-------------------容器初始化完成------------------ 创建person对象,name=prototype 创建person对象,name=prototype false

  从上面的测试结果中我们可以看出,每次调用getBean方法的时候,spring都会去调用Person类的构造方法来创建一个新的类,而且两次调用创建出来的类是不一样的。

1.4 @Bean配合@Conditional实现按照条件来注入bean

  @Conditional注解可以控制spring在注入该bean的时候先去按照我们写的要求去判断一下条件,如果是满足条件的,就注入该对象,如果是不满足条件的,就不去注入该对象,@Conditional注解是SpringBoot实现的基础的注解,在SpringBoot中就是根据这个注解来判断我们是否导入了某个功能的jar包,然后替我们将对应功能的配置类导入到容器中,实现自动化装配的功能。   @Conditional注解中的参数是一个实现了org.springframework.context.annotation.Condition接口的类,通过这个接口的matches方法,判断是否需要导入新的bean,我们用判断Windows和Linux系统的方式,来导入不同的对象来作为示例,代码如下:

实现WindowsCondition类,用来判断在windows系统时注入对象 public class WindowsCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Environment environment = conditionContext.getEnvironment(); if (environment.getProperty("os.name").contains("Windows")){ return true; } return false; } } 实现LinuxCondition类,用来判断在linux系统时注入对象 public class LinuxCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { Environment environment = conditionContext.getEnvironment(); if (environment.getProperty("os.name").contains("Linux")) { return true; } return false; } } 在BeanConfig中用@Bean注解分别注入不同条件下的bean @Bean @Conditional(WindowsCondition.class) public Person windowsPerson() { return new Person("windows"); } @Bean @Conditional(LinuxCondition.class) public Person linuxPerson() { return new Person("Linux"); } 在BeanConfigTest中编写对应的测试方法 @Test public void testConditionBean(){ ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); System.out.println("-------------------容器初始化完成------------------"); Person windowsPerson = (Person) context.getBean("windowsPerson"); System.out.println(windowsPerson.getName()); Person linuxPerson = (Person) context.getBean("linuxPerson"); System.out.println(linuxPerson.getName()); }

测试结果如下:

创建person对象,name=windows -------------------容器初始化完成------------------ windows org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'linuxPerson' available

  从上面的结果中我们看到,因为我的系统时windows,spring根据@Conditional注解只注入了windows的person对象,并没有注入linux的对象,因此我们在获取linux的对象的时候,抛出了没有bean的异常。

2、@ComponentScan注解扫描并自动注入包中的bean

   @ComponentScan注解可以认为是批量的@Bean注解,spring可以根据***@ComponentScan***配置的路径或者规则自动装配扫描到的bean,这也是我们日常使用中最常见的一种方式,在这里我们该注解的使用方法做一个总结。

2.1 @ComponentScan注解的默认使用方法

  @ComponentScan注解最常用的是在参数中配置一个包路径,spring会为我们扫描这个包下面加了@Controller、@Service、@Repository、@Component这几个注解的类,然后添加到容器中。在容器中,默认bean的名称是首字母小写的类名当然,我们也可以用这些注解的value参数来指定新的类名。同时,@Lazy、@Scope和@Conditional等注解也可以加载对应的类上面,实现对应的功能。示例代码如下:

我们首先编写一个ComponentScanTest的测试类,用来打印出注入到spring的IoC容器中的bean,为了方便演示,我们在打印的时候过滤掉了IoC容器中spring的bean和配置类 public class ComponentScanTest { @Test public void showIocComponents() { ApplicationContext context = new AnnotationConfigApplicationContext(ComponentScanConfig.class); System.out.println("-----------------容器启动完成---------------------"); String[] beanDefinitionNames = context.getBeanDefinitionNames(); for (String beanDefinitionName : beanDefinitionNames) { //跳过spring的 bean和配置bean本身,只关注我们自己定义的对象 if (beanDefinitionName.contains("org.springframework.context") || beanDefinitionName.endsWith("Config")) { continue; } System.out.println(beanDefinitionName); } } } 我们在entities包下创建几个类,分别加了@Controller、@Service、@Repository、@Component这几个注解 @Controller public class ControllerBean { } @Service public class ServiceBean { } @Repository public class RepositoryBean { } @Component public class ComponentBean { } 编写ComponentScanConfig配置类,用@ComponentScan的默认方式扫描bean @Configuration @ComponentScan(value = "entities") public class ComponentScanConfig { } 执行测试方法,测试结果如下: -----------------容器启动完成--------------------- componentBean controllerBean repositoryBean serviceBean 2.2 关闭@ComponentScan的默认扫描

  从上面2.1的测试结果我们可以看出,@ComponentScan注解默认扫描了指定包下面加了@Controller、@Service、@Repository、@Component这几个注解的类。我们可以设置@ComponentScan注解中的useDefaultFilters = false,来关闭默认的扫描方式。示例代码和测试结果如下

关闭默认扫描的ComponentScanConfig 类 @Configuration @ComponentScan(value = "entities",useDefaultFilters = false) public class ComponentScanConfig { } 测试结果 -----------------容器启动完成--------------------- Process finished with exit code 0

  我们可以看到,将参数useDefaultFilters 设置为false之后,原本加了@Controller、@Service、@Repository、@Component注解的类也扫描不到容器中了,也就是说默认的扫描方式失效了

2.3 用includeFilters指定扫描的注解

  从上面的示例中我们可以看出,用将useDefaultFilters 设置为false的方式,我们可以关闭默认的扫描方式,那我们要是只需要扫描其中的一两个注解要怎么办呢?方法就是用includeFilters指定需要扫描的注解,代码如下:

我们在ComponentScanConfig中设置useDefaultFilters = false,并用includeFilters指定只扫描@Controller注解 @Configuration @ComponentScan(value = "entities" , includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = { Controller.class})} , useDefaultFilters = false) public class ComponentScanConfig { } 测试结果如下: -----------------容器启动完成--------------------- controllerBean

  从结果可以看出,IoC中只加入了我们指定的@Controller注解标注的类

2.4 用excludeFilters参数排除部分的扫描类

  spring默认扫描了四个注解,我们可以用excludeFilters参数排除掉 其中的一些注解,使标注了排除注解的类不再被自动加载到容器中。代码如下:

在ComponentScanConfig中排除添加了Controller注解的类 @Configuration @ComponentScan(value = "entities" , excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})}) public class ComponentScanConfig { } 测试结果如下: -----------------容器启动完成--------------------- componentBean repositoryBean serviceBean

  从上面结果中可以看出,标注了@Controller注解的bean没有被加载到IoC容器中

2.5 用includeFilters添加特定的Bean到容器中

  @ComponentScan会默认扫描添加了@Controller、@Service、@Repository、@Component这几个注解的bean,并将它们注入到IoC容器中,如果一个bean没有添加这几个注解(比如引入一个二方jar包,我们没法添加注解)的bean,我们要如何将其加入到spring容器中呢?可以使用@ComponentScan的includeFilters属性将其添加进来,具体的做法是在includeFilters属性列表中加入一个@ComponentScan.Filter对象,其中type选择FilterType.ASSIGNABLE_TYPE,value就是需要添加的具体对象的class列表,示例如下:

新增一个没有添加任何注解的类ComponentFilterBean public class ComponentFilterBean { } 在ComponentScanConfig中添加includeFilters属性 @Configuration @ComponentScan(value = "entities" , includeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {ComponentFilterBean.class})}) public class ComponentScanConfig { } 测试结果如下: -----------------容器启动完成--------------------- componentBean componentFilterBean controllerBean repositoryBean serviceBean

  从上面的结果中可以看出,除了默认的几个bean,没有添加任何注解的ComponentFilterBean也被添加到了容器中

2.6 自定义@CompanentScan的扫描过滤器

  @CompanentScan注解除了用includeFilters和excludeFilters两个属性来灵活选择需要注入的类外,还可以自定义扫描类的filter,来判断是否将扫描到的类加入到容器中。   具体的做法是,我们需要新建一个实现了org.springframework.core.type.filter.TypeFilter接口的类,并实现match方法,spring会将扫描到的类挨个传递给指定的filter,在这个filter中,我们可以根据自己的逻辑指定是否将这个类加入到spring容器中,需要加入的返回true,不需要加入的返回false;然后将这个自定义的filter类加入到includeFilters属性中,type指定为FilterType.CUSTOM即可,示例代码如下:

新建一个MyComponentScanFilter类,实现TypeFilter接口,其中只将componentFilterBean加入容器中,其他的都不加入 public class MyComponentScanFilter implements TypeFilter { @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("ComponentFilterBean")){ return true; } return false; } } 修改ComponentScanConfig的@ComponentScan,将自定义的filter类加入到includeFilters属性中 @Configuration @ComponentScan(value = "entities" , includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {MyComponentScanFilter.class})} , useDefaultFilters = false) public class ComponentScanConfig { } 测试结果如下: ------entities.ComponentBean------ ------entities.ComponentFilterBean------ ------entities.ControllerBean------ ------entities.RepositoryBean------ ------entities.ServiceBean------ -----------------容器启动完成--------------------- componentFilterBean

  从上面的测试结果可以看出,spring将扫描到的所有的类都传入到我们自定义的filter中,但是只根据我们写的filter的逻辑,将componentFilterBean加入到了容器中。

由于文章比较长,一次发不出去,分开来发,后面两种方法请查看《Spring中给IoC容器注入bean的几种方法(二)》

后记   个人总结,欢迎转载、评论、批评指正



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3