| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> Spring系列3:bean实例化的方式知多少 -> 正文阅读 |
|
[Java知识库]Spring系列3:bean实例化的方式知多少 |
本文内容
通过构造函数实例化beanSpring中可以通过空构造函数或是默认构造函数来实例bean,直接上案例。 定义一个简单类
配置文件
来个测试测试进行测试
运行结果
通过静态工厂方法实例化bean在定义使用静态工厂方法创建的 bean 时,使用 class 属性来指定包含静态工厂方法的类,并使用名为 定义一个类,包含一个静态工厂方法返回该类的实例
配置文件中配置静态工厂方法实例化bean
测试类
通过实例工厂方法实例化bean与通过静态工厂方法进行实例化类似,使用实例工厂方法进行实例化从容器中调用现有 bean 的非静态方法来创建新 bean。 要使用此机制, 定义一个类及其工厂类
配置文件中进行bean定义
测试类输出通过实例工厂方法创建的bean
结果输出,输出了
总结本篇主要说明了3中实例化bean的方式,可以按照实际需要进行选择使用。下一篇介绍bean中的依赖是如何注入的。 Spring系列4:依赖注入的2种方式本文内容
基于构造器的依赖注入案例定义2个简单的bean类,BeanOne 和 BeanTwo,前者依赖后者。
通过xml配置文件实现bean定义和依赖注入
来个测试类验证下注入
输出如下
对照配置文件BeanOne的3个依赖都通过构造器的方式进行注入了,符合预期,很简单。
|
元素名 | 作用 |
---|---|
name | 参数名 |
index | 参数索引,0开始 |
type | 参数类型 |
value | 值 |
ref | bean引用 |
例如案例中的配置
<bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
<constructor-arg name="age" index="0" type="int" value="20"/>
<constructor-arg name="name" index="1" type="java.lang.String" value="xxx"/>
<constructor-arg name="beanTwo" index="2" type="com.crab.spring.ioc.demo02.BeanTwo" ref="bean2"/>
</bean>
注意: 在没有引起歧义的情况下,上面的部分元素并不是都必须配置的。如指定了index
时可以定位参数位置,那么name
是可以不配置的,又如通过ref
引用依赖bean,type
可以省略
<bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
<constructor-arg index="0" type="int" value="20"/>
<constructor-arg index="1" type="java.lang.String" value="xxx"/>
<constructor-arg index="2" ref="bean2"/>
</bean>
当在<constructor />
使用 name
元素指定构造函数中的参数名时,尤其要方法参数名编译后是否保留的情况。举个例子
// 编译前的方法参数
public BeanOne(int age, String name, BeanTwo beanTwo)
// 编译后的方法参数
public BeanOne(int var1, String var2, BeanTwo var3)
编译后方法参数被编译成var1
这种形式会导致仅指定name
的构造注入失效。
如何解决?提供2种方法
Maven
编译插件添加编译参数,保留参数名,pom
文件下增加如下插件配置
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>UTF8</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
使用 @ConstructorProperties
JDK 注释显式命名构造函数参数
/**
* 构造函数,用于依赖注入,定义3个依赖
* @param age
* @param name
* @param beanTwo
*/
@ConstructorProperties({"age", "name", "beanTwo"}) // 显式声明构造参数名称
public BeanOne(int age, String name, BeanTwo beanTwo) {
this.age = age;
this.name = name;
this.beanTwo = beanTwo;
}
基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。
来一个简单类BeanThree
依赖BeanOne
和BeanTwo
并提供了Setter
方法来设置。
package com.crab.spring.ioc.demo02;
/**
* @author zfd
* @version v1.0
* @date 2022/1/13 8:18
*/
public class BeanThree {
private BeanTwo beanTwo;
private BeanOne beanOne;
public void setBeanTwo(BeanTwo beanTwo) {
this.beanTwo = beanTwo;
}
public void setBeanOne(BeanOne beanOne) {
this.beanOne = beanOne;
}
}
对应的配置文件可以通过标签property
中的ref
或name
来设置属性引用或是属性值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="bean2" class="com.crab.spring.ioc.demo02.BeanTwo"/>
<!--构造函数注入-->
<bean id="bean1" class="com.crab.spring.ioc.demo02.BeanOne">
<constructor-arg name="age" index="0" type="int" value="20"/>
<constructor-arg name="name" index="1" type="java.lang.String" value="xxx"/>
<constructor-arg name="beanTwo" index="2" type="com.crab.spring.ioc.demo02.BeanTwo" ref="bean2"/>
</bean>
<!--setter注入-->
<bean id="bean3" class="com.crab.spring.ioc.demo02.BeanThree">
<!-- 1 ref元素-->
<property name="beanOne" ref="bean1"></property>
<!-- 2 ref标签-->
<property name="beanTwo">
<ref bean="bean2"></ref>
</property>
<property name="name" value="xxxx"/>
</bean>
</beans>
运行测试类和结果,可见依赖注入成功
public class demo02Test {
@Test
public void test_construct() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo02/spring1.xml");
BeanOne bean1 = context.getBean("bean1", BeanOne.class);
System.out.println(bean1);
System.out.println("演示Setter注入");
BeanThree beanThree = context.getBean(BeanThree.class);
System.out.println(beanThree);
context.close();
}
}
BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24}
演示Setter注入
BeanThree{name='xxxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24, beanOne=BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24}}
什么是循环依赖,如何处理?
如果主要使用构造函数注入,则可能会创建一个不可解析的循环依赖场景。
例如:类A需要类B的一个实例通过构造函数注入,类B需要类A的一个实例通过构造函数注入。如果将类A和类B的bean配置为相互注入,Spring IoC容器会在运行时检测此循环引用,并抛出
BeanCurrentlyInCreationException
。一个可能的解决方案是编辑一些由setter而不是构造函数配置的类的源代码。或者,避免构造函数注入,只使用setter注入。换句话说,可以使用setter注入配置循环依赖项。
与典型情况(没有循环依赖关系)不同,bean A 和 bean B 之间的循环依赖关系强制其中一个 bean 在完全初始化之前注入另一个 bean(典型的先有鸡还是先有蛋的场景)。
选择用构造器注入还是Setter注入?
Spring官方的推荐,构造函数用于强制依赖项,将 setter 方法或配置方法用于可选依赖项。请注意,在 setter 方法上使用 @Required 注释可用于使属性成为必需的依赖项;然而,带有参数的编程验证的构造函数注入是更可取的。
Spring 团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。作为旁注,大量的构造函数参数是一种不好的代码气味,这意味着该类可能有太多的职责,应该重构以更好地解决适当的关注点分离。 Setter 注入应该主要只用于可以在类中分配合理默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。 setter 注入的一个好处是 setter 方法使该类的对象可以在以后重新配置或重新注入。
有时,在处理没有源代码的第三方类时,如果第三方类没有公开任何 setter 方法,那么构造函数注入可能是 DI 的唯一可用形式。
本文演示2种依赖注入的方式:构造函数注入和Setter方法注入,并对比如何选择这2种方式。下一篇继续深入依赖注入。
知识分享,转载请注明出处。学无先后,达者为先!
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 7:16:18- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |