文章目录
IoC控制反转
- 控制反转是一种思想
- 控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
- 控制反转,反转的是什么?
- 将对象的创建权力交出去,交给第三方容器负责
- 将对象和对象之间关系的维护权交出去,交给第三方容器负责
- 控制反转思想的实现方式:依赖注入(DI)
依赖注入
依赖注入实现了控制反转思想
Spring通过依赖注入的方式来完成Bean管理的
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)
依赖注入:
- 依赖指的是对象和对象之间的关联关系。
- 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系。
依赖注入常见的实现方式包括两种:
- 第一种:ser注入
- 第二种:构造注入
set注入
set注入,基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值。这种方式要求属性必须对外提供set方法。
//UserDao
package com.powernode.spring6.dao;
public class UserDao {
public void insert(){
System.out.println("正在保存用户数据。");
}
}
//UserService
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
public class UserService {
private UserDao userDao;
// 使用set方式注入,必须提供set方法。
// 反射机制要调用这个方法给属性赋值的。
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save(){
userDao.insert();
}
}
<!-- spring.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="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
</beans>
//test
public class DITest {
@Test
public void testSetDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
userService.save();
}
}
实现原理:
通过property标签获取到属性名:userDao
通过属性名推断出set方法名:setUserDao
通过反射机制调用setUserDao()方法给属性赋值
通过·property标签的name是属性名。
property标签的ref是要注入的bean对象的id(通过ref属性来完成bean的装配,这是bean最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)
底层时机调用了setUserDao()方法,所以需要确保这个方法存在
property标签的name是:setUserDao()方法名演变得到的。
演变规律:
- setUsername()演变为username
- setPassword()演变为password
- setUserDao()演变为userDao
- setUserService演变为userService
注入外部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 id="userDaoBean" class="com.powernode.spring6.dao.UserDao"/>
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
</bean>
</beans>
外部Bean的特点:bean定义到外面,在property标签中使用ref属性进行注入。
(好像没什么区别)
注入内部Bean
内部注入Bean的方式:在Bean标签中嵌套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 id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<bean class="com.powernode.spring6.dao.UserDao"/>
</property>
</bean>
</beans>
@Test
public void testInnerBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-inner-bean.xml");
UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
userService.save();
}
了解了解,用的不多
注入简单类型
我们在注入的时候,对象的属性是另一个对象,但是并没有涉及简单类型
package com.powernode.spring6.beans;
public class User {
private int age;
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
'}';
}
}
<?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.powernode.spring6.beans.User">
<!--如果像这种int类型的属性,我们称为简单类型,这种简单类型在注入的时候要使用value属性,不能使用ref-->
<!--<property name="age" value="20"/>-->
<property name="age">
<value>20</value>
</property>
</bean>
</beans>
//test
@Test
public void testSimpleType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-simple-type.xml");
User user = applicationContext.getBean("userBean", User.class);
System.out.println(user);
}
给简单类型注入,只要用value标签或者value属性给它赋上值就行了
注意不是ref,ref译为参考,后面加的是个类名,参考这个类,就是它最后生成的样子就是这个类,参考这个类注入。
简单类型包括:
- 基本数据类型
- 基本数据类型对应的包装类
- String或其他的CharSequence子类
- Number子类
- Date子类(这玩意不好用,格式很复杂,正常人记不住)
- Enum子类
- URI
- URL
- Temporal子类
- Local
- Class
- 另外还包括以上简单类型对应的数组类型。
通过注入方式实现javax.sql.DateSource接口
//MyDataSource.java
package com.powernode.spring6.beans;
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 MyDataSource 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 "MyDataSource{" +
"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 -->
<?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="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>
//test
@Test
public void testDataSource(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml");
MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class);
System.out.println(dataSource);
}
测试简单类型
//类A
package com.powernode.spring6.beans;
import java.net.URI;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.Locale;
public class A {
private byte b;
private short s;
private int i;
private long l;
private float f;
private double d;
private boolean flag;
private char c;
private Byte b1;
private Short s1;
private Integer i1;
private Long l1;
private Float f1;
private Double d1;
private Boolean flag1;
private Character c1;
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
}
<!-- spring-all-simple-type.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="a" class="com.powernode.spring6.beans.A">
<property name="b" value="1"/>
<property name="s" value="1"/>
<property name="i" value="1"/>
<property name="l" value="1"/>
<property name="f" value="1"/>
<property name="d" value="1"/>
<property name="flag" value="false"/>
<property name="c" value="a"/>
<property name="b1" value="2"/>
<property name="s1" value="2"/>
<property name="i1" value="2"/>
<property name="l1" value="2"/>
<property name="f1" value="2"/>
<property name="d1" value="2"/>
<property name="flag1" value="true"/>
<property name="c1" value="a"/>
<property name="str" value="zhangsan"/>
<!--注意:value后面的日期字符串格式不能随便写,必须是Date对象toString()方法执行的结果。-->
<!--如果想使用其他格式的日期字符串,就需要进行特殊处理了。具体怎么处理,可以看后面的课程!!!!-->
<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>
//test
@Test
public void testAllSimpleType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-all-simple-type.xml");
A a = applicationContext.getBean("a", A.class);
System.out.println(a);
}
需要注意的是:
如果把Date当作简单类型的话,日期字符串格式不能随便写(随便写的话,会报错这是个字符串类型,不是date类型)。格式必须符合Date的toString方法格式(长这个臭样”Fri Sep 30 15:26:38 CST 2022“)。如果我们提供一个这样的日期字符串:2010-10-10,在这里无法赋值给Date类型的属性的。
spring6之后,当注入的是URL,那么这个url字符串是会进行有效性检测的。如果是一个存在的url,那就没问题。如果不存在就会报错
级联属性赋值(了解)
这个名字让我想起了mybatis里的级联赋值(帮助完成一对多,多对一映射的关联)
直接看一下如何在spring配置bean文件里的代码,我的评价是不如constructor-arg
标签好用
<?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">
<property name="name" value="张三"/>
<!--要点1:以下两行配置的顺序不能颠倒-->
<property name="clazz" ref="clazzBean"/>
<!--要点2:clazz属性必须有getter方法-->
<property name="clazz.name" value="高三一班"/>
</bean>
</beans>
这种方式注意:
- 在spring配置文件中注意顺序
- 在spring配置文件中,clazz属性必须提供getter方法。
注入数组
当数组的元素是简单类型:
//Person.java
package com.powernode.spring6.beans;
import java.util.Arrays;
public class Person {
private String[] favariteFoods;
public void setFavariteFoods(String[] favariteFoods) {
this.favariteFoods = favariteFoods;
}
@Override
public String toString() {
return "Person{" +
"favariteFoods=" + Arrays.toString(favariteFoods) +
'}';
}
}
<!-- spring-array-simple.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="person" class="com.powernode.spring6.beans.Person">
<property name="favariteFoods">
<array>
<value>鸡排</value>
<value>汉堡</value>
<value>鹅肝</value>
</array>
</property>
</bean>
</beans>
//test
@Test
public void testArraySimple(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array-simple.xml");
Person person = applicationContext.getBean("person", Person.class);
System.out.println(person);
}
当注入的元素是非简单类型:(一个订单中包含多个商品)
//Goods.java
package com.powernode.spring6.beans;
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 + '\'' +
'}';
}
}
//Order.java
package com.powernode.spring6.beans;
import java.util.Arrays;
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) +
'}';
}
}
<!-- spring-array.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="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>
//test
@Test
public void testArray(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array.xml");
Order order = applicationContext.getBean("order", Order.class);
System.out.println(order);
}
要点:
- 如果数组中的简单类型,使用
value
标签直接赋值。 - 如果数组是非简单类型,使用
ref
标签赋值。
注入List集合
List集合的特性:有序可重复
package com.powernode.spring6.beans;
import java.util.List;
public class People {
// 一个人有多个名字
private List<String> names;
public void setNames(List<String> names) {
this.names = names;
}
@Override
public String toString() {
return "People{" +
"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="names">
<list>
<value>张三</value>
<value>李四</value>
<value>王五</value>
<value>赵六</value>
<value>钱七</value>
<value>勾八</value>
<value>勾八</value>
<value>勾八</value>
</list>
</property>
</bean>
</beans>
//test
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注意:
注入List集合的时候使用list
标签,如果List集合中是简单类型使用value标签;反之使用ref
标签
注入Set集合
Set集合的特性:无序,不可重复
//People.java
package com.powernode.spring6.beans;
import java.util.List;
import java.util.Set;
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 +
'}';
}
}
<?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>
</set>
</property>
</bean>
</beans>
注意:
使用<set>
标签
set集合中元素是简单类型的使用value标签;反之使用ref标签
注入Map集合
//People.java
package com.powernode.spring6.beans;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 +
'}';
}
}
<!-- spring-coolection.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="peopleBean" class="com.powernode.spring6.beans.People">
<property name="addrs">
<map>
<!--如果key不是简单类型,使用 key-ref 属性-->
<!--如果value不是简单类型,使用 value-ref 属性-->
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
</map>
</property>
</bean>
</beans>
要点:
使用<map>
标签
如果key是简单类型,使用key属性;反之使用key-ref
属性
如果value是简单类型,使用value属性,反之使用value-ref
属性
注入Properties
java.util.Properties
继承java.uril.Hashtable
,所以Properties
也是一个Map集合。
//People.java
package com.powernode.spring6.beans;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
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 +
'}';
}
}
<!-- spring-collection.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="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>
要点:
使用<props>
标签嵌套<prop>
标签完成。
注入null和空字符串
- 注入空字符串使用:
<value/>
或者value=""
- 注入null使用:
<null>
或者不为该属性赋值
//Vip.java
package com.powernode.spring6.beans;
public class Vip {
private String email;
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Vip{" +
"email='" + email + '\'' +
'}';
}
}
<!-- spring-null,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="vipBean" class="com.powernode.spring6.beans.Vip">
<!--空串的第一种方式-->
<!--<property name="email" value=""/>-->
<!--空串的第二种方式-->
<property name="email">
<value/>
</property>
</bean>
</beans>
//test
@Test
public void testNull(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-null.xml");
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
如何注入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" />
</beans>
使用<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">
<property name="email">
<null/>
</property>
</bean>
</beans>
注入的值中含有特殊符号
XML中有5个特殊字符,分别是<
、>
、'
、"
、&
以上五个特殊符号在XML中会被特殊对待,会被当作XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错
解决方案包括两种
- 第一种:特殊符号使用转义字符代替
- 第二种:将含有特殊符号的字符串放到:
<![CDATE[]]>
当中。因为放在CDATE区中的数据不会被XML文件解析器解析。
转义字符
//Math.java
package com.powernode.spring6.beans;
public class Math {
private String result;
public void setResult(String result) {
this.result = result;
}
@Override
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
}
<!-- spring-special.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" value="2 < 3"/>
</bean>
</beans>
//test
@Test
public void testSpecial(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-special.xml");
Math mathBean = applicationContext.getBean("mathBean", Math.class);
System.out.println(mathBean);
}
CDATE
<?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标签-->
<value><![CDATA[2 < 3]]></value>
</property>
</bean>
</beans>
注意:
使用CDATE时,不能使用value属性,只能使用value
标签
构造注入
核心原理:通过调用构造方法来给属性赋值。
//OrderDao.java
package com.powernode.spring6.dao;
public class OrderDao {
public void deleteById(){
System.out.println("正在删除订单。。。");
}
}
//OrderService.java
package com.powernode.spring6.service;
import com.powernode.spring6.dao.OrderDao;
public class OrderService {
private OrderDao orderDao;
// 通过反射机制调用构造方法给属性赋值
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void delete(){
orderDao.deleteById();
}
}
<!-- spring.xml -->
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<!--index="0"表示构造方法的第一个参数,将orderDaoBean对象传递给构造方法的第一个参数。-->
<constructor-arg index="0" ref="orderDaoBean"/>
</bean>
//test
@Test
public void testConstructorDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
orderServiceBean.delete();
}
如果构造参数有两个,那就需要将两个参数都加入到bean中,利用constructor-arg
子标签将构造的参数补齐
- index属性名:补齐参数对应位置,用0,1方式对应参数的位置
- name属性名:补齐参数对应位置,用对应上构造参数值的名字赋值进去
- 空:也可以不指定参数的位置,利用spring强大的功能让他自己对应进去,位置可以不对应
- ref属性名:指向要映射成的类
p命名空间注入
目的:简化配置。
使用p命名空间注入的前提条件包括两个:
- 第一:在XML头部信息中添加p命名空间的配置信息:
xmlns:p="http://www.springframework.org/schema/p"
- 第二:p命名空间注入是基于
setter
方法的,所以需要对应的属性提供setter
方法。
//Customer.java
package com.powernode.spring6.beans;
public class Customer {
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 "Customer{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
<!-- spring-p.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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="customerBean" class="com.powernode.spring6.beans.Customer" p:name="zhangsan" p:age="20"/>
</beans>
//test
@Test
public void testP(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");
Customer customerBean = applicationContext.getBean("customerBean", Customer.class);
System.out.println(customerBean);
}
p命名空间实际上是对set注入的简化
c命名空间注入
c命名空间是简化构造方法注入的
使用c命名空间的两个前提条件:
- 第一:需要在xml配置信息文件头部添加信息:
xmlns:c="http://www.springframework.org/schema/c"
- 第二:需要提供构造方法
//MyTime.java
package com.powernode.spring6.beans;
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 -->
<?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">
<!--<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>
//test
@Test
public void testC(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
MyTime myTimeBean = applicationContext.getBean("myTimeBean", MyTime.class);
System.out.println(myTimeBean);
}
不管是p命名空间还是c命名空间,注入的时候都可以注入简单类型以及非简单类型
util命名空间
使用util命名空间可以让配置复用
使用util命名空间的前提是:在spring配置头文件添加配置信息
//MyDataSource1.java
package com.powernode.spring6.beans;
import java.util.Properties;
public class MyDataSource1 {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource1{" +
"properties=" + properties +
'}';
}
}
//MyDataSource2.java
package com.powernode.spring6.beans;
import java.util.Properties;
public class MyDataSource2 {
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "MyDataSource2{" +
"properties=" + properties +
'}';
}
}
<!-- spring-util.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"
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="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>
<bean id="dataSource1" class="com.powernode.spring6.beans.MyDataSource1">
<property name="properties" ref="prop"/>
</bean>
<bean id="dataSource2" class="com.powernode.spring6.beans.MyDataSource2">
<property name="properties" ref="prop"/>
</bean>
</beans>
//test
@Test
public void testUtil(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
MyDataSource1 dataSource1 = applicationContext.getBean("dataSource1", MyDataSource1.class);
System.out.println(dataSource1);
MyDataSource2 dataSource2 = applicationContext.getBean("dataSource2", MyDataSource2.class);
System.out.println(dataSource2);
}
基于XML的自动装配
Spring还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配。
根据名称自动装配
//UserDao.java
package com.powernode.spring6.dao;
public class UserDao {
public void insert(){
System.out.println("正在保存用户数据。");
}
}
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
public class UserService {
private UserDao aaa;
// 这个set方法非常关键
public void setAaa(UserDao aaa) {
this.aaa = aaa;
}
public void save(){
aaa.insert();
}
}
<!-- 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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>
</beans>
这个配置起到关键作用:
- UserService Bean中需要添加autowire=“byName”,表示通过名称进行装配。
- UserService类中有一个UserDao属性,而UserDao属性的名字是aaa,对应的set方法是setAaa(),正好和UserDao Bean的id1是一样的。这就是根据名称自动装配。
//test
@Test
public void testAutowireByName(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.save();
}
如果根据名称装配(byName),底层会调用set方法进行注入。
例如:setAge()对应的名字是age,setPassword()对应的名字是password,setEmail()对应的名字是email。
根据类型自动装配
//AccountDao.java
package com.powernode.spring6.dao;
public class AccountDao {
public void insert(){
System.out.println("正在保存账户信息");
}
}
//AccountService.java
package com.powernode.spring6.service;
import com.powernode.spring6.dao.AccountDao;
/**
* @author 动力节点
* @version 1.0
* @className AccountService
* @since 1.0
**/
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void save(){
accountDao.insert();
}
}
<!-- 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">
<!--byType表示根据类型自动装配-->
<bean id="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>
<bean class="com.powernode.spring6.dao.AccountDao"/>
</beans>
@Test
public void testAutowireByType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
accountService.save();
}
无论是byName还是byType,在装配的时候都是基于set方法的。所以set方法是必须要提供的。提供构造方法是不行的。
当byType进行自动装配的时候,配置文件中,某种类型的Bean必须是唯一的,不能出现多个。
Spring引入外部属性配置文件
mybatis可以将配置文件的信息单独写到一个文件中,Spring也可以
- 第一步:写一个数据源类,提供相关属性
package com.powernode.spring6.beans;
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 MyDataSource implements DataSource {
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
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;
}
//......
}
- 第二步:在类路径下新创建jdbc.propertirs文件,并配置信息
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/数据库名
username=账号
password=密码
- 在spring配置文件中引入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文件
<?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="dataSource" class="com.powernode.spring6.beans.MyDataSource">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</bean>
</beans>
//test
@Test
public void testProperties(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class);
System.out.println(dataSource);
}
注意一个事情,就是properties文件中的username在注入的时候会先查询windows本地的,所以在调试的时候会发现username注入的值不是我们提供的值,而是本地的一个值
建议properties文件加个前缀jdbc啥的