1.依赖注入
(1) 依赖注入(DI)的概念:某个bean的依赖项,由容器来负责注入维护,而非我们自己手动去维护,以此来达到bean之间解耦的目的,如下
//情况一:不使用依赖注入
public class A {}
public class B {
//B依赖了A对象,这种依赖关系是由我们自己手动来维护的,编码于代码之中,是强依赖
private A a = new A();
}
//情况二:使用依赖注入
@Component
public class A {}
@Component
public class B {
//B依赖了A对象,这个A对象是由容器来提供的,无需我们关心
@Autowired
private A a;
}
(2) 依赖注入的两种方式
//例一:
//ExampleA继承自ExampleB
public class ExampleA extends ExampleB {}
public class ExampleB {}
public class Combine {
//Combine依赖了ExampleA和ExampleB
public Combine(ExampleA a, ExampleB b) {
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
<bean id="combine" class="cn.example.spring.boke.Combine">
<!-- Spring会按照类型进行精确匹配,因此1会被注入到构造函数的第二个参数b中,而2会被注入到构造函数的第一个参数a中,此时与这些构造函数标签声明的先后顺序无关 -->
<constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
<constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean>
</beans>
//例二:
//将Combine构造函数变更一下
public class Combine {
//构造函数参数均为ExampleB
public Combine(ExampleB b1, ExampleB b2) {
}
}
<!-- xml配置文件 -->
<bean id="combine" class="cn.example.spring.boke.Combine">
<!-- 此时无法进行精确匹配,因为构造函数参数均为ExampleB,这时就会按照这些构造函数标签声明的先后顺序进行依赖项的注入,
结果为1会被注入到构造函数的第一个参数b1中,而2会被注入到构造函数的第二个参数b2中,如果将这两个构造函数标签的声明顺序颠倒一下,结果也会随之相反 -->
<constructor-arg ref="exampleB"></constructor-arg> <!-- 1 -->
<constructor-arg ref="exampleA"></constructor-arg> <!-- 2 -->
</bean>
//例三
public class ExampleC {
public ExampleC(int number, String str) {
}
}
<!-- xml配置文件1 -->
<beans ...>
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 此时的Spring无法判断2000或50000究竟是一个int还是一个String,因此它首先会采取例二中的办法,按构造函数标签的声明顺序进行注入,结果为number被注入2000,str被注入字符串50000 -->
<constructor-arg value="2000"></constructor-arg>
<constructor-arg value="50000"></constructor-arg>
</bean>
</beans>
<!-- xml配置文件2 -->
<beans ...>
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 可以使用type属性,指定希望注入的参数的类型,来解决歧义问题,结果number被注入50000,str被注入字符串2000,但是如果构造函数参数列表中有多个类型相同的参数,则又会按照构造函数标签的声明顺序进行注入 -->
<constructor-arg type="java.lang.String" value="2000"></constructor-arg>
<constructor-arg type="int" value="50000"></constructor-arg>
</bean>
</beans>
<!-- xml配置文件3 -->
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 使用index属性,按构造函数参数索引位置进行注入,index属性为0的构造函数标签的值会被注入到构造函数的第一个参数,以此类推 -->
<constructor-arg index="1" value="50000"></constructor-arg>
<constructor-arg index="0" value="2000"></constructor-arg>
</bean>
<!-- xml配置文件4 -->
<bean id="c" class="cn.example.spring.boke.ExampleC">
<!-- 使用name属性,按照构造函数参数名称进行注入,不过要注意,Spring要求使用这种方式时必须开启debug flag或结合@ConstructorProperties注解一起使用,详见官方文档(此处存疑,因为在我本地可直接使用下面这种方式,无需其他配置) -->
<constructor-arg name="str" value="50000"></constructor-arg>
<constructor-arg name="number" value="2000"></constructor-arg>
</bean>
public class ExampleA {}
public class ExampleC {
private ExampleA exampleA;
//使用setter方法注入依赖项exampleA
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
}
<!-- 方法一:使用自动装配(autowire),即容器自动去寻找依赖项来进行注入 -->
<beans ...>
<!-- 被依赖项exampleA -->
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<!-- 注意,在基于xml的配置中,如要使用基于setter的自动装配,则要指定autowire属性为byName或byType,如果不指定,则autowire属性默认为no,即不进行依赖注入 -->
<bean id="exampleC" class="cn.example.spring.boke.ExampleC" autowire="byType"></bean>
</beans>
<!-- 方法二:使用手动装配 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<bean id="exampleC" class="cn.example.spring.boke.ExampleC">
<!-- 使用property标签手动注入,name即属性名称,ref代表注入对象 -->
<property name="exampleA" ref="exampleA"></property>
</bean>
</beans>
(3) 在依赖注入过程中,Spring会结合PropertyEditor一起使用,来将属性从一种类型转换为另一种类型,我们也可以利用PropertyEditor进行数据类型转换工作
(4) Spring对依赖项的解析规则:
(5) Spring在容器创建完成后便会对bean的配置元数据进行检验(此时不会进行依赖项的注入,直到bean被成功创建后才会进行注入)。单例bean在默认情况下会被提前实例化(即在容器被创建后创建),而其他作用域的bean只有在需要的时候才会被创建(懒加载)。在实际创建bean时,Spring会尽可能晚设置该bean的属性值和依赖项,因此Spring容器可能在创建的时候正常但在之后的使用中会产生异常
(6) 在不存在循环依赖的情况下,bean A如果有一个依赖项为bean B,则Spring容器会在调用bean A的setBeanB方法来注入bean B之前会完全配置好bean B,即Bean B的依赖项已被完全注入,其生命周期回调也已被执行,如下
/**
* 提供三个类,ExampleA,ExampleB和ExampleC,其中ExampleA依赖了ExampleB,而ExampleC又依赖了ExampleA,即B->A->C
* 此时,容器会先配置好ExampleB,再注入给ExampleA,而ExampleA配置完毕后,最后会被注入给ExampleC,可观察控制台打印语句的输出顺序
*/
public class ExampleB {
public ExampleB() {
System.out.println("ExampleB构造器...");
}
}
public class ExampleA {
public ExampleA() {
System.out.println("ExampleA构造器...");
}
private ExampleB exampleB;
public void setExampleB(ExampleB exampleB) {
System.out.println("ExampleA设置了" + exampleB);
this.exampleB = exampleB;
}
}
public class ExampleC {
public ExampleC() {
System.out.println("ExampleC构造器...");
}
private ExampleA exampleA;
public void setExampleA(ExampleA exampleA) {
System.out.println("ExampleC设置了" + exampleA);
this.exampleA = exampleA;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleC" class="cn.example.spring.boke.ExampleC">
<property name="exampleA" ref="exampleA"></property>
</bean>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="exampleB" ref="exampleB"></property>
</bean>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
</beans>
(7) 依赖注入的例子:
//例一:
public class ExampleA {}
public class ExampleB {
private int year;
private ExampleA exampleA;
public void setYear(int year) {
this.year = year;
}
public void setExampleA(ExampleA exampleA) {
this.exampleA = exampleA;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA"></bean>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB">
<!-- 注入其他bean -->
<property name="exampleA">
<ref bean="exampleA"></ref>
</property>
<!-- 注入基本类型 -->
<property name="year" value="20221222"></property>
</bean>
</beans>
//例二:
public class ExampleA {
public ExampleA(int number, String str) {
}
}
public class ExampleAFactory {
public static ExampleA createInstance(int number, String str) {
ExampleA exampleA = new ExampleA(number, str);
return exampleA;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleAFactory" factory-method="createInstance">
<!-- 使用constructor-arg标签,来给静态工厂方法提供参数 -->
<constructor-arg name="number" value="2022"></constructor-arg>
<constructor-arg name="str" value="12"></constructor-arg>
</bean>
</beans>
2.依赖项配置细节
(1) 简单值:比如一个int,boolean或String等,Spring会将这些值转换为参数的实际类型,如下所示
public class ExampleA {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="properties">
<!-- Spring会借助PropertyEditor机制来将下面这些我们所定义的值转换为Properties类型并进行注入 -->
<value>
name=abc
phone=15311111111
</value>
</property>
</bean>
</beans>
(2) idref标签:传递一个bean的id,注意是bean的id,是一个字符串,而不是bean对象本身,它的好处是Spring会提前进行校验,看这个id所代表的那个bean是否存在,防止你把bean的id给拼错了
public class ExampleB {}
public class ExampleA {
//注意这里是String类型,而不是ExampleB类型
private String exampleB;
public void setExampleB(String exampleB) {
this.exampleB = exampleB;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="exampleB">
<!-- 此处将exampleB这个字符串作为参数传给ExampleA的成员变量exampleB,而非将exampleB这个bean传过去 -->
<idref bean="exampleB"></idref>
</property>
<!-- 上面的等价于下面这个property标签,注意这个property标签内部使用的是value属性而不是ref属性 -->
<!-- <property name="exampleB" value="example"></property> -->
</bean>
</beans>
(3) 依赖其他的bean:通过ref标签,来引用同一容器或父容器中的bean,而不管它们是否配置在了同一个xml文件中,ref标签主要使用bean属性和parent属性,它们的值均为目标bean的id或name
<beans ...>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
<!-- ExampleA对象依赖了ExampleB对象 -->
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="exampleB">
<!-- 使用ref标签中的bean属性,来引用id为exampleB的bean进行注入,使用bean属性时,被引用的bean可在当前容器中,也可在当前容器的父容器中 -->
<ref bean="exampleB"></ref>
<!-- 使用ref标签中的parent属性,来引用父容器中id为parentExampleB的bean进行注入,parent属性适用于容器分层的时候,被引用的bean必须在当前容器的父容器中 -->
<!-- <ref parent="parentExampleB"></ref> -->
</property>
</bean>
</beans>
(4) Inner bean:Inner bean与匿名内部类相似,当一个bean标签位于一个property标签或constructor-arg标签内部时,就定义了一个Inner bean,定义Inner bean不需要指定它的id,name或scope,它们会被容器忽略掉,因为Inner bean始终是匿名的,与外部bean一起被创建,我们不能单独访问Inner bean或将其注入到其他的bean中
<beans ...>
<!-- ExampleA对象依赖了ExampleB对象 -->
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="exampleB">
<!-- ExampleB对象此时是一个Inner Bean -->
<bean class="cn.example.spring.boke.ExampleB">
</bean>
</property>
</bean>
</beans>
(5) 集合:list,set,map,和props标签分别用于配置List,Set,Map和Properties类型,如下
public class ExampleA {
private Properties properties;
private List<String> list;
private Map<String, Integer> map;
private Set<String> set;
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public void setSet(Set<String> set) {
this.set = set;
}
}
<!-- xml配置文件 -->
<beans ...>
<!-- 不仅可以向集合中注入一些基本的类型,还可注入容器中的bean -->
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="properties">
<props>
<prop key="name">abc</prop>
<prop key="phone">15311111111</prop>
</props>
</property>
<property name="list">
<list>
<value>My List</value>
<value>Test</value>
</list>
</property>
<property name="map">
<map>
<entry key="height" value="200"/>
<entry key="weight" value="400"/>
</map>
</property>
<property name="set">
<set>
<value>test1</value>
<value>test2</value>
</set>
</property>
</bean>
</beans>
(6) 合并集合:可以bean A中声明list,map,set或props标签,并设置bean B的parent属性为bean A且bean B中的list,map,set或props标签的merge属性为true来继承或覆盖bean A集合中的某些值,达到合并集合的目的,如下
//注意,下面这个ExampleA和ExampleB是相互独立的关系,它们之间不存在继承关系
public class ExampleA {
private List<String> list;
private Map<String, Integer> map;
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
}
public class ExampleB {
private List<String> list;
private Map<String, Integer> map;
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
@Override
public String toString() {
return "ExampleB{" +
"list=" + list +
", map=" + map +
'}';
}
}
<!-- xml配置文件 -->
<beans ...>
<!-- exampleA,一个普通的bean -->
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<property name="list">
<list>
<value>parent</value>
<value>common</value>
</list>
</property>
<property name="map">
<map>
<entry key="parent" value="1"></entry>
<entry key="common" value="2"></entry>
</map>
</property>
</bean>
<!-- 注意:exampleB要指定它的parent属性,表明它要继承哪个bean -->
<bean id="exampleB" class="cn.example.spring.boke.ExampleB" parent="exampleA">
<property name="list">
<!-- 这里将merge属性设置为true,表明需要继承parent bean中的集合 -->
<list merge="true">
<value>common</value>
<value>son</value>
</list>
</property>
<property name="map">
<map merge="true">
<entry key="common" value="233"></entry>
<entry key="son" value="2333"></entry>
</map>
</property>
</bean>
</beans>
//获取容器中的exampleB,并打印,可以看到parent bean中集合中的元素被继承覆盖:
ExampleB{list=[parent, common, common, son], map={parent=1, common=233, son=2333}}
使用集合合并时需注意,不能合并不同类型的集合(例如将Map和List进行合并),如果这样做,Spring会抛出异常,此外merge属性必须在子bean中指定,在父bean上指定merge属性是多余的
(7) 注入null或空字符串
public class ExampleA {
private String str1;
private String str2;
public void setStr1(String str1) {
this.str1 = str1;
}
public void setStr2(String str2) {
this.str2 = str2;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<!-- 此时注入的是空字符串,等价于exampleA.setStr1("") -->
<property name="str" value=""></property>
<!-- 此时注入的是null,等价于exampleA.setStr2(null) -->
<property name="str2">
<null/>
</property>
</bean>
</beans>
(8) p命名空间,用来替换掉property标签;c命名空间,用来替换掉constructor-arg标签,它们的作用就是简化代码,其他就没什么了,具体实例省略,详见官方文档
(9) 向嵌套属性中注入值:要求组件嵌套路径中的对象都不能为null,如下,exampleB.exampleC.str这个路径中exampleB和exampleC都不能为null,否则会抛出空指针异常
//这里有3个类,其中ExampleA依赖了ExampleB,ExampleB又依赖了ExampleC,ExampleC有一个属性str
//注意这里的ExampleA和ExampleB都要提供setter和getter方法,用于获取,设置外层类,而ExampleC只需提供setter方法来用于注入str属性
public class ExampleA {
private ExampleB exampleB;
public ExampleB getExampleB() {
return exampleB;
}
public void setExampleB(ExampleB exampleB) {
this.exampleB = exampleB;
}
}
public class ExampleB {
private ExampleC exampleC;
public ExampleC getExampleC() {
return exampleC;
}
public void setExampleC(ExampleC exampleC) {
this.exampleC = exampleC;
}
}
public class ExampleC {
private String str;
public void setStr(String str) {
this.str = str;
}
}
<!-- xml配置文件 -->
<beans ...>
<bean id="exampleC" class="cn.example.spring.boke.ExampleC"></bean>
<bean id="exampleB" class="cn.example.spring.boke.ExampleB"></bean>
<bean id="exampleA" class="cn.example.spring.boke.ExampleA">
<!-- 向exampleA注入嵌套的属性,如果中间过程中有一处为null,则会抛出空指针异常 -->
<property name="exampleB" ref="exampleB"></property>
<property name="exampleB.exampleC" ref="exampleC"></property>
<!-- 将嵌套的属性str设置为1233 -->
<property name="exampleB.exampleC.str" value="1233"></property>
</bean>
</beans>
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
有什么方法可以告诉sidekiq一项工作依赖于另一项工作,并且在后者完成之前无法开始? 最佳答案 仅使用Sidekiq;答案是否定的。正如DickieBoy所建议的那样,您应该能够在依赖作业完成时将其启动。像这样。#app/workers/hard_worker.rbclassHardWorkerincludeSidekiq::Workerdefperform()puts'Doinghardwork'LazyWorker.perform_async()endend#app/workers/lazy_worker.rbclassLaz
Ruby有一些不错的文档生成器,例如Yard、rDoc,甚至Glyph。问题是Sphinx可以做网站、PDF、epub、LaTex等。它在重组文本中完成所有这些事情。在Ruby世界中有替代方案吗?也许是程序的组合?如果我也能使用Markdown就更好了。 最佳答案 自1.0版以来,Sphinx有了“域”的概念,它是从Python和/或C以外的语言标记代码实体(如方法调用、对象、函数等)的方法。有一个rubydomain,所以你可以只使用Sphinx本身。您唯一会缺少的(我认为)是Sphinx使用autodoc从源代码自动创建文档
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/