文章目录
Spring学习目录
环境
spring6里程碑版本的仓库
依赖:spring context依赖、junit依赖、log4j2依赖
log4j2.xml文件放到类路径下。
IoC 控制反转
控制反转是一种思想。
控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
控制反转,反转的是什么?
- 将对象的创建权利交出去,交给第三方容器负责。
- 将对象和对象之间关系的维护权交出去,交给第三方容器负责。
控制反转这种思想如何实现呢?
DI(Dependency Injection):依赖注入
依赖注入
依赖注入实现了控制反转的思想。
Spring通过依赖注入的方式来完成Bean管理的。
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
依赖注入(Dependency Injection 简称DI):
- 依赖:A对象和B对象的关系。
- 注入:通过手段让A与B产生关系(依赖)。
依赖注入常见的实现方式包括两种:
- 1.set注入(执行set方法给属性赋值)
- 2.构造方法注入(执行构造方法给属性赋值)
set注入
创建UserDao
public class UserDao {
//使用slf4j记录日志
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert(){
logger.info("新增用户数据");
}
}
创建UserService
必须提供set方法,Spring就会调用这个set方法,给userDao属性赋值,这里的set的方法可以随意,只需要以set开头即可,但是之后在spring配置文件配置set注入的时候就不能随便写了。这里先不按照规范写一个set方法测试。
/**
* set注入
*/
public class UserService {
private UserDao userDao;
//提供set方法之后,还需要在配置文件里面配置
public void setMyUserDao(UserDao xy){
this.userDao = xy;
}
public void saveUser(){
System.out.println("set注入");
userDao.insert();
}
}
创建spring.xml配置文件
通过property标签进行set注入。
- property标签:配置set注入
- 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--set注入-->
<!--配置dao-->
<bean id="userDaoBean" class="com.dependency.injection.dao.UserDao"/>
<!--配置service-->
<bean id="userServiceBean" class="com.dependency.injection.service.UserService">
<property name="myUserDao" ref="userDaoBean"/>
</bean>
<bean id="carDaoBean" class="com.dependency.injection.dao.CarDao"></bean>
</beans>
测试程序:
@Test
public void testSetDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}
实现原理:
- 1.通过property标签获取到set方法名称:setMyUserDao
- 2.通过反射机制推断出属性名:userDao
this.userDao = xy;
- 3.通过反射机制调用setUserDao()方法给属性赋值
说明property标签的name是:setUserDao()方法名演变得到的。演变的规律是:
- setUsername() 演变为 username
- setPassword() 演变为 password
- setUserDao() 演变为 userDao
- setUserService() 演变为 userService
一般情况下set方法名不会自定义,编写都是要符合java规范,也可以采用自动生成
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
所以spring.xml配置文件property标签的name就可以直接复制属性名
<property name="userDao" ref="userDaoBean"/>
构造注入
核心原理:通过调用构造方法来给属性赋值。
创建CarDao:
public class CarDao {
//使用slf4j记录日志
private static final Logger logger = LoggerFactory.getLogger(CarDao.class);
public void insert(){
logger.info("新增汽车数据");
}
}
创建CarService:
提供构造方法
/**
* 构造注入
*/
public class CarService {
private UserDao userDao;
private CarDao carDao;
public CarService(UserDao userDao, CarDao carDao) {
this.userDao = userDao;
this.carDao = carDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void setCarDao(CarDao carDao) {
this.carDao = carDao;
}
public void saveCar(){
System.out.println("构造注入");
userDao.insert();
carDao.insert();
}
}
创建spring2.xml配置文件:
构造注入需要在这个文件配置,通过constructor-arg标签配置。
- constructor-arg标签:配置构造注入
- index属性:指定的参数下标,以0开始
- name属性:指定参数的名字
- ref属性:指定的是要注入的bean的id
构造注入有三种配置方式:
- 1.根据参数下标注入,使用index属性
- 2.根据参数名字注入,使用name属性
- 3.不指定参数下标和名字,Spring可以自己做自动类型匹配把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.dependency.injection.dao.UserDao"></bean>
<bean id="carDaoBean" class="com.dependency.injection.dao.CarDao"></bean>
<!--配置service-->
<bean id="carServiceBean" class="com.dependency.injection.service.CarService">
<!--根据参数下标注入-->
<constructor-arg index="0" ref="userDaoBean" />
<constructor-arg index="1" ref="carDaoBean" />
</bean>
<bean id="carServiceBean2" class="com.dependency.injection.service.CarService">
<!--根据参数名字注入-->
<constructor-arg name="userDao" ref="userDaoBean" />
<constructor-arg name="carDao" ref="carDaoBean" />
</bean>
<bean id="carServiceBean3" class="com.dependency.injection.service.CarService">
<!--不指定参数下标和名字,Spring可以自己做自动类型匹配把ref注入-->
<constructor-arg ref="userDaoBean" />
<constructor-arg ref="carDaoBean" />
</bean>
</beans>
测试程序:
@Test
public void testConstructorDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring2.xml");
//根据参数下标注入测试
CarService carServiceBean = applicationContext.getBean("carServiceBean", CarService.class);
carServiceBean.saveCar();
//根据参数名字注入测试
CarService carServiceBean2 = applicationContext.getBean("carServiceBean2", CarService.class);
carServiceBean2.saveCar();
//不指定参数下标和名字测试
CarService carServiceBean3 = applicationContext.getBean("carServiceBean", CarService.class);
carServiceBean3.saveCar();
}
set注入专题
注入外部Bean
注入外部Bean就是使用ref属性注入,上面的set注入就是一个外部Bean注入
创建OrderDao:
public class OrderDao {
private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);
public void insert(){
logger.info("生成订单。。。");
}
}
创建OrderService:
public class OrderService {
private OrderDao orderDao;
public void setOrderDao(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void generate(){
orderDao.insert();
}
}
创建配置文件set-DI.xml
<!--
注入外部Bean:在property标签使用ref属性注入
-->
<!--定义Bean-->
<bean id="orderDaoBean" class="com.dependency.injection.dao.setDI.OrderDao"/>
<bean id="orderServiceBean" class="com.dependency.injection.service.setDI.OrderService">
<!--注入外部Bean:使用ref属性注入-->
<property name="orderDao" ref="orderDaoBean"/>
</bean>
测试程序:
@Test
public void testSetDI2(){
//外部Bean测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
orderServiceBean.generate();
}
注入内部Bean(了解)
在property标签嵌套bean标签注入,称为内部Bean
在set-DI.xml配置:
<!--
注入内部Bean
-->
<bean id="orderServiceBean2" class="com.dependency.injection.service.setDI.OrderService">
<property name="orderDao">
<bean class="com.dependency.injection.dao.setDI.OrderDao"/>
</property>
</bean>
测试程序:
@Test
public void testSetDI2(){
//外部Bean测试
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
orderServiceBean.generate();
//内部Bean测试
OrderService orderServiceBean2 = applicationContext.getBean("orderServiceBean2", OrderService.class);
orderServiceBean2.generate();
}
注入简单类型
我们之前在进行注入的时候,对象的属性是另一个对象。使用的是一个ref属性。
如果我们注入的是一个int类型,我们就需要使用value属性赋值。
注入简单类型,Spring规定了哪些是简单类型呢,进入源码可以看一下BeanUtils工具类
public static boolean isSimpleValueType(Class<?> type) {
return Void.class != type && Void.TYPE != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type);
}
分别包括:
- isPrimitiveOrWrapper:基本类型或其对应的包装类
- Enum:枚举类型
- CharSequence: 字符接口,凡是实现了字符接口都为简单类型,String实现了该接口,所以String为简单类型
- Number:数字类型,凡是继承这个抽象类,都为简单类型
- Date:日期类型
- Temporal:时间和时区接口,这个是java8以后的新特性
- URI:uri类
- URL:url类
- Locale:语言类,例如英文、中文等等
- Class:class类
需要注意的是Date类型,想要使用Value进行赋值的话,这个value是有格式要求的。例如
这种格式是无法转换成java.util.Date类型的
<property name="birth" value="2020-11-11"/>
需要以下特定格式:
<property name="birth" value="Wed Nov 02 18:47:56 CST 2022"/>
但是这个格式是外国人看得比较舒服的格式,中国人看不习惯,所以,在实际开发中,不会把这个Date当作简单类型,一般采用ref进行赋值
我们来测试以下简单类型的注入
枚举类:
/**
* 表示季节
*/
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
创建SimpleValueType类:
/**
* 测试简单类型
*/
public class SimpleValueType {
//基本类型及其包装类
private boolean flag;
private Boolean flag2;
private char c;
private Character c2;
//枚举类型
private Season season;
//CharSequence
private String username;
//Number
private int age;
private Integer age2;
//Class
private Class clazz;
//Date
private Date birth;
@Override
public String toString() {
return "SimpleValueType{" +
"flag=" + flag +
", flag2=" + flag2 +
", c=" + c +
", c2=" + c2 +
", season=" + season +
", username='" + username + '\'' +
", age=" + age +
", age2=" + age2 +
", clazz=" + clazz +
", birth=" + birth +
'}';
}
public void setBirth(Date birth) {
this.birth = birth;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public void setFlag2(Boolean flag2) {
this.flag2 = flag2;
}
public void setC(char c) {
this.c = c;
}
public void setC2(Character c2) {
this.c2 = c2;
}
public void setSeason(Season season) {
this.season = season;
}
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
public void setAge2(Integer age2) {
this.age2 = age2;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
}
set-DI.xml文件配置
<!--测试简单类型-->
<bean id="svtBean" class="com.dependency.injection.bean.SimpleValueType">
<property name="age" value="20"/>
<property name="age2" value="20"/>
<property name="username" value="张三"/>
<property name="season" value="SPRING"/>
<property name="flag" value="false"/>
<property name="flag2" value="true"/>
<property name="c" value="男"/>
<property name="c2" value="女"/>
<property name="clazz" value="java.lang.String"/>
<property name="birth" value="Wed Nov 02 18:47:56 CST 2022"/>
<!--这个格式是外国人看得比较舒服的格式,中国人看不习惯,所以,在实际开发中,不会把这个Date当作简单类型,一般采用ref进行赋值-->
</bean>
测试程序:
@Test
public void testSimpleTypeSet(){
//简单类型
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
SimpleValueType svtBean = applicationContext.getBean("svtBean", SimpleValueType.class);
System.out.println(svtBean);
}
经典案例:给数据源的属性注入值
简单类型注入有什么用呢?
假设我们现在要自己手写一个数据源,我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password等。可以让Spring容器管理数据源
/**
* 把数据源交给Spring容器管理
*/
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + 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 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;
}
}
set-DI.xml配置:
<bean id="myDataSource" class="com.dependency.injection.jdbc.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>
测试程序:
@Test
public void testMyDataSource(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-DI.xml");
MyDataSource myDataSource = applicationContext.getBean("myDataSource", MyDataSource.class);
System.out.println(myDataSource);
}
级联属性赋值(了解)
创建Clazz班级类:
/**
* 班级,级联赋值
*/
public class Clazz {
private String name;//班级名称
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Clazz{" +
"name='" + name + '\'' +
'}';
}
}
创建Student学生类:
/**
* 学生,级联赋值
*/
public class Student {
private String name;
private Clazz clazz;//学生属于哪个班级
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
public void setName(String name) {
this.name = name;
}
public Clazz getClazz() {
return clazz;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", clazz=" + clazz +
'}';
}
}
创建cascade.xml配置文件:
级联赋值,需要有两个注意点:
- 1.这种情况Student里面还想要提供这个clazz属性的get方法
- 2.这种方式赋值,想要保证clazz有值,也就是说先配置clazz的注入信息,再进行赋值,顺序不能颠倒。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="studentBean" class="com.dependency.injection.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean" />
<!--这里的顺序不能颠倒,并且保证Student类里有getName方法-->
<property name="clazz.name" value="高三二班" />
</bean>
<!--<bean id="clazzBean" class="com.dependency.injection.bean.Clazz">
<property name="name" value="高三一班"/>
</bean>-->
<bean id="clazzBean" class="com.dependency.injection.bean.Clazz"></bean>
</beans>
测试程序:
@Test
public void testCascade(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("cascade.xml");
Clazz clazzBean = applicationContext.getBean("clazzBean", Clazz.class);
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
System.out.println(clazzBean);
}
注入数组
简单类型数组注入
创建Arrays类
/**
* 注入数组
*/
public class Arrays {
private String[] like;//爱好
public void setLike(String[] like) {
this.like = like;
}
@Override
public String toString() {
return "Arrays{" +
"like=" + java.util.Arrays.toString(like) +
'}';
}
}
创建SpringArray.xml文件配置:
属性是简单类型,并且是数组,property标签里面使用array标签套value标签,value标签里面输入值
<!--数组注入-->
<bean id="arrayBean" class="com.dependency.injection.bean.Arrays">
<property name="like">
<array>
<value>唱</value>
<value>跳</value>
<value>rap</value>
</array>
</property>
</bean>
测试程序:
@Test
public void testArrays(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("SpringArray.xml");
Arrays arrayBean = applicationContext.getBean("arrayBean", Arrays.class);
System.out.println(arrayBean);
}
非简单类型数组注入
创建朋友类:
/**
* 朋友类,注入数组
*/
public class Friend {
private String name;
@Override
public String toString() {
return "Friend{" +
"name='" + name + '\'' +
'}';
}
public void setName(String name) {
this.name = name;
}
}
修改Arrays类:
/**
* 注入数组
*/
public class Arrays {
private String[] like;//爱好
//多个朋友
private Friend[] friends;
public void setFriends(Friend[] friends) {
this.friends = friends;
}
@Override
public String toString() {
return "Arrays{" +
"like=" + java.util.Arrays.toString(like) +
", friends=" + java.util.Arrays.toString(friends) +
'}';
}
public void setLike(String[] like) {
this.like = like;
}
/*@Override
public String toString() {
return "Arrays{" +
"like=" + java.util.Arrays.toString(like) +
'}';
}*/
}
修改SpringArray.xml配置文件:
不是简单类型,且是数组
需要提前创建好Friend的bean,使用array标签,套ref标签,在bean属性中填Friend的id
<bean id="arrayBean" class="com.dependency.injection.bean.Arrays">
<property name="like">
<array>
<value>唱</value>
<value>跳</value>
<value>rap</value>
</array>
</property>
<property name="friends">
<array>
<ref bean="friendBean1"/>
<ref bean="friendBean2"/>
<ref bean="friendBean3"/>
</array>
</property>
</bean>
<bean id="friendBean1" class="com.dependency.injection.bean.Friend">
<property name="name" value="张三"/>
</bean>
<bean id="friendBean2" class="com.dependency.injection.bean.Friend">
<property name="name" value="李四"/>
</bean>
<bean id="friendBean3" class="com.dependency.injection.bean.Friend">
<property name="name" value="王五"/>
</bean>
再次运行测试程序:
注入集合
注入List集合
创建Collection类
/**
* 注入集合
*/
public class Collection {
//注入List集合
private List<String> names;
public void setNames(List<String> names) {
this.names = names;
}
@Override
public String toString() {
return "Collection{" +
"names=" + names +
'}';
}
}
创建spring-collection.xml配置:
注入List集合,与注入数组类似,简单类型使用list标签套value标签,非简单类型就套ref标签
这里演示简单类型
List集合:有序可重复
<bean id="collectionBean" class="com.dependency.injection.bean.Collection">
<property name="names">
<!--注入List集合-->
<list>
<value>张三</value>
<value>李四</value>
<value>王五</value>
<value>赵六</value>
<value>赵六</value>
</list>
</property>
</bean>
测试程序:
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
Collection collectionBean = applicationContext.getBean("collectionBean", Collection.class);
System.out.println(collectionBean);
}
注入Set集合
修改Collection类:
/**
* 注入集合
*/
public class Collection {
//注入List集合
private List<String> names;
//注入Set集合
private Set<String> addrs;
@Override
public String toString() {
return "Collection{" +
"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配置:
注入Set集合,也是与数组差不多,简单类型使用set标签套value标签,非简单类型就套ref标签
这里演示简单类型
Set集合:无序不可重复
<bean id="collectionBean" class="com.dependency.injection.bean.Collection">
<!--注入List集合-->
<property name="names">
<list>
<value>张三</value>
<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>
再次运行测试程序,发现没有两个广州,无序不可重复
注入Map集合
修改Collection类:
/**
* 注入集合
*/
public class Collection {
//注入List集合
private List<String> names;
//注入Set集合
private Set<String> addrs;
//注入Map
private Map<Integer,String> phones;
@Override
public String toString() {
return "Collection{" +
"names=" + names +
", addrs=" + addrs +
", phones=" + phones +
'}';
}
public void setPhones(Map<Integer, String> phones) {
this.phones = phones;
}
public void setNames(List<String> names) {
this.names = names;
}
public void setAddrs(Set<String> addrs) {
this.addrs = addrs;
}
}
修改spring-collection.xml配置:
注入Map集合 使用map标签套entry标签
- 如果是简单类型,则entry标签的属性使用key和value
- 如果非简单类型,则entry标签的属性使用key-ref和value-ref
key是无序不可重复,value有序可重复
注意:如果key或key-ref没有输入值的话,默认会是null,且只能有一个null
<!--集合注入-->
<bean id="collectionBean" class="com.dependency.injection.bean.Collection">
<!--注入List集合-->
<property name="names">
<list>
<value>张三</value>
<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>
<!--注入Map集合-->
<property name="phones">
<map>
<entry key="1" value="110"/>
<entry key="2" value="111"/>
<entry key="3" value="119"/>
<entry key="" value="120"/>
</map>
</property>
</bean>
再次运行程序
注入Properties
注入Properties属性类,Properties本质上也是Map集合,
/因为它的父类是一个Hashtbale实现了Map接口。但是注入方式不一样,Properties的key和value的类型只能是String
修改Collection类:
/**
* 注入集合
*/
public class Collection {
//注入List集合
private List<String> names;
//注入Set集合
private Set<String> addrs;
//注入Map
private Map<Integer,String> phones;
//注入Properties
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "Collection{" +
"names=" + names +
", addrs=" + addrs +
", phones=" + phones + "\n" +
", properties=" + properties +
'}';
}
public void setPhones(Map<Integer, String> phones) {
this.phones = phones;
}
public void setNames(List<String> names) {
this.names = names;
}
public void setAddrs(Set<String> addrs) {
this.addrs = addrs;
}
}
修改spring-collection.xml配置:
注入Properties属性类
- 使用props标签套prop标签
- key写在prop标签key的属性里面,value写在prop标签里面
注意:与map不一样key如果没写值,它就不是null,它是真的为空–“”
<bean id="collectionBean" class="com.dependency.injection.bean.Collection">
<!--注入List集合-->
<property name="names">
<list>
<value>张三</value>
<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>
<!--注入Map集合 -->
<property name="phones">
<map>
<entry key="1" value="110"/>
<entry key="2" value="111"/>
<entry key="3" value="119"/>
<entry key="" value="120"/>
</map>
</property>
<!--注入Properties属性类-->
<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>
<prop key="">空</prop>
</props>
</property>
</bean>
再次运行测试程序:
注入null和空字符串
创建Empty类
/**
* 注入null或空字符串
*/
public class Empty {
//注入null
private String name1;
private String name2;
//注入空字符串
private String name3;
private String name4;
public void setName1(String name1) {
this.name1 = name1;
}
public void setName2(String name2) {
this.name2 = name2;
}
public void setName3(String name3) {
this.name3 = name3;
}
public void setName4(String name4) {
this.name4 = name4;
}
@Override
public String toString() {
return "Empty{" +
"name1='" + name1 + '\'' +
", name2='" + name2 + '\'' +
", name3='" + name3 + '\'' +
", name4='" + name4 + '\'' +
'}';
}
}
创建spring-empty.xml配置:
注入null,两种方式
- 不给name属性赋值
- 或者在property里面使用null标签
注意:如果在value里面写一个null,这是赋值一个字符串null,而不是一个真正的null
注入空字符串,两种方式
- 不给value赋值,默认为空字符串
- 或者在property里面使用value标签,不给这个标签赋值
<!--
注入null或空字符串
-->
<bean id="emptyBean" class="com.dependency.injection.bean.Empty">
<!--注入null,name2不赋值-->
<property name="name1">
<null/>
</property>
<!--注入空字符串-->
<property name="name3" value=""/>
<property name="name4">
<value/>
</property>
</bean>
测试程序:
@Test
public void testEmpty(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-empty.xml");
Empty emptyBean = applicationContext.getBean("emptyBean", Empty.class);
System.out.println(emptyBean);
}
注入特殊字符
XML中有5个特殊字符,分别是:<、>、'、"、&
以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。
解决方案包括两种:
第一种方案:特殊符号使用转义字符(实体符号)代替。
第二种方案:使用<![CDATA[]]>,因为放在CDATA区中的数据不会被XML文件解析器解析。它会把里面的内容当中一个普通字符串处理。
注意:使用<![CDATA[]]>,不能在property使用value属性了,只能嵌套一个value标签
5个特殊字符对应的转义字符分别是:
特殊字符 | 转义字符 |
---|---|
> | > |
< | < |
’ | ' |
" | " |
& | & |
创建MathBean类:
/**
* 注入特殊字符
*/
public class MathBean {
private String result1;//使用转义字符
private String result2;//使用<![CDATA[]]>
public void setResult1(String result1) {
this.result1 = result1;
}
public void setResult2(String result2) {
this.result2 = result2;
}
@Override
public String toString() {
return "MathBean{" +
"result1='" + result1 + '\'' +
", result2='" + result2 + '\'' +
'}';
}
}
创建spring-special.xml:
<!--注入特殊字符-->
<bean id="mathBean" class="com.dependency.injection.bean.MathBean">
<property name="result1" value="2 < 3"/>
<property name="result2">
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
测试程序:
@Test
public void testSpecial(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-special.xml");
MathBean mathBean = applicationContext.getBean("mathBean", MathBean.class);
System.out.println(mathBean);
}
p命名空间注入
目的:简化配置。
创建P类
/**
* p命名空间注入
*/
public class P {
private String name;
private int age;
private Date birth;
@Override
public String toString() {
return "P{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
创建spring-p.xml配置:
p命名空间注入,底层还是set注入,只不过p命名空间注入可以让spring配置变得更加简单。
使用步骤:
- 第一步:使用p命名空间,需要在beans标签加上一个配置
xmlns:p=“http://www.springframework.org/schema/p” - 第二步:在bean标签使用即可,普通类型使用p:属性名=“属性值”,非简单类型使用p:属性名-ref=“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: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="pBean" class="com.dependency.injection.bean.P" p:age="20" p:name="张三" p:birth-ref="birthBean"/>
<bean id="birthBean" class="java.util.Date"/>
</beans>
测试程序:
@Test
public void testP(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spting-p.xml");
P pBean = applicationContext.getBean("pBean", P.class);
System.out.println(pBean);
}
c命名空间注入
目的:简化配置。
创建C类:
/**
* c命名空间注入
*/
public class C {
private String name;
private int age;
private Date birth;
public C(String name, int age, Date birth) {
this.name = name;
this.age = age;
this.birth = birth;
}
@Override
public String toString() {
return "C{" +
"name='" + name + '\'' +
", age=" + age +
", birth=" + birth +
'}';
}
}
创建spring-c.xml配置:
c命名空间注入,底层还是基于构造方法注入的,c命名空间注入是用来简化构造注入的
使用步骤:
-
第一步:与p命名空间一样,需要在上面beans标签加上一个配置
xmlns:c=“http://www.springframework.org/schema/c” -
第二步:在bean标签使用即可
-
简单类型:根据下标:c:_下标=“属性值”,根据属性名:c:属性名=“属性值”
-
非简单类型:根据下标:c:_下标-ref=“属性值”,根据属性名:c:属性名-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: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="cBean" class="com.dependency.injection.bean.C" c:_0="张三" c:age="20" c:birth-ref="birthBean"/>
<bean id="birthBean" class="java.util.Date"/>
</beans>
测试程序:
@Test
public void testC(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
C cBean = applicationContext.getBean("cBean", C.class);
System.out.println(cBean);
}
util命名空间
util命名空间注入,主要是让配置复用。
例如写两个数据源,都是同样的配置,就可以使用utils
创建数据源MyDataSource1类:
/**
* 把数据源交给Spring容器管理
*/
public class MyDataSource1 implements DataSource {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource1{" +
"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;
}
}
创建数据源MyDataSource2类:
/**
* 把数据源交给Spring容器管理
*/
public class MyDataSource2 implements DataSource {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource1{" +
"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配置:
- 第一步:使用utils命名空间,需要在上面beans标签加上两个配置
xmlns:utils="http://www.springframework.org/schema/util"
xsi里面添加,用来约束xml文件的
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
-
第二步:配置相关的util标签
-
第三步:在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 http://www.springframework.org/schema/util/spring-util.xsd">
<util:properties id="properties">
<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="ds1" class="com.dependency.injection.jdbc.MyDataSource1">
<property name="properties" ref="properties"/>
</bean>
<!--数据源2-->
<bean id="ds2" class="com.dependency.injection.jdbc.MyDataSource2">
<property name="properties" ref="properties"/>
</bean>
</beans>
测试程序:
@Test
public void testUtil(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
MyDataSource1 ds1 = applicationContext.getBean("ds1", MyDataSource1.class);
MyDataSource2 ds2 = applicationContext.getBean("ds2", MyDataSource2.class);
System.out.println(ds1);
System.out.println(ds2);
}
基于XML的自动装配
Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配。
自动装配也是基于set方式实现的。
根据名称自动装配
还是使用之前的OrderDao和OrderService
创建spring-outowire.xml配置:
根据名称自动装配
- 第一步:需要在bean标签配置属性autowire=“byName”
- 第二步:被注入的bean的id就不能随便写了,必须写set方法,去掉set,然后首字母小写。
<bean id="orderDao" class="com.dependency.injection.dao.setDI.OrderDao"/>
<bean id="orderService" class="com.dependency.injection.service.setDI.OrderService" autowire="byName"></bean>
根据类型自动装配
还是使用之前的UserDao、UserService
修改spring-outowire.xml:
根据类型自动装配
只需要在bean标签配置属性autowire=“byType”
注意:根据类型自动装配的使用,在有效的配置文件当中,某种类型的实例只能有一个,也就是说某种类型的bean只能有一个。
例如配置以下程序之后会报错:
因为在之前的配置文件有UserDao的bean对象,Spring“晕了”,不知道哪个bean是需要注入。
把之前的bean对象注释掉即可
<!--根据名称自动装配-->
<bean id="orderDao" class="com.dependency.injection.dao.setDI.OrderDao"/>
<bean id="orderService" class="com.dependency.injection.service.setDI.OrderService" autowire="byName"></bean>
<!--根据类型自动装配-->
<bean class="com.dependency.injection.dao.UserDao"/>
<bean id="userDaoBean" class="com.dependency.injection.service.UserService" autowire="byType"/>
测试程序:
@Test
public void testOutowrieByType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-outowire.xml");
UserService userService = applicationContext.getBean("userDaoBean", UserService.class);
userService.saveUser();
}
Spring引入外部属性配置文件
引入外部的properties文件
创建数据源MyDataSource3类:
public class MyDataSource3 implements DataSource {
private String driver;
private String url;
private String username;
private String password;
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + 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 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.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/spring
username=root
password=123456
创建spring-properties.xml配置:
引入外部的properties文件
- 第一步:引入context命名空间。
xmlns:context="http://www.springframework.org/schema/context"
在xsi引入 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- 第二步:使用context:property-placeholder标签location属性的引入,location是默认从类的根路径加载。
<context:property-placeholder location="jdbc.properties"/>
- 第三步:在property标签的value属性使用${key}进行取值。
<?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">
<context:property-placeholder location="jdbc.properties"/>
<bean id="myDataSource" class="com.dependency.injection.jdbc.MyDataSource3">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
</beans>
测试程序:
@Test
public void testProperties(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
MyDataSource3 myDataSource = applicationContext.getBean("myDataSource", MyDataSource3.class);
System.out.println(myDataSource);
}
运行后发现username居然不是root,而是shinle,而我的电脑名称也是shinle,是不是很巧。
这是因为${}进行加载的时候,Spring默认加载的是电脑的系统环境,所以配置文件一般都会加一个前缀。
jdbc.properties文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=123456
spring-properties.xml配置文件改为:
<context:property-placeholder location="jdbc.properties"/>
<bean id="myDataSource" class="com.dependency.injection.jdbc.MyDataSource3">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
再次运行: