学前要求: 已学完本系列(1)、(2)、(3)
*系列(3)的结尾我们学习了bean的生命周期的案列,现在我们讲解一个依赖注入
依赖注入
在Spring中,常用的依赖注入方式主要分为两种:构造函数注入(Constructor Injection)和属性注入(Setter Injection)。
构造函数注入(官方名:构造器注入):这种方式是通过Java类的构造函数来实现对象之间的依赖注入。在容器创建bean的时候,会自动调用bean的构造函数,并将相关的参数值传递到构造函数中去。我们可以根据需要提供一个或多个包含所需参数的构造函数,在应用程序启动时使组件具有正确的状态。
属性注入(官方名:setter注入):这种方式是通过setter方法来实现对象之间的依赖注入。在容器创建bean的过程中,会先创建一个空对象,然后通过属性注入的方式来设置该bean的相应属性。以set()方法为例子,在xml文件中可以设置一个property属性,指定set()方法所需要设置的参数。
注入目标:数据(基本数据类型:byte、short、int、long、float、double、char、boolean等8种数据类型 | 引用数据类型:String、Array、类等)
引用数据类型表示一个指向对象存储位置的引用,在使用时需要通过实例化关键字创建对象以及使用相应的引用变量访问对象。
1.setter注入的引用类型
新建一个TestDemo8
public class TestDemo8{
// 新建后就不用管了,让TestDemo7来
}
再新建一个TestDemo7
public class TestDemo7 {
private TestDemo8 testDemo8;
// setter方法,用于注入 TestDemo8 类型的属性
public void setTestDemo8(TestDemo8 testDemo8) {
this.testDemo8 = testDemo8;
}
// getter 方法,用于获取 TestDemo8 类型的属性值
public TestDemo8 getTestDemo8() {
return testDemo8;
}
}
不要觉得setTestDemo8没有使用到,其实当在Spring配置文件中使用元素进行依赖注入时,会根据被注入的bean类的属性类型以及名称等信息,自动找到对应的setter方法,并调用该方法将值注入进去。
不信可以删除set方法,会报错:Bean property ‘testDemo8’ is not writable or has an invalid setter method.
配置文件applicationContext.xml
<!-- 注入的使用 -->
<bean id="testdemo7" class="com.javala.dao.TestDemo7">
<property name="testDemo8" ref="testdemo8"/>
<property name="testDemo9" ref="testdemo9"/> <!-- name对应TestDemo7中的那个数据,ref对应下面的bean的id -->
</bean>
<bean id="testdemo8" class="com.javala.dao.TestDemo8"/>
<bean id="testdemo9" class="com.javala.dao.TestDemo9"/>
最后使用案例类
public class App7 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TestDemo7 testdemo7 = (TestDemo7) ctx.getBean("testdemo7");
System.out.println(testdemo7.getTestDemo8());
}
}
2.setter注入的简单类型类型
新建TestDemo9
public class TestDemo9 {
private int num;
private String str;
public void setNum(int num) {
this.num = num;
}
public void setStr(String str) {
this.str = str;
}
public boolean say() {
System.out.println("say something!"+",num:"+num+",str:"+str);
return false;
}
}
配置applicationContext.xml
<!-- 注入的使用 -->
<bean id="testdemo7" class="com.javala.dao.TestDemo7">
<property name="testDemo8" ref="testdemo8"/>
<property name="testDemo9" ref="testdemo9"/> <!-- name对应TestDemo7中的那个数据,ref对应下面的bean的id -->
</bean>
<bean id="testdemo8" class="com.javala.dao.TestDemo8"/>
<bean id="testdemo9" class="com.javala.dao.TestDemo9">
<property name="num" value="999"/> <!-- name就是值的名称,value就是值的大小 -->
<property name="str" value="感冒灵"/>
</bean>
使用案例类
public class App7 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TestDemo7 testdemo7 = (TestDemo7) ctx.getBean("testdemo7");
System.out.println(testdemo7.getTestDemo8());
System.out.println(testdemo7.getTestDemo9().say());
}
}
如果到这里没有遇到问题,那就奇怪了,因为我故意没有讲TestDemo9的出现
这便是可以同时引用多个(出现TestDemo9的出现原因)
构造器注入
1.引用类型
新建TestDemo11
public class TestDemo11 {
}
再新建TestDemo10
public class TestDemo10 {
private TestDemo11 testDemo11;
public TestDemo10(TestDemo11 testDemo11la) {
this.testDemo11 = testDemo11la;
}
}
配置文件applicationContext.xml
<!-- 构造器注入 -->
<bean id="testdemo10" class="com.javala.dao.TestDemo10">
<!-- name是形参的名 -->
<constructor-arg name="testDemo11la" ref="testdemo11"/>
</bean>
<bean id="testdemo11" class="com.javala.dao.TestDemo11"/>
使用案例类不变
public class App8 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TestDemo10 testdemo10 = (TestDemo10) ctx.getBean("testdemo10");
System.out.println(testdemo10);
}
}
2.简单类型
新建TestDemo12
public class TestDemo12 {
private final int num;
private final String str;
public TestDemo12(int num, String str) {
this.num = num;
this.str = str;
}
public boolean say() {
System.out.println("num="+num+",str="+str);
return true;
}
}
TestDemo10稍加更改
public class TestDemo10 {
private TestDemo11 testDemo11;
private TestDemo12 testDemo12;
public TestDemo10(TestDemo11 testDemo11, TestDemo12 testDemo12) {
this.testDemo11 = testDemo11;
this.testDemo12 = testDemo12;
}
public TestDemo12 getTestDemo12() {
return testDemo12;
}
}
// 加个testDemo12
配置文件applicationContext.xml中改一改
<!-- 构造器注入 -->
<bean id="testdemo10" class="com.javala.dao.TestDemo10">
<!-- name是形参的名 -->
<constructor-arg name="testDemo11la" ref="testdemo11"/>
<constructor-arg name="testDemo12" ref="testdemo12"/>
</bean>
<bean id="testdemo11" class="com.javala.dao.TestDemo11"/>
<bean id="testdemo12" class="com.javala.dao.TestDemo12">
<!-- 下面是简单数据注入 -->
<constructor-arg name="num" value="666"/>
<constructor-arg name="str" value="您真刑"/>
</bean>
使用案例类
public class App8 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
TestDemo10 testdemo10 = (TestDemo10) ctx.getBean("testdemo10");
System.out.println(testdemo10);
System.out.println(testdemo10.getTestDemo12().say());
}
}
到这差不多就已经把四种注入方式讲解完毕,下面说一些小细节
前面故意使用了testDemo11la这个奇怪的名字就是为了告诉你这个是采用的形参
耦合度(Coupling)是指软件系统中,不同模块之间相互连接的紧密程度。当模块A需要访问模块B中的一些功能时,就会产生一定的耦合度,而这种耦合度应该尽可能地降低。
那多不好呀,耦合度太高了,得减低呀!
解决方式一:
将name属性改为类型锁定
例如:type=“int” | type=“java.lang.String”
缺点明显,就不讲了
解决方式二:
将name属性改为位置
例如:index=“0” | index=“1” (=0为第一个参数,=1为第二个参数)
缺点呢~哈哈哈
这个就让它过去,我们再说说这四种注入在使用时该如何选择
如何选择注入方式
在Spring框架中,我们可以使用以下四种方式来进行依赖注入:
构造器注入(Constructor Injection):将需要注入的依赖关系通过构造函数的参数列表进行指定。
Setter方法注入(Setter Injection):将需要注入的依赖关系通过setter方法进行指定。
成员变量注入(Field Injection):直接将需要注入的依赖关系作为成员变量声明,并使用自动装配实现依赖注入。
接口注入(Interface Injection):一般不太使用。
它们各有优劣,根据具体应用场景来选择最适合的依赖注入方式。通常来说,如果依赖关系是必须被注入的,则首选构造器注入;如果依赖关系是可选的或会变化的,则可以考虑Setter方法注入和成员变量注入。
构造器注入可以保证依赖关系的不可变性和完整性,因此更加安全和可靠。但是在调用时需要传入大量的参数,当依赖关系很多时会增加代码的编写难度。同时,构造器注入也可能造成循环依赖的问题,需要特别注意。
Setter方法注入则避免了构造器注入的循环依赖问题,并且对于那些不是必须的依赖关系,可以通过设置默认值或者直接留空避免调用时传入大量参数。但是Setter方法注入可能会造成对象不完整的问题,为防止这种问题出现需要在setter方法内添加必要的检查。
成员变量注入使用方便、简单,但可能导致依赖过于明显,降低了数据的封装性,也可能导致循环依赖等问题。同时,它还增加了代码的耦合度,使得代码难以测试和维护。
总结
那么学到这我们就已经掌握了四种注入方式
我是哈利巴多先生,如果觉得不错,还望多多鼓励(文章不定时更新)