一:Spring对IoC的实现
前面我们已经学会了如何用spring创建管理对象,接下来就要学习如何让对象和对象产生关系,使用依赖注入!
1. IoC 控制反转
(1)控制反转是一种思想,一种新型的设计模式!
(2)控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
(3)控制反转,反转的是什么?
①将对象的创建权利交出去,交给第三方容器负责。
②将对象和对象之间关系的维护权交出去,交给第三方容器负责。
(4)控制反转这种思想如何实现呢?
DI(Dependency Injection):依赖注入
2. 依赖注入
依赖注入实现了控制反转的思想!
Spring通过依赖注入的方式来完成Bean管理的。****Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
依赖注入常见的实现方式包括两种:
- 第一种:set注入
- 第二种:构造注入
2.1 set注入
set注入,基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值;这种方式要求属性必须对外提供set方法!
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bjpowernode</groupId>
<artifactId>spring6-002-dependency-injection</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<!--配置多个仓库-->
<repositories>
<!--spring6里程碑的仓库-->
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
<!--spring context依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--单元测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--log4j2的依赖-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.19.0</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>
log4j2.xml日志配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<loggers>
<root level="DEBUG">
<appender-ref ref="spring6log"/>
</root>
</loggers>
<appenders>
<console name="spring6log" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
</console>
</appenders>
</configuration>
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
<?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 https://www.springframework.org/schema/util/spring-util.xsd">
<!--配置dao-->
<bean id="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<!--配置service-->
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean" />
</bean>
</beans>
③另外,对于property标签来说,ref属性也可以采用标签的方式,但使用ref属性是多数的:
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<ref bean="userDaoBean"/>
</property>
</bean>
编写测试类
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会自己推断做类型匹配。
<?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="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="vipDaoBean" class="com.bjpowernode.spring6.dao.VipDao"/>
<bean id="constructServiceBean" class="com.bjpowernode.spring6.service.ConstructService">
<!--第一种方式-->
<constructor-arg index="0" ref="userDaoBean"/>
<constructor-arg index="1" ref="vipDaoBean" />
<!--第二种方式-->
<constructor-arg name="userDao" ref="userDaoBean"/>
<constructor-arg name="vipDao" ref="vipDaoBean"/>
<!--第三种方式-->
<constructor-arg ref="userDaoBean"/>
<constructor-arg ref="vipDaoBean" />
</bean>
</beans>
编写测试
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属性进行注入;通常这种方式是常用!
<?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 https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean" />
</bean>
</beans>
3.2 注入内部Bean
内部Bean的方式:在bean标签中直接嵌套bean标签,不需要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" 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 https://www.springframework.org/schema/util/spring-util.xsd">
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao">
<bean class="com.bjpowernode.spring6.dao.UserDao"/>
</property>
</bean>
</beans>
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属性的方式(常用)
<?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="userBean" class="com.bjpowernode.spring6.bean.User">
<property name="age" value="18"/>
<property name="name" value="张三"/>
</bean>
</beans>
编写测试
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> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
spring-datasource.xml:使用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 name="myDataSource" class="com.bjpowernode.spring6.jdbc.MyDateSource">
<property name="driver" value="com.mysql.jdbc.driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
</beans>
编写测试
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 +
'}';
}
}
第一种:原来的注入方式
<?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 name="clazzBean" class="com.bjpowernode.spring6.bean.Clazz">
<property name="name" value="高三一班"/>
</bean>
<bean name="studentBean" class="com.bjpowernode.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
</bean>
</beans>
第二种方式:级联注入方式
使用级联属性赋值需要注意两点:
①配置的顺序不能颠倒,先配置student在配置clazz
②clazz属性必须提供getClazz()方法(所以要在Student类当中要增加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 name="studentBean" class="com.bjpowernode.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
<!--级联属性赋值-->
<property name="clazz.name" value="高三一班"/>
</bean>
<bean name="clazzBean" class="com.bjpowernode.spring6.bean.Clazz" />
</beans>
执行结果:
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标签进行赋值!
<?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="w1" class="com.bjpowernode.spring6.bean.Woman">
<property name="name" value="小花"/>
</bean>
<bean id="w2" class="com.bjpowernode.spring6.bean.Woman">
<property name="name" value="小红"/>
</bean>
<!--简单类型-->
<bean id="yqBean" class="com.bjpowernode.spring6.bean.QY">
<!-- 注入简单类型-->
<property name="loves">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
<!--注入非简单类型-->
<property name="women" >
<array>
<ref bean="w1"/>
<ref bean="w2"/>
</array>
</property>
</bean>
</beans>
编写测试
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<String> names;
// 注入Set集合
private Set<String> addrs;
@Override
public String toString() {
return "Person{" +
"names=" + names +
", addrs=" + addrs +
'}';
}
public void setNames(List<String> names) {
this.names = names;
}
public void setAddrs(Set<String> addrs) {
this.addrs = addrs;
}
}
spring-collection.xml配置
如果是List集合或者Set集合的属性,需要先使用<list>标签和<set>标签,标签中再写value和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">
<bean id="personBean" class="com.bjpowernode.spring6.bean.Person">
<!--List集合-->
<property name="names">
<list>
<value>张三</value>
<value>李四</value>
<value>张三</value>
<value>王五</value>
</list>
</property>
<!--Set集合-->
<property name="addrs">
<set>
<value>张三</value>
<value>李四</value>
<value>张三</value>
<value>王五</value>
</set>
</property>
</bean>
</beans>
编写测试
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<String,Integer> 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<String, Integer> phones) {
this.phones = phones;
}
}
spring-collection.xml配置
如果是Map集合的属性,使用map标签嵌套entry子标签(不使用ref和value标签了)
如果是Map集合的属性,使用props标签嵌套pro标签
<?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="manBean" class="com.bjpowernode.spring6.bean.Man">
<!--Map集合-->
<property name="phones">
<map>
<entry key="张三" value="123"/>
<entry key="李四" value="456"/>
<entry key="王五" value="789"/>
</map>
</property>
<!-- Properties集合-->
<property name="properties">
<props>
<prop key="driver">com.mysql.jdbc.driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
</props>
</property>
</bean>
</beans>
执行结果:
要点:
对于Map集合使用<map>标签,对于Properties使用<props>标签嵌套<prop>标签完成。
如果key是简单类型,使用 key 属性,反之使用 key-ref 属性。
如果value是简单类型,使用 value 属性,反之使用 value-ref 属性。
3.8 注入null和空字符串
Cat类
package com.bjpowernode.spring6.bean;
public class Cat {
private String name;
private int age;
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
set-di.xml配置
①注入空字符串使用:<value/> 或者 value=""。
②注入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="carBean" class="com.bjpowernode.spring6.bean.Cat">
<!--注入null-->
<!--第一种方法:不为该属性赋值-->
<property name="name" value="张三"/>
<!--第二种方法:使用null标签,手动注入null-->
<property name="name">
<null/>
</property>
<property name="age" value="18"/>
<!-- 注入空字符串-->
<!--第一种方法-->
<property name="name" value=""/>
<!--第二种方法-->
<property name="name">
<value/>
</property>
<property name="age" value="20"/>
</bean>
</beans>
3.9 注入的值中含有特殊符号
(1)XML中有5个特殊字符,分别是:<、>、'、"、&
(2)以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。
Math类
package com.bjpowernode.spring6.bean;
public class Math {
private String result;
@Override
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
public void setResult(String result) {
this.result = result;
}
}
math.xml配置
解决方案包括两种:
第一种:特殊符号使用转义字符代替。
第二种:将含有特殊符号的字符串放到:<![CDATA[]]> 当中。因为放在CDATA区中的数据不会被XML文件解析器解析。
注:使用<![CDATA[]]>的方式只能使用value标签的形式,不能使用value属性!
注:<![CDATA[]]>是XML的语法,放在这里面的东西不会被XML解析器解析!
5个特殊字符对应的转义字符分别是:
特殊字符
转义字符
>
<
<
'
'
"
"
&
&
<?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.bjpowernode.spring6.bean.Math">
<!--直接写2<3会报错-->
<!-- <property name="result" value="2<3"/>-->
<!--第一种解决方案:使用实体符号代替特殊符号-->
<property name="result" value="2 < 3"/>
<!--第二种解决方案:<![CDATA[]]>-->
<property name="result">
<!--只能使用value标签-->
<value><![CDATA[2<3]]></value>
</property>
</bean>
</beans>
4. p命名空间注入
(1)p命名空间是简化set方法注入的。
(2)使用p命名空间注入的前提条件包括两个:
第一:在XML头部信息中添加p命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p"
第二:p命名空间注入还是基于set注入的,只不过p命名空间注入可以让spring配置变的更加简单;所以需要对应的属性提供setter方法。
Dog类:提供了setter方法
package com.bjpowernode.spring6.bean;
import java.util.Date;
public class Dog {
private String name;
private int age;
// 虽然简单类型,但是一般都是当做非简单类型对待
private Date date;
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", date=" + date +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setDate(Date date) {
this.date = date;
}
}
spring-p.xml配置
①在spring的配置文件头部添加p命名空间。
②使用:在<bean>标签中的class属性后面直接使用,对于简单类型属性赋值 **p:属性名 = "属性值";对于非简单类型属性赋值p:属性名-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"
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">
<!--原来注入的方式-->
<bean id="dogBean" class="com.bjpowernode.spring6.bean.Dog">
<property name="name" value="大黄"/>
<property name="age" value="3"/>
<property name="date" ref="nowDate"/>
</bean>
<!--p命名注入方式-->
<bean id="dogBean" class="com.bjpowernode.spring6.bean.Dog" p:name="大黄" p:age="3" p:date-ref="nowDate"/>
<bean id="nowDate" class="java.util.Date"/>
</beans>
测试程序
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.Dog;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class PTest {
@Test
public void testPTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");
Object dogBean = applicationContext.getBean("dogBean", Dog.class);
System.out.println(dogBean);
}
}
执行结果:
如果把setter方法注释掉,会报错
5. c命名空间注入
(1)c命名空间是简化构造方法注入的。
(2)使用c命名空间的两个前提条件:
①第一:需要在xml配置文件头部添加信息: xmlns:c="http://www.springframework.org/schema/c"
②第二:需要提供构造方法。
MyTime类:提供了构造方法
package com.bjpowernode.spring6.bean;
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 +
'}';
}
}
spring-c.xml配置
①在spring的配置文件头部添加c命名空间。
②使用:c:_0 下标方式或者c: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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--原来注入的方式-->
<bean id="myTimeBean" class="com.bjpowernode.spring6.bean.MyTime">
<constructor-arg index="0" value="2022"/>
<constructor-arg index="1" value="1"/>
<constructor-arg index="2" value="14"/>
</bean>
<!--c命名注入方式-->
<bean id="myTimeBean" class="com.bjpowernode.spring6.bean.MyTime" c:_0="2022" c:_1="1" c:_2="14" />
</beans>
测试程序
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.bean.MyTime;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CTest {
@Test
public void testCDI(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
MyTime myTimeBean = applicationContext.getBean("myTimeBean", MyTime.class);
System.out.println(myTimeBean);
}
}
执行结果:
如果把构造方法注释掉
注意:不管是p命名空间还是c命名空间,注入的时候都可以注入简单类型以及非简单类型。
6. util命名空间
(1)使用util命名空间可以让配置复用。
(2)使用util命名空间的前提是:在spring配置文件头部添加配置信息。如下:
(3)假设系统集成不同厂家的连接池,这里用自己写的数据源来代替;里面的连接数据库的配置实际上是相同的,所以我们就可以使用util命名空间进行配置复用!
数据源MyDataSource1
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.Properties;
import java.util.logging.Logger;
public class MyDateSource1 implements DataSource {
// 连接数据库的信息,放到成员变量里
/*private String driver;
private String url;
private String username;
private String password;*/
// 当然也可以放到一个Properties集合当中
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDateSource1{" +
"properties=" + properties +
'}';
}
@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;
}
}
spring-util.xml配置:未使用util命名空间
<?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">
<!--数据源1-->
<bean id="ds1" class="com.bjpowernode.spring6.jdbc.MyDateSource1">
<property name="properties">
<props>
<prop key="dirver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
<!--数据源2-->
<bean id="ds2" class="com.bjpowernode.spring6.jdbc.MyDateSource2">
<property name="properties">
<props>
<prop key="dirver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
</beans>
spring-util.xml配置:使用util命名空间,把公共的配置使用util命名
使用util命名空间后,把重复的配置放到util:properties标签里面,并设置唯一标识id;后面如果想使用,直接使用ref属性直接引入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"
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命名-->
<util:properties id="prop">
<prop key="dirver">com.mysql.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/spring6</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</util:properties>
<!--数据源1-->
<bean id="ds1" class="com.bjpowernode.spring6.jdbc.MyDateSource1">
<property name="properties" ref="prop" />
</bean>
<!--数据源2-->
<bean id="ds2" class="com.bjpowernode.spring6.jdbc.MyDateSource2">
<property name="properties" ref="prop"/>
</bean>
</beans>
测试代码
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.jdbc.MyDateSource1;
import com.bjpowernode.spring6.jdbc.MyDateSource2;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UtilTest {
@Test
public void testUtilTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
MyDateSource1 ds1 = applicationContext.getBean("ds1", MyDateSource1.class);
MyDateSource2 ds2 = applicationContext.getBean("ds2", MyDateSource2.class);
System.out.println(ds1);
System.out.println(ds2);
}
}
执行结果:
实际上util命名空间主要是针对集合的:
7. 基于XML的自动装配(byName & byType)
Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字(byName)进行自动装配,也可以根据类型(byType)进行自动装配。
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);
public void insert(){
logger.info("数据库正在保存用户信息!");
}
}
UserDaoService类
package com.bjpowernode.spring6.service;
import com.bjpowernode.spring6.dao.UserDao;
public class UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void saveUser(){
// 调用UserDao保存用户信息
userDao.insert();
}
}
7.1 根据名称(byName)自动装配
下面这个配置起到关键作用:
(1)UserService Bean中需要添加autowire="byName",表示通过名称进行装配。
(2)如果是正常的装配,UserDao的id随便写,只要和上面ref的值对应着就行!
(3)如果是自动装配,UserDao的id必须是UserService类中的UserDao属性对应的setUserDao(set方法)方法去掉前面的set,后面首字母变成小写的值:userDao!
(4)所以根据名称自动配置本质上也是set注入!
spring-autowire.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="userServiceBean" class="com.bjpowernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
<bean id="userDaoBean" class="com.bjpowernode.spring6.dao.UserDao"/>
<!--根据名称自动装配-->
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService" autowire="byName"/>
<bean id="userDao" class="com.bjpowernode.spring6.dao.UserDao"/>
</beans>
编写测试
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AutowireTest {
@Test
public void testAutowireTest(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}
}
执行结果:
正常执行,说明如果根据名称装配(byName),底层会调用set方法进行注入!例如:setAge() 对应的名字是age,setPassword()对应的名字是password,setEmail()对应的名字是email。
7.2 根据类型(byType)自动装配
(1)其实无论是根据名称自动装备byName还是根据类型制动装备byType,在装配的时候都是基于set方法的,所以set方法是必须要提供的!
(2)根据byType自动装配时,对于被注入的对象,只需要使用bean标签指定要注入的类型,不需要再指定id。
spring-autowire.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="userServiceBean" class="com.bjpowernode.spring6.service.UserService" autowire="byType"/>
<bean class="com.bjpowernode.spring6.dao.UserDao"/>
</beans>
执行结果:
如果byType根据类型装配时,如果配置文件中有两个类型一样的bean会出现什么问题呢?
<?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-->
<bean id="x" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="y" class="com.bjpowernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.bjpowernode.spring6.service.UserService" autowire="byType"/>
</beans>
执行结果:
测试结果说明了,当byType进行自动装配的时候,配置文件中某种类型的Bean必须是唯一的,不能出现多个!
8. Spring引入外部属性配置文件(使用context命名空间)
我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver、url、username password等信息。这些信息可以单独写到一个属性配置文件中吗?这样用户修改起来会更加的方便,当然是可以的,使用context命名空间!
第一步:写一个数据源类,提供相关属性
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> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
第二步:在类路径下新建jdbc.properties文件,并配置信息
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/spring6
username=root
password=123456
第三步:在spring-properties.xml配置文件中引入context命名空间
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
第四步:在spring中配置使用jdbc.properties文件
第一步:引入context命名空间,前面已经引过了。
第二步:使用**context:property-placeholder标签的location**属性来指定属性配置文件的路径。 location默认从类的根路径下开始加载资源。
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部的properties文件-->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.bjpowernode.spring6.jdbc.MyDateSource">
<!--使用$元符号,${key}-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
</beans>
测试程序:
package com.bjpowernode.spring6.test;
import com.bjpowernode.spring6.jdbc.MyDateSource;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class JDBCPropertiesTest {
@Test
public void testProperties(){
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
MyDateSource dataSource = applicationContext.getBean("dataSource", MyDateSource.class);
System.out.println(dataSource);
}
}
执行结果:
这里username怎么不是我们配置文件里的?spring通过${}加载,默认是是先加载windows系统的环境变量!
怎么解决?一般在所有配置前面加上jdbc前缀
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=123456
执行结果:
版权归原作者 @每天都要敲代码 所有, 如有侵权,请联系我们删除。