set注入复杂类型ref属性
测试准备
准备一个UserDao类和UserService类 , 其中 UserService类中有UserDao类型的属性
public class UserDao {
public void insert(){
System.out.println("正在保存用户数据");
}
}
public class UserService {
private UserDao userDao;
// 使用set方式注入,必须提供set方法, 反射机制要调用这个方法给属性赋值的
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save(){
userDao.insert();
}
}
注入外部Bean(常用)
注入外部Bean的方式:在外部注册一个bean, 然后通过外部bean的id配合property标签的ref引用属性进行注入
- 这个注入的 bean相当于是从容器中拿的, 直接修改注入的bean或外部bean中注入该bean的属性值中的一个都会引起另一个变化
- 对于property标签来说,ref 属性也可以采用ref标签的方式,但使用 ref 属性是多数的
<?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">
<!--配置dao-->
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<!--配置service-->
<!-- name属性的属性值怎么写:属性set方法的方法名,去掉set,然后把剩下的单词首字母变小写。-->
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
<!--对于property标签来说,ref属性也可以采用标签的方式,但使用ref属性是多数的-->
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<ref bean="userDaoBean"/>
</property>
</bean>
</beans>
注入内部Bean(了解)
注入内部Bean的方式:在bean标签中嵌套bean标签 , 内部bean的id是没有用的 , 不能通过容器被获取到所以不用写id
<?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="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<bean class="com.powernode.spring6.dao.UserDao"/>
</property>
</bean>
</beans>
set 注入的核心实现原理
set注入是基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值 , 让两个对象之间产生关系
- set 注入要求实体类的属性必须对外提供set方法, 并且想让Spring调用对应的set方法, 还需要设置property标签
- 先拿到 property 标签中 name 的属性值 ,然后首字母大写前面加上set , 最后通过反射机制调用实体类中对应的 setXxx 方法为该属性赋值
- set注入只与实体类属性对应的set方法的方法名有关,与属性名无关(如果所有getter/setter都自动生成那么property 标签的name属性的值就可以写属性名)
- 对于实体类中没有赋值的属性 , 遵循属性默认赋值规范
set注入简单类型value属性
简单类型的注入和分类
Spring的源码来分析有哪些简单类型:BeanUtils类(按两次shift键搜索)
- String类或其他的 CharSequence子类 , Number子类 , Date子类 , Enum子类 , URI , URL , Temporal子类 , Locale , Class , 包括以上简单值类型对应的数组类型
public class BeanUtils{
//........
//ctrl + F12 搜索isSimpleValueType方法
public static boolean isSimpleValueType(Class<?> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||Enum.class.isAssignableFrom(type) ||
//String 实现了 CharSequence
CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
//java8 提供的时间和时区类型
Temporal.class.isAssignableFrom(type) ||
URI.class == type || URL.class == type ||
//语言类型
Locale.class == type || Class.class == type));
}
//........
}
注入部分简单类型
- 如果把Date当做简单类型的话,日期字符串格式必须符合Date的toString()方法格式Fri Sep 30 15:26:38 CST 2022,否则无法赋值给Date类型的属性
- 所以在实际开发中我们一般不会把Date当做简单类型,采用ref给Date类型的属性赋值
- spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的即必须存在 , 如果不存在则报错
public class A {
private String str;
private Date date;
private Season season;
private URI uri;
private URL url;
private LocalDate localDate;
private Locale locale;
private Class clazz;
// 生成setter方法
// 生成toString方法
}
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
<?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="a" class="com.powernode.spring6.beans.A">
<property name="str" value="zhangsan"/>
<!--注意:value后面的日期字符串格式不能随便写,必须是Date对象toString()方法执行的结果,否则报错提示无法转换成Date类型-->
<!--如果想使用其他格式的日期字符串,就需要进行特殊处理了,具体怎么处理后面再说-->
<property name="date" value="Fri Sep 30 15:26:38 CST 2022"/>
<property name="season" value="WINTER"/>
<property name="uri" value="/save.do"/>
<!--spring6之后,会自动检查url是否有效,如果无效会报错-->
<property name="url" value="http://www.baidu.com"/>
<property name="localDate" value="EPOCH"/>
<!--java.util.Locale 主要在软件的本地化时使用,它本身没有什么功能,更多的是作为一个参数辅助其他方法完成输出的本地化-->
<property name="locale" value="CHINESE"/>
<property name="clazz" value="java.lang.String"/>
</bean>
</beans>
手写数据源并自动注入属性值
我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息例如driver、url、username、password等
//手写一个数据源并交给Spring取管理
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
//属性的set和toString方法....
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
我们给driver、url、username、password四个属性分别提供了setter方法,就可以使用spring的依赖注入完成数据源对象的创建和其属性的赋值
<?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">
<!--让Spring管理我们的数据源-->
<bean id="dataSource" class="com.powernode.spring6.beans.MyDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
</beans>
构造注入
测试准备
准备一个OrderDao类和OrderService类 , 其中OrderService类中有OrderDao类型的属性
public class OrderDao {
public void deleteById(){
System.out.println("正在删除订单");
}
}
public class UserDao {
public void insert(){
System.out.println("正在添加用户");
}
}
public class OrderService {
private OrderDao orderDao;
private UserDao userDao;
// 通过反射机制调用构造方法给属性赋值(构造方法有两个参数)
public OrderService(OrderDao orderDao, UserDao userDao) {
this.orderDao = orderDao;
this.userDao = userDao;
}
// 构造方法重载
public OrderService(Integer a, UserDao userDao) {
this.userDao = userDao;
}
public void delete(){
orderDao.deleteById();
userDao.insert();
}
}
构造注入的核心原理
通过调用构造方法来给属性赋值, 每个constructor-arg标签对应构造器的一个形参 , Spring先通过constructor-arg标签的个数确定调用哪个构造方法
- set注入是对象创建好后注入的 , 构造注入是对象实例化过程当中注入
构造方法有多个参数,通过index和name属性为构造方法的形参赋值, 对于没有赋值的属性 , 遵循属性的默认赋值规范
- 如果既不指定下标 , 也不指定参数名字 , Spring也会根据实参类型自动推断给匹配的形参赋值
<?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="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<!--这里利用构造方法上参数的下标为属性赋值-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<!--指定构造方法的第一个参数 , 第一个参数下标是0-->
<constructor-arg index="0" ref="orderDaoBean"/>
<!--指定构造方法的第二个参数 , 第二个参数下标是1-->
<constructor-arg index="1" ref="userDaoBean"/>
</bean>
<!--这里使用了构造方法上参数的名字-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg name="orderDao" ref="orderDaoBean"/>
<constructor-arg name="userDao" ref="userDaoBean"/>
</bean>
<!--这里没有指定下标,也没有指定参数名字,默认按照 constructor-arg标签从上到下的顺序给构造方法中对应的形参赋值-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="orderDaoBean"/>
<constructor-arg ref="userDaoBean"/>
</bean>
<!--这里标签的顺序已经和构造方法的参数顺序不同了 ,Spring也可以根据实参类型自动推断给哪个形参赋值-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="userDaoBean"/>
<constructor-arg ref="orderDaoBean"/>
</bean>
构造器参数相同: Spring不确定执行哪个构造方法赋值 ,可以利用 type 属性指定参数的类型
<!--这里没有指定下标,也没有指定参数名字-->
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="userDaoBean" type="com.powernode.spring6.dao.UserDao"/>
<constructor-arg ref="orderDaoBean" type="com.powernode.spring6.dao.OrderDao"/>
</bean>
级联属性赋值(了解)
概述
级联属性赋值就是给某个对象的属性(该对象关联的对象)的属性赋值
Clazz 班级类
public class Clazz {
private String name;
public Clazz() {
}
public Clazz(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Clazz{" +
"name='" + name + '\'' +
'}';
}
}
Student 有 clazz 属性 , 表示学生属于哪个班级
public class Student {
private String name;
private Clazz clazz;
public Student() {
}
public Student(String name, Clazz clazz) {
this.name = name;
this.clazz = clazz;
}
public void setName(String name) {
this.name = name;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
// clazz属性必须提供getter方法。
public Clazz getClazz() {
return clazz;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", clazz=" + clazz +
'}';
}
}
使用级联属性赋值需要注意两点
- 配置的顺序不能颠倒,因为只有先拿到关联的对象 , 才能给该对象的属性赋值
- clazz 属性必须提供 getter 方法。才能拿到关联对象
<?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="clazzBean" class="com.powernode.spring6.beans.Clazz"/>
<!--使用级联属性通过给学生对象关联的班级对象的属性赋值-->
<bean id="student" class="com.powernode.spring6.beans.Student">
<!--简单类型,使用value-->
<property name="name" value="张三"/>
<!--这不是简单类型,使用ref-->
<property name="clazz" ref="clazzBean"/>
<!--级联属性赋值-->
<property name="clazz.name" value="高三一班"/>
</bean>
<!--
采用一般方式直接给班级对象的属性赋值
<bean id="clazzBean" class="com.powernode.spring6.bean.Clazz">
<property name="name" value="高三一班"/>
</bean>
-->
</beans>
@Test
public void testCascade(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-cascade.xml");
Student student = applicationContext.getBean("student", Student.class);
System.out.println(student);
}
数组和集合类型注入
注入数组
当数组中的元素是简单类型
- 在 array 标签里使用 value标签 , 在value标签内指定数组元素的值
public class Person {
private String[] favariteFoods;
public void setFavariteFoods(String[] favariteFoods) {
this.favariteFoods = favariteFoods;
}
@Override
public String toString() {
return "Person{" +
"favariteFoods=" + Arrays.toString(favariteFoods) +
'}';
}
}
<?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="person" class="com.powernode.spring6.beans.Person">
<property name="favariteFoods">
<array>
<value>鸡排</value>
<value>汉堡</value>
<value>鹅肝</value>
</array>
</property>
</bean>
</beans>
当数组中的元素是非简单类型:一个订单中包含多个商品
- 在 array 标签里使用 ref 标签 , 使用 ref 标签的 bean 属性指定引用的 bean 的 id , 给数组中的元素赋值
public class Goods {
private String name;
public Goods() {
}
public Goods(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
'}';
}
}
public class Order {
// 一个订单中有多个商品
private Goods[] goods;
public Order() {
}
public Order(Goods[] goods) {
this.goods = goods;
}
public void setGoods(Goods[] goods) {
this.goods = goods;
}
@Override
public String toString() {
return "Order{" +
"goods=" + Arrays.toString(goods) +
'}';
}
}
<?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="goods1" class="com.powernode.spring6.beans.Goods">
<property name="name" value="西瓜"/>
</bean>
<bean id="goods2" class="com.powernode.spring6.beans.Goods">
<property name="name" value="苹果"/>
</bean>
<bean id="order" class="com.powernode.spring6.beans.Order">
<property name="goods">
<array>
<!--这里使用ref标签即可-->
<ref bean="goods1"/>
<ref bean="goods2"/>
</array>
</property>
</bean>
</beans>
注入List集合(有序可重复)
注入 List 集合的时候使用 list 标签
- 如果 List 集合中是简单类型就在 list 标签内使用 value 标签,在 value 标签内指定集合中元素的值
- 如果 List 集合中是非简单类型就在 list 标签内使用 ref 标签。使用 ref 标签的 bean 属性指定引用外部 bean 的 id
- 也可以利用 bean标签 自己创建一个内部 bean
public class People {
// 一个人有多个名字
private List<String> names;
public void setNames(List<String> names) {
this.names = names;
}
@Override
public String toString() {
return "People{" +
"names=" + names +
'}';
}
}
List集合的特点是有序可重复
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="names">
<!--List集合的特点是有序可重复-->
<list>
<value>铁锤</value>
<value>张三</value>
<value>张三</value>
<value>张三</value>
<value>狼</value>
<!-- 自己创建并添加元素 -->
<bean class=""></bean>
<!-- 引用外部元素并添加 -->
<ref bean="" />
</list>
</property>
</bean>
</beans>
测试程序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gB5HlDqG-1678292610923)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667925944025.png)]
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注入Set集合(无序不可重复)
注入 Set 集合的时候使用 set 标签
- 如果 Set 集合中是简单类型就在 set 标签内使用 value 标签,在 value 标签内指定集合元素的值
- 如果 Set 集合中是非简单类型就在 set 标签内使用 ref 标签。使用 ref 标签的 bean 属性指定引用外部 bean 的 id
- 也可以利用 bean标签 自己创建一个内部 bean
public class People {
// 一个人有多个电话
private Set<String> phones;
public void setPhones(Set<String> phones) {
this.phones = phones;
}
//......
@Override
public String toString() {
return "People{" +
"phones=" + phones +
", names=" + names +
'}';
}
}
Set集合的特点是无序不重复
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="phones">
<set>
<!--非简单类型可以使用ref,简单类型使用value-->
<value>110</value>
<value>110</value>
<value>120</value>
<value>120</value>
<value>119</value>
<value>119</value>
<!-- 自己创建并添加元素 -->
<bean class=""></bean>
<!-- 引用外部元素并添加 -->
<ref bean="" />
</set>
</property>
</bean>
</beans>
测试程序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3adMdgNy-1678292610925)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667926147465.png)]
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注入Map集合
注入 Map 集合的时候使用 map 标签 , 并且在 entry 标签内使用 key 和 value 属性
- 如果 Map 集合中 key 是简单类型,使用 key 属性,反之使用 key-ref 属性引用外部的bean。也可以在内部自己创建bean
- 如果 Map 集合中 value 是简单类型,使用 value 属性,反之使用 value-ref 属性。也可以在内部自己创建bean
public class People {
// 一个人有多个住址
private Map<Integer, String> addrs;
public void setAddrs(Map<Integer, String> addrs) {
this.addrs = addrs;
}
//......
@Override
public String toString() {
return "People{" +
"addrs=" + addrs +
", phones=" + phones +
", names=" + names +
'}';
}
}
Map集合的特点以键值对的形式存储数据
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="addrs">
<map>
<!--key 和 value 都是简单类型-->
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
<!-- key是简单类型 , value不是简单类型 , 引用的外部bean -->
<entry key="" value-ref=""></entry>
<!--key是简单类型 , value不是简单类型 , 自己创建的内部bean -->
<entry key="key04">
<bean class="">
<property name="" value=""></property>
</bean>
</entry>
</map>
</property>
</bean>
</beans>
测试程序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H4niNUwS-1678292610926)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667926854625.png)]
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注入Properties
java.util.Properties继承java.util.Hashtable,所以Properties也是一个Map集合。
-
Properties集合中存储元素的类型的 key 和 value 的类型都是 String 类型(简单类型)
-
使用< props >标签嵌套< prop >标签完成。prop 标签的 key 属性指定 key的值 , prop标签内指定value的值
public class People { private Properties properties; public void setProperties(Properties properties) { this.properties = properties; } //...... @Override public String toString() { return "People{" + "properties=" + properties + ", addrs=" + addrs + ", phones=" + phones + ", names=" + names + '}'; } }
<?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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="properties">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
测试程序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfhJaghr-1678292610926)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667927856072.png)]
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注入null和空字符串和特殊字符
注入空字符串
注入空字符串使用:< value/ >标签 或者 指定属性value=“”
public class Vip {
private String email;
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Vip{" +
"email='" + email + '\'' +
'}';
}
}
<?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="vipBean" class="com.powernode.spring6.beans.Vip">
<!--空串的第一种方式-->
<property name="email" value=""/>
<!--空串的第二种方式-->
<property name="email">
<value/>
</property>
</bean>
</beans>
注入 null
注入 null 使用 < null/ >标签 或者 不为该属性赋值
<?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="vipBean" class="com.powernode.spring6.beans.Vip" />
<!--第二种方式:使用<null/>标签-->
<bean id="vipBean" class="com.powernode.spring6.beans.Vip">
<property name="email">
<null/>
</property>
</bean>
</beans>
注入的值中含有特殊符号
XML中有5个特殊字符,分别是:<、>、'、"、& , 它们在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。
第一种:特殊符号使用转义字符代替
//<(>)、>(<)、'(')、"(")、&(&)
public class Math {
private String result;
public void setResult(String result) {
this.result = result;
}
@Override
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
}
<?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="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result" value="2 < 3"/>
</bean>
</beans>
第二种:将含有特殊符号的字符串放到:<![CDATA[]]> 当中。因为放在CDATA区中的数据不会被XML文件解析器解析, 当作普通字符串处理。
<?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="mathBean" class="com.powernode.spring6.beans.Math">
<property name="result">
<!--使用CDATA时,不能使用value属性,只能使用value标签。-->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
</beans>
测试程序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p90hVy7v-1678292610927)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667929290645.png)]
@Test
public void testSpecial(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-special.xml");
Math mathBean = applicationContext.getBean("mathBean", Math.class);
System.out.println(mathBean);
}
命名空间注入(简化配置)
p命名空间注入
使用p命名空间注入的前提条件包括两个
- 第一:在XML头部信息中添加p命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p"(先拷贝头部命名空间再修改两处即可)
- 第二:p命名空间注入是基于setter方法的,所以需要对应的属性提供setter方法。p命名空间实际上是对set注入的简化。
注意: 简单类型使用 p: 属性名 = “值” 的形式 , 非简单类型使用 p:属性名-ref =“引用的外部 bean 的 id”
public class Dog {
// 简单类型
private String name;
private int age;
// 非简单类型
private Date birth;
// p命名空间注入底层还是set注入,只不过p命名空间注入可以让spring配置变的更加简单。
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--这里把 Date 当作非简单类型-->
<bean id="dogBean" class="com.powernode.spring6.bean.Dog" p:name="小花" p:age="3" p:birth-ref="birthBean"/>
<!--这里获取的是当前系统时间。-->
<bean id="birthBean" class="java.util.Date"/>
</beans>
把setter方法去掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y13B1FCP-1678292610928)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667929818540.png)]
c 命名空间注入
使用c命名空间的两个前提条件:
- 第一:需要在xml配置文件头部添加信息:xmlns:c=“http://www.springframework.org/schema/c”(拷贝头部命名空间修改两处即可)
- 第二:需要提供构造方法。c命名空间就是简化构造方法注入的。
注意: 简单类型使用 c: 形参名/ _形参下标 = “值” 的形式 , 非简单类型使用 c:形参名/ _ 形参下标-ref =“引用的外部 bean 的 id”
public class MyTime {
private int year;
private int month;
private int day;
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
@Override
public String toString() {
return "MyTime{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
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">
<!--使用形参 name 的方式-->
<bean id="myTimeBean" class="com.powernode.spring6.beans.MyTime" c:year="1970" c:month="1" c:day="1"/>
<!--使用形参下标的方式-->
<bean id="myTimeBean" class="com.powernode.spring6.beans.MyTime" c:_0="2008" c:_1="8" c:_2="8"/>
</beans>
把构造方法注释掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Txq0EGIE-1678292610929)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667930036276.png)]
util命名空间(配置重复使用)
使用util命名空间可以让创建集合类型的bean , 使配置复用。使用 property 标签的 ref 属性引用外部集合类型 bean的 id
- 使用 util 名称空间创建的集合类型的 bean 也可以通过 id 从容器中直接拿到
- util:map 创建了一个Map集合 , util:list 相当于创建了一个List集合 , util:set 相当于创建了一个 Set 集合, 创建完集合后往集合中放元素就行了
使用util命名空间的前提是
- 在spring配置文件头部添加配置信息。添加命名空间(增加两个util)和添加约束(beans全部替换为util)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PUw2khw4-1678292610930)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667955305700.png)]
配置两个数据源 , 它们连接数据库的参数信息相同
public class MyDataSource1 {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource1{" +
"properties=" + properties +
'}';
}
}
public class MyDataSource2 {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource2{" +
"properties=" + properties +
'}';
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!--使用util命名空间 , 配置可以重复使用 , 引用 id 即可-->
<util:properties id="prop">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</util:properties>
<!--数据源1-->
<bean id="dataSource1" class="com.powernode.spring6.beans.MyDataSource1">
<property name="properties" ref="prop"/>
</bean>
<!--数据源2-->
<bean id="dataSource2" class="com.powernode.spring6.beans.MyDataSource2">
<property name="properties" ref="prop"/>
</bean>
<!--不使用util命名空间-->
<bean id="dataSource1" class="com.powernode.spring6.beans.MyDataSource1">
<property name="properties"/>
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
自动注入
Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配
根据名称自动装配
如果根据名称装配(byName),底层会调用 set 方法进行注入。需要提供属性的 set 方法
- 根据对象的属性名去找对应的 set 方法完成注入,找到组件后给这个对象的对应属性赋值;如果找不到就装配null
- 以对象的每个属性名作为id去容器中找到这个组件(bean),找到组件后给这个对象的对应属性赋值;如果找不到就装配null
public class UserDao {
public void insert(){
System.out.println("正在保存用户数据。");
}
}
public class UserService {
private UserDao aaa;
// 这个set方法非常关键
public void setAaa(UserDao aaa) {
this.aaa = aaa;
}
public void save(){
aaa.insert();
}
}
UserService类中有一个UserDao属性,而UserDao属性的名字是aaa,对应的set方法是setAaa(),正好和UserDao Bean的id是一样的。这就是根据名称自动装配。
<?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">
<!--UserService Bean中需要添加autowire="byName",表示通过名称进行装配-->
<bean id="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<!--以bean的去找对应的set方法完成-->
<bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>
</beans>
public class UserService {
// 这里没修改
private UserDao aaa;
/*public void setAaa(UserDao aaa) {
this.aaa = aaa;
}*/
// set方法名变化了
public void setDao(UserDao aaa){
this.aaa = aaa;
}
public void save(){
aaa.insert();
}
}
<?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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<!--这个id修改了-->
<!--根据名字进行自动装配的时候,被注入的对象的bean的id不能随便写,怎么写?set方法的方法名去掉set,剩下单词首字母小写。-->
<bean id="dao" class="com.powernode.spring6.dao.UserDao"/>
</beans>
根据类型自动装配
根据类型注入在装配的时候也是基于set方法的。所以set方法是必须要提供的。提供构造方法是不行的
以对象的每个自定义属性的类型作为查找依据去容器中找到这个组件bean , 找不到就装配null
- 相当于自己根据属性的类型从容器中拿 car = ioc.getBean(Car.class)
- 如果是集合类型的属性找到多个组件不会报错 . Spring会把容器中的所有该类型的对象封装到集合中然后为这个属性赋值
public class AccountDao {
public void insert(){
System.out.println("正在保存账户信息");
}
}
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void save(){
accountDao.insert();
}
}
<?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">
<!--byType表示根据类型自动装配-->
<bean id="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>
<bean class="com.powernode.spring6.dao.AccountDao"/>
</beans>
我们把UserService中的set方法注释掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QwXplYnI-1678292610930)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667958797604.png)]
当byType进行自动装配的时候,配置文件中某种类型的Bean必须是唯一的,不能出现多个。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RFgrp3Z4-1678292610931)(C:\Users\meng\AppData\Roaming\Typora\typora-user-images\1667958881120.png)]
<?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="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>
<!--根据类型进行自动装配的时候,在有效的配置文件当中,某种类型的实例只能有一个。-->
<bean id="x" class="com.powernode.spring6.dao.AccountDao"/>
<bean id="y" class="com.powernode.spring6.dao.AccountDao"/>
</beans>
SpEL(Spring Expression Language)
Spring表达式语言
${} 和 #{} , 在#{}的括号中可以使用SpEL表达式
- 含运算符的字面量, 支持使用任何运算符
- 其他要引用的bean的id
- 其他要引用的bean的某个属性值
- 调用非静态方法 : #{T(全类名).静态方法名(实参)}
- 调用静态方法: #{ 对象bean.非静态方法名(实参) }
<bean id="book01" class="com.atguigu.bean.Book">
<property name="bookName" value="book1"></property>
</bean>
<bean id="person" class="com.atguigu.bean.Person">
<!-- 字面量:使用运算符 -->
<property name="salary" value="#{12345.67*12}"></property>
<!-- 引用其他bean的某个属性值-->
<property name="lastName" value="#{book01.bookName}"></property>
<!-- 引用其他bean , 也可以使用ref属性应用-->
<property name="car" value="#{car}"></property>
<!-- 调用静态方法: UUID.randomUUID().toString();-->
<property name="email" value="#{T(java.util.UUID).randomUUID().toString().substring(0,5)}"></property>
<!--调用非静态方法; 对象.方法名 -->
<property name="gender" value="#{book01.getBookName()}"></property>
</bean>
对象的自定义类型的属性赋值
public class Person {
//...
//引用类型
private Car car;
private List<Book> books;
private Map<String, Object> maps;
private Properties properties;
}
传统方式利用property标签的ref属性手动赋值
需求: 完成对Person对象的 Car类型的car属性赋值
<bean id="car" class="com.atguigu.bean.Car">
<property name="carName" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
<bean id="person" class="com.atguigu.bean.Person">
<property name="car" ref="car"></property>
</bean>
基于XML为对象的属性自动赋值仅限于对自定义类型的属性有效
需求: 完成对Person对象的 Car类型的car属性赋值
属性名/属性值 | 功能 |
---|---|
autowire=“default/no” | 不自动为对象的自定义属性赋值 |
autowire=“byName” | 按照自定义属性的名字去容器中查找对应的组件为对象的自定义属性赋值 |
autowire=“byType” | 按照自定义属性的类型去容器中查找对应的组件为对象的自定义属性赋值 |
autowire=“constructor” | 执行构造方法为对象的自定义属性赋值 |
autowire=“byName”: 以对象的每个属性名作为id去容器中找到这个组件(bean),找到组件后给这个对象的对应属性赋值;如果找不到就装配null
- 相当于自己根据id从容器中拿 car = ioc.getBean(“car”)
<bean id="car" class="com.atguigu.bean.Car">
<property name="carName" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
<!--相当于自己根据id从容器中拿 car = ioc.getBean("car")-->
<bean id="person" class="com.atguigu.bean.Person" autowire="byName">
</bean>
autowire=“byType”: 以对象的每个自定义属性的类型作为查找依据去容器中找到这个组件bean , 找不到就装配null
- 相当于自己根据属性的类型从容器中拿 car = ioc.getBean(Car.class)
- 如果容器中有多个这个自定义属性类型的组件, 报错;NoUniqueBeanDefinitionException: No qualifying bean of type [com.atguigu.bean.Car] is defined:
expected single matching bean but found 2: car01,car02 - 如果是集合类型的属性找到多个组件不会报错 . Spring会把容器中的所有该类型的对象封装到集合中然后为这个属性赋值
<bean id="car01" class="com.atguigu.bean.Car">
<property name="carName" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
<bean id="car02" class="com.atguigu.bean.Car">
<property name="carName" value="奔驰"></property>
<property name="color" value="白色"></property>
</bean>
<!--相当于自己根据属性的类型从容器中拿 car = ioc.getBean(Car.class)-->
<bean id="person" class="com.atguigu.bean.Person" autowire="byType">
</bean>
为集合类型的属性赋值(List、Map、Properties)
<bean id="book01" class="com.atguigu.bean.Book">
<property name="bookName" value="book1"></property>
</bean>
<bean id="book02" class="com.atguigu.bean.Book">
<property name="bookName" value="book2"></property>
</bean>
<bean id="book03" class="com.atguigu.bean.Book">
<property name="bookName" value="book3"></property>
</bean>
<bean id="person" class="com.atguigu.bean.Person" autowire="byType">
</bean>
autowire=“constructor”: 执行构造方法对属性赋值
- 先按照有参构造器形参的类型作为查找依据去容器中找到这个组件bean , 然后调用这个构造方法进行属性的赋值 , 找不到就装配null , 并且不会调用构造方法
- 如果按照形参的类型在容器中找到了多个组件 , 会以构造器形参的变量名作为id继续匹配这些组件进行属性的赋值 ,并且不会调用构造方法
public Person(Car car) {
super();
this.car = car;
System.out.println("可以为car赋值的有参构造器....");
}
<!--如果按照类型在容器中找到了多个 , 会以构造器参数的名作为id继续匹配-->
<bean id="car" class="com.atguigu.bean.Car">
<property name="carName" value="宝马"></property>
<property name="color" value="白色"></property>
</bean>
<bean id="car02" class="com.atguigu.bean.Car">
<property name="carName" value="奔驰"></property>
<property name="color" value="白色"></property>
</bean>
<bean id="person" class="com.atguigu.bean.Person" autowire="constructor" >
</bean>