目录
一:Spring对IoC的实现
1.IoC 控制反转
2.依赖注入
2.1set注入
2.2构造注入
3.set注入专题
3.1 注入外部Bean
3.2 注入内部Bean
3.3 注入简单类型
3.4 级联属性赋值(了解)
3.5 注入数组
3.6 注入List集合和Set集合
3.7注入Map和Properties集合
3.8 注入null和空字符串
3.9 注入的值中含有特殊符号
4.p命名空间注入
5.c命名空间注入
6.util命名空间
7. 基于XML的自动装配(byName & byType)
7.1 根据名称(byName)自动装配
7.2根据类型(byType)自动装配
8. Spring引入外部属性配置文件(使用context命名空间)
一:Spring对IoC的实现
前面我们已经学会了如何用spring创建管理对象,接下来就要学习如何让对象和对象产生关系,使用依赖注入!
1.IoC 控制反转
(1)控制反转是一种思想,一种新型的设计模式!
(2)控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
(3)控制反转,反转的是什么?
①将对象的创建权利交出去,交给第三方容器负责。
②将对象和对象之间关系的维护权交出去,交给第三方容器负责。
(4)控制反转这种思想如何实现呢?
DI(Dependency Injection):依赖注入
2.依赖注入
依赖注入实现了控制反转的思想!
Spring通过依赖注入的方式来完成Bean管理的。Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
依赖注入常见的实现方式包括两种:
- 第一种:set注入
- 第二种:构造注入
2.1set注入
set注入,基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值;这种方式要求属性必须对外提供set方法!
pom.xml配置
4.0.0 com.bjpowernode spring6-002-dependency-injection 1.0-SNAPSHOT jar repository.spring.milestone Spring Milestone Repository https://repo.spring.io/milestone org.springframework spring-context 6.0.0-M2 junit junit 4.12 org.apache.logging.log4j log4j-core 2.19.0 org.apache.logging.log4j log4j-slf4j2-impl 2.19.0 17 17
log4j2.xml日志配置
UserDao类:连接数据库的操作
package com.bjpowernode.spring6.dao;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class UserDao { // 一般声明为常量 private static final Logger logger = LoggerFactory.getLogger(UserDao.class); // 使用日志进行打印,用System.out.println也可以 public void insert(){ logger.info("数据库正在保存用户信息!"); }}
UserService类:调用UserDao中的方法
①set注入的话,必须提供一个set方法;Spring容器会调用这个set方法,来给userDao属性赋值。
②这个set方法不符合javabean规范也可以,但是必须以set开头,例如:setUD也是可以的;这里我使用的是IDEA自动生成的符合javabean规范的。
package com.bjpowernode.spring6.service;import com.bjpowernode.spring6.dao.UserDao;public class UserService { private UserDao userDao; // set注入,必须提供一个set方法 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void saveUser(){ // 调用UserDao保存用户信息 userDao.insert(); }}
spring.xml配置
①配置userDaoBean和UserService,让spring管理这两个类。
②对于UserService,想让Spring调用对应的set方法,需要配置property标签:
name属性值:set方法的方法名,去掉set,然后把剩下的单词首字母变小写
ref属性值:翻译为引用,英语单词references,后面指定的是要注入的bean的id
③另外,对于property标签来说,ref属性也可以采用标签的方式,但使用ref属性是多数的:
编写测试类
package com.bjpowernode.spring6.test;import com.bjpowernode.spring6.service.UserService;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class DITest { @Test public void testSetDI(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml"); UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class); userServiceBean.saveUser(); }}
执行结果:
正常输出日志信息了,说明两个问题:
①spring正常创建UserDao和UserService对象了!
②spring关联了对象与对象之间的关系了!
总结:set注入的核心实现原理是通过反射机制调用set方法来给属性赋值,让两个对象之间产生关系。
2.2构造注入
核心原理:通过调用构造方法来给属性赋值。
①set注入:是先创建对象,才能执行set方法,给属性赋值。
②构造注入:是在创建对象的同时,给属性赋值,时机是不同的。
在定义一个VipDao类
package com.bjpowernode.spring6.dao;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class VipDao { private static final Logger logger = LoggerFactory.getLogger(VipDao.class); public void delete(){ logger.info("正在删除信息!"); }}
ConstructService类
构造方法注入,必须要提供一个构造方法!
package com.bjpowernode.spring6.service;import com.bjpowernode.spring6.dao.UserDao;import com.bjpowernode.spring6.dao.VipDao;public class ConstructService { private UserDao userDao; private VipDao vipDao; // 构造注入,必须有构造方法 public ConstructService(UserDao userDao, VipDao vipDao) { this.userDao = userDao; this.vipDao = vipDao; } public void save(){ userDao.insert(); vipDao.delete(); }}
bean.xml配置
访问的方式有三种:使用的是constructor-arg标签
①第一种方式是根据下标index的方式,下标的顺序是构造方法参数的顺序。
②第二种方式是根据构造方法参数的名字name的方式。
③第三种方式是根据类型进行注入,不指定,spring会自己推断做类型匹配。
编写测试
package com.bjpowernode.spring6.test;import com.bjpowernode.spring6.service.ConstructService;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class ConstructTest { @Test public void testConstructDI(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); ConstructService constructServiceBean = applicationContext.getBean("constructServiceBean", ConstructService.class); constructServiceBean.save(); }}
执行结果:
3.set注入专题
set注入和构造注入中,set注入用的比较多,所以下面就学习一下set注入的专题!
3.1 注入外部Bean
(1)在之前我们使用的案例一直就是注入外部Bean的方式!
(2)外部Bean的特点:bean定义到外面,在property标签中使用ref属性进行注入;通常这种方式是常用!
3.2 注入内部Bean
内部Bean的方式:在bean标签中直接嵌套bean标签,不需要ref属性引入。
3.3 注入简单类型
(1)之前在进行注入的时候,对象的属性都是另一个对象;那如果对象的属性是int类型呢?也可以通过set注入的方式给该属性赋值,实际上只要能够调用set方法就可以给属性赋值。
(2)重点:如果给简单类型赋值,就不能使用ref属性,需要使用value属性!
User类:定义了两个简单类型,写上set方法
package com.bjpowernode.spring6.bean;public class User { private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; }}
set-di.xm配置
注:既可以使用value标签的方式,也可以使用value属性的方式(常用)
编写测试
package com.bjpowernode.spring6.test;import com.bjpowernode.spring6.bean.User;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SetDITest { @Test public void testSimpleTypeSet(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml"); User user = applicationContext.getBean("userBean", User.class); System.out.println(user); }}
执行结果:默认会调用toString方法
需要特别注意:如果给简单类型赋值,使用value属性或value标签,而不是ref!
(3)那么简单类型包括哪些呢?可以通过Spring的源码来分析一下!
双击shift搜索BeanUtils类,ctrl+F12搜索isSimpleValueType方法,里面都是简单类型:
(4)这里重点说一下Date类型,如果硬要把Date类型当做简单类型,使用value赋值的话,这个日期的格式有要求:Thu Jan 12 21:05:49 CST 2023 ,所以在实际的开发中,我们一般采用ref属性的方式给Date类型的属性赋值!
(5)简单类型注入的经典案例:给数据源的属性注入值:
假设我们现在要自己手写一个数据源(能够提供Connection对象的),我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password等。
数据源MyDataSource
package com.bjpowernode.spring6.jdbc;import javax.sql.DataSource;import java.io.PrintWriter;import java.sql.Connection;import java.sql.SQLException;import java.sql.SQLFeatureNotSupportedException;import java.util.logging.Logger;public class MyDateSource implements DataSource { private String driver; private String url; private String username; private String password; public void setDriver(String driver) { this.driver = driver; } public void setUrl(String url) { this.url = url; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "MyDateSource{" + "driver='" + driver + '\'' + ", url='" + url + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } @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 unwrap(Class iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class
编写测试
package com.bjpowernode.spring6.test;import com.bjpowernode.spring6.bean.User;import com.bjpowernode.spring6.jdbc.MyDateSource;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SetDITest { @Test public void testMyDataSource(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml"); MyDateSource myDataSource = applicationContext.getBean("myDataSource", MyDateSource.class); System.out.println(myDataSource); }}
测试结果:成功注入连接数据库的信息
3.4 级联属性赋值(了解)
我们先回顾一下原来使用的注入方式,然后在使用级联属性赋值,进行对比!
clazz班级类
package com.bjpowernode.spring6.bean;public class Clazz { // 班级名称 private String name; public void setName(String name) { this.name = name; } @Override public String toString() { return "Clazz{" + "name='" + name + '\'' + '}'; }}
Student学生类
package com.bjpowernode.spring6.bean;public class Student { // 学生姓名 private String name; // 班级 private Clazz clazz; public void setName(String name) { this.name = name; } public void setClazz(Clazz clazz) { this.clazz = clazz; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", clazz=" + clazz + '}'; }}
第一种:原来的注入方式
第二种方式:级联注入方式
使用级联属性赋值需要注意两点:
①配置的顺序不能颠倒,先配置student在配置clazz
②clazz属性必须提供getClazz()方法(所以要在Student类当中要增加getter方法)
执行结果:
3.5 注入数组
这里主要学习两种情况:数组中的元素是简单类型和当数组中的元素是非简单类型!
Woman类,作为非简单类型
package com.bjpowernode.spring6.bean;public class Woman { private String name; @Override public String toString() { return "Woman{" + "name='" + name + '\'' + '}'; } public void setName(String name) { this.name = name; }}
QY类,里面包含简单类型和非简单类型的数组属性
package com.bjpowernode.spring6.bean;import java.util.Arrays;public class QY { // 简单类型的数组 private String[] loves; // 非简单类型的数组 private Woman[] women; @Override public String toString() { return "QY{" + "loves=" + Arrays.toString(loves) + ", women=" + Arrays.toString(women) + '}'; } public void setLoves(String[] loves) { this.loves = loves; } public void setWomen(Woman[] women) { this.women = women; }}
spring-array.xml配置
当属性是数组时,需要先使用一下array标签,在array标签中再写value和ref标签进行赋值!
抽烟 喝酒 烫头
编写测试
package com.bjpowernode.spring6.test;import com.bjpowernode.spring6.bean.Clazz;import com.bjpowernode.spring6.bean.QY;import com.bjpowernode.spring6.bean.Student;import com.bjpowernode.spring6.bean.User;import com.bjpowernode.spring6.jdbc.MyDateSource;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SetDITest { @Test public void testArraySet(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array.xml"); QY yqBean = applicationContext.getBean("yqBean", QY.class); System.out.println(yqBean); }}
执行结果:
要点:
如果数组中是简单类型,使用value标签。
如果数组中是非简单类型,使用ref标签。
3.6 注入List集合和Set集合
Person类
package com.bjpowernode.spring6.bean;import java.util.List;import java.util.Set;public class Person { // 注入List private List names; // 注入Set集合 private Set addrs; @Override public String toString() { return "Person{" + "names=" + names + ", addrs=" + addrs + '}'; } public void setNames(List names) { this.names = names; } public void setAddrs(Set addrs) { this.addrs = addrs; }}
spring-collection.xml配置
如果是List集合或者Set集合的属性,需要先使用标签和标签,标签中再写value和ref标签进行赋值!
张三 李四 张三 王五 张三 李四 张三 王五
编写测试
package com.bjpowernode.spring6.test;import com.bjpowernode.spring6.bean.*;import com.bjpowernode.spring6.jdbc.MyDateSource;import org.junit.Test;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SetDITest { @Test public void testCollectionSet(){ ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml"); Person personBean = applicationContext.getBean("personBean", Person.class); System.out.println(personBean); }}
执行结果:
从执行结果上看,可以得出:List集合是有序可重复、Set集合是无序不可重复!
注意:注入List集合的时候使用list标签,注入Set集合的时候使用set标签,如果集合中是简单类型使用value标签,反之使用ref标签。
3.7注入Map和Properties集合
Properties集合本质上也是一个Map集合,但是Properties集合的key和value只能是String类型,并且注入的方式也是与Map集合不同的!
Man类
package com.bjpowernode.spring6.bean;import java.util.Map;import java.util.Properties;public class Man { // 注入Map集合 private Map phones; // 注入Properties private Properties properties; @Override public String toString() { return "Man{" + "phones=" + phones + ", properties=" + properties + '}'; } public void setProperties(Properties properties) { this.properties = properties; } public void setPhones(Map phones) { this.phones = phones; }}
spring-collection.xml配置
如果是Map集合的属性,使用map标签嵌套entry子标签(不使用ref和value标签了)
如果是Map集合的属性,使用props标签嵌套pro标签
com.mysql.jdbc.driver jdbc:mysql://localhost:3306/spring6
执行结果:
要点:
对于Map集合使用