DI 注入

  • 一. 传统的注入方式
    • 通过bean标签方式注入
    • 通过包扫描的方式注入
  • 二. 注解方式配置注入
    • @Configuration 与 @Bean 注解方式注入
    • @ComponentScan 包扫描方式注入
    • @Import 注入
    • FactoryBean 方式注入
  • 三. 获取容器中的实例
    • scopt 单例与多例取值,与创建实例的时间
    • 针对单例
  • 四. @Conditional() 根据条件向容器中注入实例
  • 五. 总结

一. 传统的注入方式

通过bean标签方式注入

  1. 例如配置数据库连接的DruidDataSource,设置数据库连接,登入数据库的用户名密码,然后注入到Spring容器中,在.xml配置文件中通过bean标签的形式,注入
  2. 获取测试(注意点ClassPathXmlApplicationContext获取时默认获取resources下的直接文件,若文件在resources文件夹中的文件夹中需要通过""classpath:resources文件夹下的文件夹"来指定)
 public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-mybatis.xml");DruidDataSource dataSource = (DruidDataSource) context.getBean("dataSource");System.out.println(dataSource.getUrl());}

通过包扫描的方式注入

  1. 在使用Spring时都用过@Controller,@Service等,将Controller层,Service层注入到Spring容器中,在使这些注解时,前提是通过xml配置开启包扫描,扫描哪些包下的类使用了这些注解来修饰,如果有则将扫描到的注入到Spring容器中
  2. 配置扫描与排除
 <!--扫描位置,扫描com.test包中所有的--><context:component-scan base-package="com.test" ><!--排除掉注解, 为Controller类型的--><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /></context:component-scan>
  1. 原理: 会向容器中注入一个实现了BeanDefinitionParser接口的注解解析器例如ComponentScanBeanDefinitionParser,通过该类中的parse()方法,解析base-package属性配置的扫描路径,进行扫描,进行创建,初始化,注入等

二. 注解方式配置注入

@Configuration 与 @Bean 注解方式注入

创建配置类,该类使用@Configuration修饰,
配置类中创建方法,方法被@Bean注解修饰,(如果@Bean修饰的方法有形参,形参默认在容器中获取并赋值)
方法有返回值,返回的数据就是注入到Spring容器中的数据

  1. 通过注解的方式配置数据库连接
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyConfiguration {private String url="bbb";private String name="ccc";private String password = "ccc";//@Bean注解,修饰方法,将方法返回的对象注入到Spring容器中//默认方法名为对象名,有点像xml方式配置是的id="方法名"//也可以通过@Bean("指定")来指定@Beanpublic DruidDataSource configMethod(){DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setUrl(url);druidDataSource.setName(name);druidDataSource.setPassword(password);return druidDataSource;}
}
  1. 获取测试
 public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext (MyConfiguration.class);DruidDataSource dataSource = (DruidDataSource) context.getBean(DruidDataSource.class);System.out.println(dataSource.getUrl());System.out.println(dataSource.getPassword());}

@ComponentScan 包扫描方式注入

  1. 创建配置类,配置类使用@Configuration修饰(?是否是必须的)
  2. 配置类还需要使用@ComponentScan(value=“com.扫描的包路径”),修饰,指定扫描哪些包中的
  3. 将扫描的包中被@Service,@Controller,@Component,@Repository等修饰的都会被注入到Spring容器中
  4. @ComponentScan注解可以指定扫描的包中包指定哪些includeFilters,排除哪些excludeFilters
@Configuration
/*value 指定扫描那个包
* excludeFilters 指定排除哪些,后面是个{}括号,数组存在的,如果想排除多个,逗号分隔即可
* type 是排除类型,可以根据注解,类,按照正则表达式排除,自定义排除等
* 此处的示例是扫描com.ssm.service包,排除该包下的注解,为Controller类型的
* 如果把excludeFilters 换成 includeFilters 则就是扫描 com.ssm.service 中
* 注解是Controller类型的注入到Spring容器中, 但是还需要添加设置@ComponentScan
* 中的一个 useDefaultFilters = false 默认规则是扫描所有,禁用默认规则,才能生效*/
@ComponentScan(value = "com.ssm.service",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class MyConfiguration {}

@Import 注入

  1. 可以使用@Import注解向容器中注入bean
//向容器中注入String,与Persion,多个使用逗号隔开
@Import({String.class , Persion.class})
public class MyConfiguration {}
  1. 使用@Import注入,也可以向该注解传递一个实现了ImportSelector接口的类,该类重写ImportSelector中的selectImports()方法,将该方法返回的数组中包含的所有组件注入到容器中,数组中包含的是需要注入的全类名,做到批量导入

创建返回批量需要注入的的组件的类,此时就可以向@Import({MyImportSelector .class})注解中传递,在注入时自动调用MyImportSelector 中的selectImports()方法,将方法返回的数组中的类注入到容器中,注意数组可以为0,但是不能为null,会报空指针

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;//实现ImportSelector 接口,重写selectImports()方法
public class MyImportSelector implements ImportSelector {/*** 该方法的返回值就是要导入到容器中的组件,全类名* @param annotationMetadata 当前使用@Import注解修饰的类的所有信息* @return*/@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{"com.ssm.configaution.Persion"};}
}
  1. 使用@Import 注入可以向该注解中传递一个实现了 ImportBeanDefinitionRegistrar 接口,重写接口中registerBeanDefinitions()方法的类,该方法可以拿到 BeanDefinitionRegistry,通过这个类对象可以实现手动向容器中注册ben,查询容器中是否存在指定bean, 删除容器中的某个bean等,在容器启动时会自动调用该方法,然后实现bean的手动注册,删除,等…设置

创建 实现ImportBeanDefinitionRegistrar 接口,并重写接口中的抽象方法类

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {/*** @param annotationMetadata  通过这个变量可以拿到使用@Import修饰的的类的所有信息* @param beanDefinitionRegistry 通过该变量可以在容器启动时,手动的想容器中注册,删除注册,判断容器中是否存在已注册的某个bean*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {//通过beanDefinitionRegistry查询容器中是存在已注册的"persion",也可以传递全类名boolean b = beanDefinitionRegistry.containsBeanDefinition("persion");//如果已注册,手动向容器中注册一个persion2,注意注册的bean需要使用RootBeanDefinition包裹//如果没有注册,手动向容器中注册一个"persion3"if(b){RootBeanDefinition beanDefinition = new RootBeanDefinition(Persion.class);//registerBeanDefinition()向容器中注册beabeanDefinitionRegistry.registerBeanDefinition("persion2", beanDefinition);}else{RootBeanDefinition beanDefinition = new RootBeanDefinition(Persion.class);beanDefinitionRegistry.registerBeanDefinition("persion3", beanDefinition);}}
}

此时在使用@Import注解时就可以向注解中传递MyImportBeanDefinitionRegistrar.class实现对容器中的bean的一些手动操作

//向容器中注入String,与Persion,多个使用逗号隔开
@Import({MyImportBeanDefinitionRegistrar.class})
public class MyConfiguration {}

FactoryBean 方式注入

创建一个实现了 FactoryBean 接口的类, 泛型T就是想容器中注入的bean的类型,重写接口中的抽象方法,定义向容器中注入的bean的一些信息
在使用@Bean注解修饰方法向容器中注入bean时,设置方法返回的数据是实现了 FactoryBean 接口的类型数据,此时就将实现该接口时重写 getObject() 方法返回的bean注入到了容器中

  1. 创建实现 FactoryBean接口的类,假设需要向容器中注入一个Persion
import org.springframework.beans.factory.FactoryBean;public class MyFactoryBean implements FactoryBean<Persion> {//该方法返回的Bean对象就是我们想要注入到容器中的bean@Overridepublic Persion getObject() throws Exception {return new Persion();}//设置注入到容器中的bean的类型@Overridepublic Class<?> getObjectType() {return Persion.class;}//设置注入到容器中的bean是否是单例模式,true 是单例,@Overridepublic boolean isSingleton() {return true;}
}
  1. 通过 @Configuration 与 @Bean 使用 MyFactoryBean 向容器中注入bean
@Configuration
public class MyConfiguration {@Beanpublic MyFactoryBean configMethod1(){return new MyFactoryBean();}
}

3.调用测试

public class Test {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);//注意点,虽然,在使用@Bean修饰方法,有方法层面看返回注入//到容器中的bean是自定义的MyFactoryBean类型,但是实际是通过MyFactoryBean调用//里面重写的getObject()方法注入的beanObject obj = context.getBean("configMethod1");//输出class com.ssm.configaution.PersionSystem.out.println(obj.getClass());//如果我们就是想要获取到注入的 MyFactoryBean//在通过id获取时在id前面添加"&"Object obj2 = context.getBean("&configMethod1");System.out.println(obj2.getClass());}}

三. 获取容器中的实例

在Spring中默认注入到容器中的实例都是单例的,通过scopt可以进行修改设置

scopt 单例与多例取值,与创建实例的时间

  • singleton: 默认单例,默认在容器启动时,就会创建实例放入ioc容器的HashMap集合中,以后每次在容器中直接获取
  • prototype: 多例,在获取实例实例时,创建实例对象放入容器中,获取几次创建几次
  • session: 同一个session创建创建一个实例
  • request: 同一次请求创建一个实例
  1. xml配置方式
 <!--singleton: 默认,单例的prototype: 多例session: 同一个session创建创建一个实例request: 同一次请求创建一个实例--><bean id="hh" class="com.ssm.entity.HelpCategory" scope="singleton"><property name="name" value="aaaa"/><property name="url" value="bbbb"/></bean>
  1. @Scopt注解设置
@Configuration
public class MyConfiguration {@Scope(value = "singleton")@Beanpublic DruidDataSource configMethod(){DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setUrl("aaa");druidDataSource.setName("bbb");druidDataSource.setPassword("ccc");return druidDataSource;}
}

针对单例

在单例模式时,有懒加载与非懒加载的区别, Spring创建容器,就会创建实例,将实例放入容器中,默认非懒加载,但是有些对象实例可能是重量级的,在程序运行时也不一定会用到,为了节省资源则可以设置为懒加载

@Lazy设置懒加载

@Configuration
public class MyConfiguration {private String url="bbb";private String name="ccc";private String password = "ccc";//@Lazy 单例模式,懒加载,容器创建不会创建实例,获取实例时才会@Lazy@Beanpublic DruidDataSource configMethod(){DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setUrl(url);druidDataSource.setName(name);druidDataSource.setPassword(password);return druidDataSource;}
}

四. @Conditional() 根据条件向容器中注入实例

使用 @Conditional()注解, 可以根据条件向ioc中注入实例,对实例进行判断,满足则向容器中添加,不满足则不添加,该注解需要一个,解需要一个 Condition 类型的参数, Condition 是一个接口,需要创建该接口的实现类,重写接口中的matches()抽象方法,在方法中编写条件,该注解可以修饰方法,也可以修饰类

案例
有一个persion对象,当项目运行环境是windows系统时,将这个persion对象注入到Spring容器中

  1. Persion 类
public class Persion {private String name;public Persion(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
  1. 创建判断是否允许注入到容器中的 Condition 实现类,重写matches()判断方法
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;public class MyCondition implements Condition {/**** @param conditionContext 上下文环境* @param annotatedTypeMetadata 注释信息* @return*/@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {//==========查看ConditionContext中可以获取什么数据 =============//1.使用上下文环境对象可以获取到当前ioc使用的创建实例,进行装配的bean工厂ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();//2.可以获取到类加载器ClassLoader loader = conditionContext.getClassLoader();//3.可以获取到当前环境信息,运行时的一下信息,虚拟机信息,环境变量等等Environment environment = (Environment) conditionContext.getEnvironment();//4.可以获取到,Spring运行时的注册类,//运行时所有bean通过这个类注册,可以通过这个类注册一个bean//移除一个bea,查看bean是否存在等BeanDefinitionRegistry reader = (BeanDefinitionRegistry) conditionContext.getRegistry();//============查看ConditionContext中可以获取什么数据 =============//此处通过第三步拿到的运行上下文环境,去获取数据//获取运行项目的系统名称String property = environment.getProperty("os.name");//判读如果运行环境系统名称包含"Windows",则将使用该类的bean注入到容器中if(property.contains("Windows")){return true;}else {return false;}}
}
  1. 创建向容器中注入 bean 的配置类, 使用@Conditional(),修饰注入bean的方法,注解中传递第二步创建的判断条件类MyCondition ,在向容器中创建被@Conditional()修饰的需要注入的bean时,会自动调用MyCondition中的matches()方法,如果方法返回true,才允许注入,否则不允许
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;//如果使用该注解修饰类,则这个配置类中所有向容器中注册bean的方法都会被监管
//@Conditional({MyCondition.class}) @Configuration
public class MyConfiguration {private String url="bbb";private String name="ccc";private String password = "ccc";/*@Conditional()设置向容器中注入实例的条件,该注解需要一个 Condition 类型的参数, Condition 是一个接口创建该接口的实现类,重写接口中的matches()抽象方法*/@Conditional({MyCondition.class})@Bean("persion")public Persion configMethod1(){return new Persion("联想电脑");}}
  1. 运行测试
     public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);Map<String,Persion> persionMap = context.getBeansOfType(Persion.class);for(Map.Entry<String,Persion> entry: persionMap.entrySet()){System.out.println(entry.getValue().getName());}}
  1. 另外,使用@Import 与 FactoryBean的方式好像也可以实现例如条件判断样式的向容器中注入bean

五. 总结

通过了解bean的几种不同注入方式我们了解到

  • Spring 中注入的 bean 默认为单例,的,也可以设置为多例,单例与多例的不同会影响到创建的时间,具体查看上面 scopt 单例与多例取值,与创建实例的时间
  • 针对单例模式,可以设置懒加载与非懒加载(默认)
  • Spring 在启动时底层通过 BeanDefinitionRegistry 实现向容器中注册, 删除等操作容器中的bean
  • 在使用@Controller, @Service等相关类型的注解向 Spring 中进行注入时,并不是使用该注解修饰就可以注入了,而是在其他地方定义了包扫描,被扫描的包中有这些注解修饰的才会注入
  • 通过条件输入了解到 ApplicationContext , ConditionContext 等上下文环境对象,是一个比较强大的类,可以获取到运行环境数据,类加载器,Spring 的 bean 工厂, 对容器中的bean进行管理的 BeanDefinitionRegistry 等相关数据

Spring Bean 的注入方式相关推荐

  1. Spring——Bean管理-xml方式进行属性注入

    目录 一.xml方式创建对象 二.xml方式注入属性 第①种方式注入:set方法注入 第②种方式注入:有参构造函数注入 constructor-arg:通过构造函数注入 用name标签属性: 不按照顺 ...

  2. spring 常用的注入方式有哪些?

    spring 常用的注入方式有哪些? 1.xml中配置 bean 的申明.注册 节点注册 bean 节点的 factory-bean 参数指工厂 bean,factory-method 参数指定工厂方 ...

  3. Spring Bean、XML方式Bean配置、Bean实例化配置、Bean注入

    文章目录 Bean管理 一.SpringBoot Bean 初了解 1.1 了解 1.2 Bean的作用域 1.2.1 注意事项 1.3 第三方Bean 二. 基于XML方式Bean的配置 2.1 S ...

  4. Spring中bean的注入方式

    平常的Java开发中,程序员在某个类中需要依赖其它类的方法. 通常是new一个依赖类的实例再调用该实例的方法,这种开发存在的问题是new的类实例不好统一管理. Spring提出了依赖注入的思想,即依赖 ...

  5. Spring:Bean依赖注入方式

    目录 1 依赖注入方式 1.1 创建maven工程 pom.xml 1.2 创建bean Student.java Teacher.java 1.3 创建配置文件 applicationContext ...

  6. spring(一):spring IoC的注入方式总结

    前言:谈谈对springIOc的理解,两张图很好的阐述了springIoc容器的作用. 传统应用程序示意图.jpg IoC容器后程序结构示意图.jpg springIoC容器注入方式有set注入,构造 ...

  7. spring三种注入方式

    设置Spring的作用域 或者使用枚举值设置 单例和多里使用场景 自动注入 @Primary 一个接口有多个实现被spring管理吗,在依赖注入式,spring会不知道注入哪个实现类就会抛出NoUni ...

  8. 【Spring】- 属性注入方式

    2019独角兽企业重金招聘Python工程师标准>>> House类:只有一个People属性,验证引用的ref引用bean的set方法注入方式 package com.zhiwei ...

  9. Spring之DI注入方式

    什么是DI? DI(Dependency Injection)依赖注入,指容器复制创建和维护对象之间的依赖关系,而不是通过对象本身复制自己的创建和解决自己的依赖.控制反转是通过依赖注入实现的. 依赖注 ...

最新文章

  1. python安装第三方库win10_在win里anaconda怎么安装第三方的库
  2. linux装redis环境变量,linux 怎样安装redis
  3. 操作socket报Too many open files errno :24错误解决方法
  4. 下单送奖励金的实现思路
  5. 子类和父类对象在进行类型转换时_不一样的面向对象(三)
  6. 开发板Linux内核,芯灵思SinlinxA33开发板Linux内核workqueue(附实测代码)
  7. [2018.10.31 T2] 电梯
  8. 涵盖全网动漫、影视、小说的APP集合,手机有了他们,看遍全网
  9. 谁将打开腾讯业绩增速天花板?
  10. QT实现USB热插拔监控
  11. [转载] 柴静《看见》新书发布会
  12. 变量覆盖(超详细!)
  13. win10设置计算机关机时间,win10怎样固定时间关机_win10怎样设置电脑关机时间设置...
  14. jvm参数调优_3_问题排查
  15. 利用python提取视频中的字幕
  16. linux 脚本设置定时,脚本添加定时任务(Linux)
  17. AHB2APB桥接器设计(1)——基本原理
  18. 迭代器以及如何获得迭代器地址
  19. vin端口是什么意思_振荡器基础1——为什么振荡器需要正反馈?什么是LC自激振荡器?...
  20. 你认识这些三叠字和四叠字吗? 犇猋骉蟲麤毳淼掱焱垚鑫卉芔鱻飍姦掱贔

热门文章

  1. [POI2011] SEJ-Strongbox(数论)
  2. (ElasticSearch02)day80分布式查漏补缺
  3. 一分钟教你配置DHCP服务,超级简单一看就会!!!!!
  4. Tumbler QML Type
  5. 【高并发】多线程之无锁队列|性能优化
  6. (翻译)邀请好友模式(Invite friends)
  7. 实现僵尸跑酷游戏的 UGUI 实践
  8. 2022-05-14前端周报 巴厘岛项目交接完成
  9. HBuilderX 安装教程
  10. 《人物》:计算机世界第一人——艾兰·图灵(转贴)