文章目录
一、Spring的特点
- 方便解耦,简化开发
- 方便集成各种优秀框架
- AOP编程的支持
- 降低JavaEE API的使用难度
- 声明式事务的支持
- 开源项目
- 方便程序测试
体系结构图
核心概念
控制反转(IOC:Inversion of Control):将对象的控制权由硬编码的程序转交给Spring容器
解释:传统的JavaSE程序通过new关键字进行创建对象,对象的控制权在硬编码的Java程序。实现了IOC设计的Spring程序,对象的控制权在Spring容器。
Spring配置文件的bean节点相关属性
- Spring的bean节点中具备很多属性,如下表
属性名称 | 说明 |
---|---|
id | 唯一标识;方便在别的地方可以找到这个对象 |
class | 类的全称,注意:全称是包名加上类名 |
lazy-init | 是否延迟加载 |
init-method | 对象初始化完成之后就执行的方法 |
destroy-method | 当前对象被销毁之前Spring会自动调用的方法 |
scope | 单例还是多例 |
depends-on | 强制要求实例化这个bean之前必须先实例化另一个bean |
- 其中还有property子节点,property子节点的属性如下
属性名称 | 说明 |
---|---|
name | 属性名 |
value | 属性值 |
ref | 对象类型的引入 |
-
示例代码:
- Spring配置文件
<bean id="user" class="com.iflytek.entity.User" init-method="fun1"
destroy-method="fun2" scope="prototype" depends-on="dept" >
<property name="name" value="张三"></property>
<property name="age" value="18"></property>
</bean>
<bean id="dept" class="com.iflytek.entity.Dept"></bean>
- 测试类
public static void main(String[] args) {
// ApplicationContext 是一个接口;不可直接new;可导入源码查看,源码在下载spring的jar中resources结尾的jar包,所以创建实现类对象
//参数为:spring配置文件的文件名称
//注意:ApplicationContext初始化之时;配置的需要spring容器帮助创建的对象也就创建了
ApplicationContext ctx=new ClassPathXmlApplicationContext("ApplicationContext.xml");
//根据id值来获取spring容器帮助你创建的User对象,需要强转
User user=(User)ctx.getBean("user");
//打印user对象的相关对象
System.out.println(user);
//User user2=(User)ctx.getBean("user");
// ((ClassPathXmlApplicationContext)ctx).close();
}
- 扩展:Spring的上下文也可以同时加载多个Spring的配置文件,例:
ApplicationContext ctx=new ClassPathXmlApplicationContext("a.xml","b.xml");
//或:
ApplicationContext ctx=new ClassPathXmlApplicationContext(new String[] {"a.xml","b.xml"});
二、依赖注入的三种方式
- 实体类Customer:
package com.entity;
public class Customer {
private String uname;
private BankCard bankCard;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public BankCard getBankCard() {
return bankCard;
}
public void setBankCard(BankCard bankCard) {
this.bankCard = bankCard;
}
public Customer(String uname) {
this.uname = uname;
}
public Customer() {
super();
}
public Customer(String uname, BankCard bankCard) {
super();
this.uname = uname;
this.bankCard = bankCard;
}
}
- 实体类BankCard
package com.entity;
public class BankCard {
private String bcId;
private IdCard ic;
public String getBcId() {
return bcId;
}
public void setBcId(String bcId) {
this.bcId = bcId;
}
public IdCard getIc() {
return ic;
}
public void setIc(IdCard ic) {
this.ic = ic;
}
}
- 实体类IdCard
package com.entity;
public class IdCard {
private String idCard;
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
}
1、Set方法注入
<bean id="cust" class="com.entity.Customer">
<property name="uname" value="张学友"></property>
<property name="bankCard" ref="bc"></property>
</bean>
<bean id="bc" class="com.entity.BankCard">
<property name="bcId" value="123456789"></property>
<property name="ic" ref="icc"></property>
</bean>
<bean id="icc" class="com.entity.IdCard">
<property name="idCard" value="1111111111111"></property>
</bean>
2、构造方法注入
注意:一定要有该构造方法,index:参数的位置,ref:对象类型的引用,type:参数对象的类型,value:值
<bean id="cust22" class="com.entity.Customer">
<constructor-arg index="0" type="String" value="刘德华"></constructor-arg>
</bean>
<bean id="cust322" class="com.entity.Customer">
<constructor-arg index="0" type="String" value="刘德华2"></constructor-arg>
<constructor-arg index="1" type="com.entity.BankCard" ref="bc"></constructor-arg>
</bean>
3、 静态工厂注入
<bean id="qa" class="com.factory.BankCardFactory" factory-method="createBankCard"></bean>
<bean id="cust4" class="com.entity.Customer">
<property name="uname" value="周杰伦"></property>
<property name="bankCard" ref="qa"></property>
</bean>
注意点:
需要新建一个类,类中包含一个静态方法,factory-method :静态方法名
package com.factory;
import com.entity.BankCard;
public class BankCardFactory {
public static BankCard createBankCard() {
return new BankCard();
}
}
三、复杂数据类型注入
- 创建班级实体类和学生实体类
1、 数组类型注入
<!--Array与list节点都可以,但是一般推荐使用list-->
<bean id="c1" class="com.entity.Clazz">
<property name="clazzName" value="Java6班"></property>
<property name="sname">
<!--
<array>
<value>jack1</value>
<value>jack2</value>
<value>jack3</value>
</array>
-->
<list>
<value>jack1</value>
<value>jack2</value>
<value>jack3</value>
</list>
</property>
</bean>
2、List集合类型注入
<bean id="c1" class="com.entity.Clazz">
<property name="clazzName" value="Java6班"></property>
<property name="stus">
<list>
<ref bean="s1"/>
<ref bean="s2"/>
<ref bean="s3"/>
</list>
</property>
</bean>
<bean id="s1" class="com.entity.Student">
<property name="sname" value="jack1"></property>
</bean>
<bean id="s2" class="com.entity.Student">
<property name="sname" value="jack2"></property>
</bean>
<bean id="s3" class="com.entity.Student">
<property name="sname" value="jack3"></property>
</bean>
3、Set集合类型注入
<bean id="c1" class="com.entity.Clazz">
<property name="clazzName" value="Java6班"></property>
<property name="stuSet">
<set>
<ref bean="s1"/>
<ref bean="s2"/>
<ref bean="s3"/>
</set>
</property>
</bean>
<bean id="s1" class="com.entity.Student">
<property name="sname" value="jack1"></property>
</bean>
<bean id="s2" class="com.entity.Student">
<property name="sname" value="jack2"></property>
</bean>
<bean id="s3" class="com.entity.Student">
<property name="sname" value="jack3"></property>
</bean>
4、Map集合类型注入
<bean id="c1" class="com.entity.Clazz">
<property name="clazzName" value="Java6班"></property>
<property name="stuMap">
<map>
<entry key="1" value-ref="s1"></entry>
<entry key="2" value-ref="s2"></entry>
<entry key="3" value-ref="s3"></entry>
</map>
</property>
</bean>
<bean id="s1" class="com.entity.Student">
<property name="sname" value="jack1"></property>
</bean>
<bean id="s2" class="com.entity.Student">
<property name="sname" value="jack2"></property>
</bean>
<bean id="s3" class="com.entity.Student">
<property name="sname" value="jack3"></property>
</bean>
5、Date时间类型注入
- 在学生实体类中添加属性生日
package com.entity;
import java.util.Date;
//学生类
public class Student {
private String sname;//学生姓名
private Date sbirth;
public Date getSbirth() {
return sbirth;
}
public void setSbirth(Date sbirth) {
this.sbirth = sbirth;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "Student [sname=" + sname + "]";
}
}
- Spring配置文件中
<bean id="s4" class="com.entity.Student">
<property name="sbirth" >
<!-- 下列配置 类似于下列转换
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
sdf.parse("2020-09-09");
-->
<bean factory-bean="sdf" factory-method="parse">
<constructor-arg value="2020-09-09"></constructor-arg>
</bean>
</property>
</bean>
<bean id="sdf" class="java.text.SimpleDateFormat">
<constructor-arg index="0" name="pattern" value="yyyy-MM-dd"></constructor-arg>
</bean>
四、Spring的自动装配
1、之前注入的写法
<bean id="cust" class="com.entity.Customer">
<property name="uname" value="张学友"></property>
<property name="bankCard" ref="bc"></property>
</bean>
<bean id="bc" class="com.entity.BankCard">
<property name="bcId" value="123456789"></property>
</bean>
2、 升级版——>将ref移除掉(根据类型来自动装配——>byType)
<bean id="cust" class="com.entity.Customer" autowire="byType">
<property name="uname" value="张学友"></property>
</bean>
<bean id="bc" class="com.entity.BankCard">
<property name="bcId" value="123456789"></property>
</bean>
3、根据类型来自动装配的弊端
当Spring的主配置文件存在两个相同类型时,例如上述例子注入了两个BankCard类型的对象即会产生问题
<bean id="bc" class="com.entity.BankCard">
<property name="bcId" value="1111 2222 3333 5555"></property>
</bean>
<bean id="bankCard" class="com.entity.BankCard">
<property name="bcId" value="1111 2222 3333 4444"></property>
</bean>
<bean id="cust" class="com.entity.Customer" autowire="byType">
<property name="uname" value="jack"></property>
</bean>
4、错误信息
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.entity.BankCard' available: expected single matching bean but found 2: bc,bankCard
5、 解释
因为采用的是根据类型来进行自动装配,但是在装配的过程中发现有两个BankCard的类型可以装入,具体装配哪一个就无法确定了,所以就会出现异常信息
6、 解决方法
将根据类型自动装配改成根据名称来进行自动装配——>(byName);根据属性的名称与bean节点id的值是否一致来进行自动装配,即Customer中BankCard的属性名称bankCard;那么即会自动装配进id值为bankCard的bean节点对象
<bean id="bc" class="com.entity.BankCard">
<property name="bcId" value="1111 2222 3333 5555"></property>
</bean>
<bean id="bankCard" class="com.entity.BankCard">
<property name="bcId" value="1111 2222 3333 4444"></property>
</bean>
<bean id="cust" class="com.entity.Customer" autowire="byName">
<property name="uname" value="jack"></property>
</bean>
7、 自动装备再升级
将autowire的属性写入bean中即表示这个bean的注入为自动注入,同时也可以写在Spring的命名空间上,表示全文件都采用自动注入
8、 总结
Spring的自动装配共有两种方式,详情见下表
自动装配 | 特点 |
---|---|
byType | 根据类型来自动注入对象的文字属性 例如:Customer类的bankCard属性的类型BankCard spring会自动查找spring配置文件中所有符合BankCard类型的bean对象;如果找到一个符合类型的或多个类型都符合,则报错 |
byName | 根据名称来自动注入对象的子对象属性 ;例如:Customer类中有属性名叫bankCard和spring文件中注入的BankCard类的bean的id相同,则自动注入 |