【Spring】Spring 对 Ioc 的实现

一、Ioc 控制反转

  • 控制反转是一种思想

  • 控制反转是为了降低程序耦合度,提高程序扩展力,达到 OCP 原则,达到 DIP 原则

  • 控制反转,反转的是什么?

    • 将对象的创建权利交出去,交给第三方容器负责

    • 将对象和对象之间关系的维护权交出去,交给第三方容器负责

  • 控制反转这种思想如何实现呢?

    • DI(Dependency Injection):依赖注入

 

二、依赖注入 

依赖注入实现了控制反转的思想

Spring通过依赖注入的方式来完成Bean管理的

Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)

依赖注入:

  • 依赖指的是对象和对象之间的关联关系

  • 注入指的是一种数据传递行为,通过注入行为来让对象和对象产生关系

依赖注入常见的实现方式包括两种:

  • 第一种:set 注入

  • 第二种:构造注入

 

三、set 注入 

set 注入,基于 set 方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值。这种方式要求属性必须对外提供 set 方法  

<?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>org.qiu</groupId><artifactId>spring-003-dependency-injection</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.23</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></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></project>
package org.qiu.spring.dao;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.dao* @date 2022-10-31-17:46* @since 1.0*/
public class UserDao {private static final Logger logger = LoggerFactory.getLogger(UserDao.class);public void insert(){logger.info("正在保存用户数据......");}
}
package org.qiu.spring.service;import org.qiu.spring.dao.UserDao;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.service* @date 2022-10-31-17:53* @since 1.0*/
public class UserService {private UserDao userDao;// 使用set方式注入,必须提供set方法。// 反射机制要调用这个方法给属性赋值的。public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void save(){userDao.insert();}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userDaoBean" class="org.qiu.spring.dao.UserDao"></bean><bean id="userServiceBean" class="org.qiu.spring.service.UserService"><!--想让 Spring 调用对应的 set 方法,需要配置 property 标签name:set方法的方法名,去掉 set,剩下的单词首字母变小写ref: references,“引用”。指定要注入的 bean 的 id一般情况下 name 位置写属性名就行了--><property name="userDao" ref="userDaoBean"></property></bean></beans>
package org.qiu.spring.test;import org.junit.Test;
import org.qiu.spring.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.test* @date 2022-10-31-17:55* @since 1.0*/
public class DITest {@Testpublic 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 最简单的一种装配方式。装配指的是:创建系统组件之间关联的动作)

可以把 set 方法注释掉,再测试一下

通过测试得知,底层实际上调用了 setUserDao() 方法。所以需要确保这个方法的存在

另外,对于 property 标签来说,ref 属性也可以采用标签的方式,但使用 ref 属性是多数的:

<bean id="userServiceBean" class="com.powernode.spring6.service.UserService"><property name="userDao"><ref bean="userDaoBean"/></property>
</bean>

总结:set 注入的核心实现原理:通过反射机制调用 set 方法来给属性赋值,让两个对象之间产生关系  

 

四、构造注入

核心原理:通过调用构造方法来给属性赋值  

package org.qiu.spring.service;import org.qiu.spring.dao.UserDao;
import org.qiu.spring.dao.VipDao;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.service* @date 2022-11-01-20:25* @since 1.0*/
public class CustomerService {private UserDao userDao;private VipDao vipDao;public CustomerService(UserDao userDao, VipDao vipDao) {this.userDao = userDao;this.vipDao = vipDao;}public void save(){userDao.insert();vipDao.insert();}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userDaoBean" class="org.qiu.spring.dao.UserDao"/><bean id="vipDaoBean" class="org.qiu.spring.dao.VipDao"/><bean id="csBean" class="org.qiu.spring.service.CustomerService"><!--构造注入public CustomerService(UserDao userDao, VipDao vipDao) {this.userDao = userDao;this.vipDao = vipDao;}指定构造方法的第一个参数,下标是 0--><constructor-arg index="0" ref="userDaoBean"/><constructor-arg index="1" ref="vipDaoBean"/></bean><!-- 也可以使用 name 注入,若不指定下标,也不指定参数名,Spring自动根据类型匹配注入 --><bean id="csBean2" class="org.qiu.spring.service.CustomerService"><constructor-arg name="userDao" ref="userDaoBean"/><constructor-arg name="vipDao" ref="vipDaoBean"/></bean></beans>
@Test
public void testConstructDI(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");CustomerService service = applicationContext.getBean("csBean",CustomerService.class);service.save();
}

运行结果:  

 

五、set 注入专题 

  • 注入外部 Bean

这种方式比较常用

<!-- 声明/定义 bean -->
<bean id="orderDaoBean" class="org.qiu.spring.dao.OrderDao"/><bean id="orderServiceBean" class="org.qiu.spring.service.OrderService"><!-- 使用 ref 属性来引入,就是注入外部 bean --><property name="orderDao" ref="orderDaoBean"/>
</bean>
  • 注入内部 Bean

这种方式使用较少,了解即可。

<bean id="orderServiceBean2" class="org.qiu.spring.service.OrderService"><property name="orderDao"><!-- 内部 bean --><bean class="org.qiu.spring.dao.OrderDao"/></property>
</bean>
  • 注入简单类型

package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-07:14* @since 1.0*/
public class User {private String username;private String password;private int age;@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", password='" + password + '\'' +", age=" + age +'}';}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}public void setAge(int age) {this.age = age;}
}
<!-- 注入简单类型 -->
<bean id="userBean" class="org.qiu.spring.bean.User"><property name="username" value="张三"/><property name="password" value="123"/><property name="age" value="20"/>
</bean>
 @Test
public void testSimpleTypeSet(){ApplicationContext application = new ClassPathXmlApplicationContext("set-di.xml");User userBean = application.getBean("userBean", User.class);System.out.println(userBean);
}

运行结果:  

 

分析简单类型包括哪些?

Spring 源码:BeanUtils 类

public class BeanUtils{//......./*** Check if the given type represents a "simple" property: a simple value* type or an array of simple value types.* <p>See {@link #isSimpleValueType(Class)} for the definition of <em>simple* value type</em>.* <p>Used to determine properties to check for a "simple" dependency-check.* @param type the type to check* @return whether the given type represents a "simple" property* @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE* @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies* @see #isSimpleValueType(Class)*/public static boolean isSimpleProperty(Class<?> type) {Assert.notNull(type, "'type' must not be null");return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));}/*** Check if the given type represents a "simple" value type: a primitive or* primitive wrapper, an enum, a String or other CharSequence, a Number, a* Date, a Temporal, a URI, a URL, a Locale, or a Class.* <p>{@code Void} and {@code void} are not considered simple value types.* @param type the type to check* @return whether the given type represents a "simple" value type* @see #isSimpleProperty(Class)*/public static boolean isSimpleValueType(Class<?> type) {return (Void.class != type && void.class != 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));}//........
}
  • 简单类型:

    • 基本数据类型

    • 基本数据类型对应的包装类

    • String 或 其他的 CharSequence 子类

    • Number 子类

    • Date 子类

    • URI

    • URL

    • Temporal 子类(Java8 新特性,有关于时间时区的类)

    • Locale

    • Class

    • 另外还包括以上简单值类型对应的数组类型  

package org.qiu.spring.bean;import java.net.URI;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.Locale;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-07:37* @since 1.0*/
public class SimpleValueType {public static void main(String[] args) {System.out.println(new Date());}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
}
<?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="simpleValueType" class="org.qiu.spring.bean.SimpleValueType"><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()方法执行的结果。--><!--如果想使用其他格式的日期字符串,就需要进行特殊处理了--><!--一般不会把 Date 当作简单类型,而是使用 ref 给 Date 类型属性赋值--><property name="date" value="Sun Nov 06 07:50:46 CST 2022"/><property name="season" value="WINTER"/><property name="uri" value="/save.do"/><!--spring6之后,会自动检查url是否有效,如果无效会报错。--><property name="url" value="http://www.baidu.com"/><!--java.util.Locale 主要在软件的本地化时使用。它本身没有什么功能,更多的是作为一个参数辅助其他方法完成输出的本地化。--><property name="locale" value="CHINESE"/><property name="clazz" value="java.lang.String"/></bean></beans>
@Test
public void testAllSimpleType(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-all-simple-type.xml");SimpleValueType simpleValueType = applicationContext.getBean("simpleValueType", SimpleValueType.class);System.out.println(simpleValueType);
}

运行结果:  

需要注意的是:

  • 如果把 Date 当做简单类型的话,日期字符串格式不能随便写。格式必须符合 Date 的 toString() 方法格式。显然这就比较鸡肋了。如果我们提供一个这样的日期字符串:2010-10-11,在这里是无法赋值给 Date 类型的属性的。

  • Spring6 之后,当注入的是 URL,那么这个 url 字符串是会进行有效性检测的。如果是一个存在的 url,那就没问题。如果不存在则报错。  

  • 级联属性赋值(了解)

package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-08:58* @since 1.0*/
public class Clazz {private String name;public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Clazz{" +"name='" + name + '\'' +'}';}
}
package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-09:01* @since 1.0*/
public class Student {private String name;public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}
<bean id="studentBean" class="org.qiu.spring.bean.Student"><property name="name" value="张三"/>
</bean><bean id="clazzBean" class="org.qiu.spring.bean.Clazz"><property name="name" value="高三一班"/>
</bean>
@Test
public void testCascade(){ApplicationContext application = new ClassPathXmlApplicationContext("cascade.xml");Student studentBean = application.getBean("studentBean", Student.class);System.out.println(studentBean);Clazz clazzBean = application.getBean("clazzBean", Clazz.class);System.out.println(clazzBean);
}

运行结果:  

package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-09:01* @since 1.0*/
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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", clazz=" + clazz +'}';}
}
<bean id="studentBean" class="org.qiu.spring.bean.Student"><property name="name" value="张三"/><property name="clazz" ref="clazzBean"/>
</bean>
@Test
public void testCascade(){ApplicationContext application = new ClassPathXmlApplicationContext("cascade.xml");Student studentBean = application.getBean("studentBean", Student.class);System.out.println(studentBean);Clazz clazzBean = application.getBean("clazzBean", Clazz.class);System.out.println(clazzBean);
}

运行结果:  

 

<!--级联属性赋值顺序不能颠倒使用级联属性赋值,clazz 属性需要提供对应的 getter 方法
-->
<bean id="studentBean" class="org.qiu.spring.bean.Student"><property name="name" value="张三"/><property name="clazz" ref="clazzBean"/><property name="clazz.name" value="高三二班"/>
</bean><bean id="clazzBean" class="org.qiu.spring.bean.Clazz"/>
public Clazz getClazz() {return clazz;
}

运行结果:  

 

  • 注入数组

数组中的元素是简单类型

package org.qiu.spring.bean;import java.util.Arrays;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-22:16* @since 1.0*/
public class QianDaYe {private String[] aiHaos;public void setAiHaos(String[] aiHaos) {this.aiHaos = aiHaos;}@Overridepublic String toString() {return "QianDaYe{" +"aiHaos=" + Arrays.toString(aiHaos) +'}';}
}
<bean id="yuQian" class="org.qiu.spring.bean.QianDaYe"><!-- 数组属性的元素是简单类型 --><property name="aiHaos"><array><value>抽烟</value><value>喝酒</value><value>烫头</value></array></property>
</bean>
@Test
public void testArray(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array.xml");QianDaYe yuQian = applicationContext.getBean("yuQian", QianDaYe.class);System.out.println(yuQian);
}

运行结果:

 

数组中元素不是简单类型  

package org.qiu.spring.bean;import java.util.Arrays;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-22:16* @since 1.0*/
public class QianDaYe {private String[] aiHaos;private Woman[] women;public void setAiHaos(String[] aiHaos) {this.aiHaos = aiHaos;}public void setWomen(Woman[] women) {this.women = women;}@Overridepublic String toString() {return "QianDaYe{" +"aiHaos=" + Arrays.toString(aiHaos) +", women=" + Arrays.toString(women) +'}';}
}
package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-06-22:24* @since 1.0*/
public class Woman {private String name;public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Woman{" +"name='" + name + '\'' +'}';}
}
<!-- 数组属性的元素不是简单类型 -->
<property name="women"><array><ref bean="w1" /><ref bean="w2" /><ref bean="w3" /><ref bean="w4" /></array>
</property>

运行结果:  

 

  • 注入 List 集合、Set 集合

注入集合,集合中存放简单类型元素就用 <value> 标签,存放的是非简单类型元素就用 <ref> 标签

package org.qiu.spring.bean;import java.util.List;
import java.util.Set;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-07:12* @since 1.0*/
public class Person {// 注入 List 集合private List<String> names;// 注入 Set 集合private Set<String> addrs;@Overridepublic 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;}
}
<bean id="personBean" class="org.qiu.spring.bean.Person"><property name="names"><!-- List 集合有序可重复--><list><value>张三</value><value>张三</value><value>张三</value><value>李四</value><value>李四</value><value>王五</value><value>王五</value></list></property><property name="addrs"><!-- Set 集合无序不可重复--><set><value>北京</value><value>广东</value><value>上海</value><value>上海</value><value>上海</value></set></property>
</bean>
 @Test
public void testCollection(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");Person person = applicationContext.getBean("personBean", Person.class);System.out.println(person);
}

运行结果:  

 

  • 注入 Map 集合

// 注入 Map 集合
private Map<Integer,String> phones;@Override
public String toString() {return "Person{" +"names=" + names +", addrs=" + addrs +", phones=" + phones +'}';
}
<property name="phones"><map><!-- key/value 都是简单类型,使用 key/value --><entry key="1" value="110"/><entry key="3" value="119"/><entry key="5" value="120"/><!-- key/value 不是简单类型,使用 key-ref/value-ref --><!-- <entry key-ref="" value-ref="" /> --></map>
</property>

运行结果:  

 

  • 注入 Properties 属性类对象

/*** 注入属性类对象* Properties 本质上也是一个 Map 集合* Properties 的父类是 HashTable,HashTable 实现了 Map 接口* 虽然也是一个 Map 集合,但是注入方式相似,但不同* Properties 的 key/value 只能是 String 类型*/
private Properties properties;public void setProperties(Properties properties) {this.properties = properties;
}@Override
public String toString() {return "Person{" +"names=" + names +", addrs=" + addrs +", phones=" + phones +", properties=" + properties +'}';
}
<property name="properties"><props><prop key="driver">com.nmysql.cj.jdbc.Driver</prop><prop key="url">jdbc:mysql://localhost:3306/spring</prop><prop key="username">root</prop><prop key="password">123</prop></props>
</property>

运行结果:  

 

  • 注入 null 和 空字符串

package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-07:42* @since 1.0*/
public class Cat {private String name;private int age;@Overridepublic String toString() {return "Cat{" +"name='" + name + '\'' +", age=" + age +'}';}public void setName(String name) {this.name = name;}public void setAge(int age) {this.age = age;}
}
<bean id="catBean" class="org.qiu.spring.bean.Cat"><!-- <property name="name" value=""></property> --><!-- 手动注入 null --><property name="name"><null/></property><property name="age" value="3"></property>
</bean>
@Test
public void testNull(){ApplicationContext application = new ClassPathXmlApplicationContext("set-di.xml");Cat cat = application.getBean("catBean", Cat.class);System.out.println(cat);
}

运行结果:  

不给属性值注入,默认就是 null,也可以手动注入  

<bean id="catBean" class="org.qiu.spring.bean.Cat"><!-- 注入 空字符串 --><property name="name" value=""></property><!-- 方式二 --><!--<property name="name"><value/></property>--><property name="age" value="3"></property>
</bean>

运行结果:  

 

  • 注入特殊字符

XML中有5个特殊字符,分别是:<、>、'、"、&

以上5个特殊符号在XML中会被特殊对待,会被当做XML语法的一部分进行解析,如果这些特殊符号直接出现在注入的字符串当中,会报错。

解决方案包括两种:

  • 第一种:特殊符号使用转义字符代替。

  • 第二种:将含有特殊符号的字符串放到:<![CDATA[]]> 当中。因为放在 CDATA 区中的数据不会被 XML 文件解析器解析。

5个特殊字符对应的转义字符分别是:

特殊字符转义字符
>&gt;
<&lt;
'&apos;
"&quot;
&&amp;

 

package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-07:58* @since 1.0*/
public class Math {private String result;public void setResult(String result) {this.result = result;}@Overridepublic String toString() {return "Math{" +"result='" + result + '\'' +'}';}
}
<!-- 方式一 -->
<bean id="mathBean" class="org.qiu.spring.bean.Math"><property name="result" value="2 &lt; 3"/>
</bean>
@Test
public void testSpecial(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("set-di.xml");Math mathBean = applicationContext.getBean("mathBean", Math.class);System.out.println(mathBean);
}

运行结果:  

 

使用 CDATA 方式:  

<bean id="mathBean" class="com.powernode.spring6.beans.Math"><property name="result"><!--只能使用 value 标签--><value><![CDATA[2 < 3]]></value></property>
</bean>

注意:使用CDATA时,不能使用value属性,只能使用value标签。

运行结果:

 

六、p 命名空间注入

目的:简化配置

使用 p 命名空间注入的前提条件包括两个:

  • 第一:在 XML 头部信息中添加 p 命名空间的配置信息:xmlns:p="http://www.springframework.org/schema/p"

  • 第二:p 命名空间注入是基于 setter 方法的,所以需要对应的属性提供 setter 方法

package org.qiu.spring.bean;import java.util.Date;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-08:10* @since 1.0*/
public class Dog {private String name;private int age;private Date birth;@Overridepublic String toString() {return "Dog{" +"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;}
}
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--第一步:在 Spring 配置文件头部添加 p 命名空间xmlns:p="http://www.springframework.org/schema/p"第二步:使用p:属性名 = "属性值"(简单类型)p:属性名-ref = "属性值"(非简单类型)--><bean id="dogBean" class="org.qiu.spring.bean.Dog" p:name="小花" p:age="3" p:birth-ref="birthBean"/><!-- 获取当前系统时间 --><bean id="birthBean" class="java.util.Date"/></beans>
@Test
public void testP(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");Dog dog = applicationContext.getBean("dogBean", Dog.class);System.out.println(dog);
}

运行结果:  

 

七、C 命名空间注入 

c 命名空间是简化构造方法注入的

使用 c 命名空间的两个前提条件:

第一:需要在 xml 配置文件头部添加信息:xmlns:c="http://www.springframework.org/schema/c"

第二:需要提供构造方法

package org.qiu.spring.bean;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-21:34* @since 1.0*/
public class People {private String name;private int age;private boolean sex;@Overridepublic String toString() {return "People{" +"name='" + name + '\'' +", age=" + age +", sex=" + sex +'}';}public People(String name, int age, boolean sex) {this.name = name;this.age = age;this.sex = sex;}
}
<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--第一步:在 Spring 配置文件头部添加 xmlns:c="http://www.springframework.org/schema/c"第二步:使用可以使用属性名字或下标注入--><bean id="peopleBean" class="org.qiu.spring.bean.People" c:_0="张三" c:age="18" c:sex="true"></bean></beans>
@Test
public void testC(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");People people = applicationContext.getBean("peopleBean", People.class);System.out.println(people);
}

运行结果:  

 

八、util 命名空间 

util 命名空间让配置复用

使用util命名空间的前提是:在spring配置文件头部添加配置信息。如下:

package org.qiu.spring.bean;import java.util.Properties;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-21:34* @since 1.0*/
public class MyDataSource1 {private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic String toString() {return "MyDataSource1{" +"properties=" + properties +'}';}
}
package org.qiu.spring.bean;import java.util.Properties;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-07-21:34* @since 1.0*/
public class MyDataSource2 {private Properties properties;public void setProperties(Properties properties) {this.properties = properties;}@Overridepublic String toString() {return "MyDataSource2{" +"properties=" + properties +'}';}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:util="http://www.springframework.org/schema/util"xsi:schemaLocation="http://www.springframework.org/schema/beans 				http://www.springframework.org/schema/beans/spring-beans.xsdhttp://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="org.qiu.spring.bean.MyDataSource1"><property name="properties" ref="prop"/></bean><bean id="dataSource2" class="org.qiu.spring.bean.MyDataSource2"><property name="properties" ref="prop"/></bean>
</beans>
@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 还可以完成自动化的注入,自动化注入又被称为自动装配。它可以根据名字进行自动装配,也可以根据类型进行自动装配

  • 根据名称自动装配

package org.qiu.spring.dao;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.dao* @date 2022-11-05-20:40* @since 1.0*/
public class OrderDao {private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);public void generate(){logger.info("订单正在生成......");}
}
package org.qiu.spring.service;import org.qiu.spring.dao.OrderDao;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.service* @date 2022-11-05-20:42* @since 1.0*/
public class OrderService {private OrderDao orderDao;public void setOrderDao(OrderDao orderDao) {this.orderDao = orderDao;}/*** 生成订单的业务方法*/public void generate(){orderDao.generate();}
}
<!--id一般也叫做 bean 的名称根据名称自动装配,这个 id 需要是 setter 方法的名字,去掉 set,再将首字母改为小写
-->
<bean id="orderDao" class="org.qiu.spring.dao.OrderDao"/><!--根据名字进行自动装配自动装配也是基于 set 注入实现的
-->
<bean id="orderService" class="org.qiu.spring.service.OrderService" autowire="byName"></bean>
@Test
public void testAutowire(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");OrderService orderService = applicationContext.getBean("orderService", OrderService.class);orderService.generate();
}

运行结果:  

 

  • 根据类型自动装配

package org.qiu.spring.dao;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.dao* @date 2022-10-31-20:27* @since 1.0*/
public class VipDao {private static final Logger logger = LoggerFactory.getLogger(UserDao.class);public void insert(){logger.info("正在保存VIP信息......");}
}
package org.qiu.spring.dao;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.dao* @date 2022-10-31-17:46* @since 1.0*/
public class UserDao {private static final Logger logger = LoggerFactory.getLogger(UserDao.class);public void insert(){logger.info("正在保存用户数据......");}
}
package org.qiu.spring.service;import org.qiu.spring.dao.UserDao;
import org.qiu.spring.dao.VipDao;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.service* @date 2022-11-01-20:25* @since 1.0*/
public class CustomerService {private UserDao userDao;private VipDao vipDao;public void setUserDao(UserDao userDao) {this.userDao = userDao;}public void setVipDao(VipDao vipDao) {this.vipDao = vipDao;}/*public CustomerService(UserDao userDao, VipDao vipDao) {this.userDao = userDao;this.vipDao = vipDao;}*/public void save(){userDao.insert();vipDao.insert();}
}
<!--根据类型注入自动装配是基于 setter 方法的,必须提供 setter 方法根据类型进行自动装配的时候,在有效的配置文件当中,某种类型的实例只能有一个
-->
<bean class="org.qiu.spring.dao.VipDao"/>
<bean class="org.qiu.spring.dao.UserDao"/>
<bean id="cs" class="org.qiu.spring.service.CustomerService" autowire="byType"/>
@Test
public void testAutowireByType(){ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");CustomerService customerService = applicationContext.getBean("cs", CustomerService.class);customerService.save();
}

运行结果:  

 

十、Spring 引入外部属性配置文件

我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver url username password等信息。这些信息可以单独写到一个属性配置文件中,这样用户修改起来会更加的方便

第一步:写一个数据源类,提供相关属性:

package org.qiu.spring.bean;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;/*** @author 秋玄* @version 1.0* @email qiu_2022@aliyun.com* @project Spring* @package org.qiu.spring.bean* @date 2022-11-09-11:01* @since 1.0*/
public class MyDataSource implements DataSource {private String driver;private String url;private String username;private String password;@Overridepublic String toString() {return "MyDataSource{" +"driver='" + driver + '\'' +", url='" + url + '\'' +", username='" + username + '\'' +", password='" + password + '\'' +'}';}public String getDriver() {return driver;}public void setDriver(String driver) {this.driver = driver;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}// 省略实现 DataSource 接口中的方法
}

第二步:在类路径下新建 jdbc.properties 文件,并配置信息:  

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/spring
username=root
password=root

第三步:在 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.xsdhttp://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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:property-placeholder location="jdbc.properties"/><bean id="dataSource" class="org.qiu.spring.bean.MyDataSource"><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");MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class);System.out.println(dataSource);
}

运行结果:  

 

一  叶  知  秋,奥  妙  玄  心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/668436.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Angular BaseView抽离页面公用属性

前言 如果有一系列的页面布局很类似&#xff0c;为了节省时间&#xff0c;我们可以把这些类似的页面所通用的属性和方法抽离成一个BaseView&#xff0c;让其它页面继承该基础页面&#xff0c;同时将一些经常改变的属性和差异的属性写到配置文件里。例如树容器初始时是否展开、…

什么是前端工程化,请举例说明

前端工程化 前端工程化的定义为什么需要前端工程化前端工程化的核心概念 模块化开发&#xff1a;组件化开发&#xff1a;规范化开发&#xff1a;自动化开发&#xff1a;持续集成 前端工程化的主要工具前端工程化的应用总结&#xff1a; 前端工程化 前端工程化的定义 前端工程…

从资深用户角度谈三款出色数据可视化工具

作为一名数据可视化领域的老用户&#xff0c;我接触过众多数据可视化产品&#xff0c;其中不乏佼佼者。今天&#xff0c;我想为大家介绍三款在我心目中颇具特色的数据可视化产品&#xff0c;它们分别是山海鲸可视化、Tableau和Power BI。 首先&#xff0c;让我们来谈谈山海鲸可…

STM32单片机的基本原理与应用(六)

串口测试实验 基本原理 在串口实验中&#xff0c;是通过mini_USB线搭建终端与电脑端&#xff08;也可称终端&#xff0c;为做区分称电脑端&#xff09;的“桥梁”&#xff0c;电脑端的串口调试助手通过mini_USB线向终端发送信息&#xff0c;由CH340芯片将USB接口进行转换&…

机器学习中常用的性能度量—— ROC 和 AUC

什么是泛化能力&#xff1f; 通常我们用泛化能力来评判一个模型的好坏&#xff0c;通俗的说&#xff0c;泛化能力是指一个机器学期算法对新样本&#xff08;即模型没有见过的样本&#xff09;的举一反三的能力&#xff0c;也就是学以致用的能力。 举个例子&#xff0c;高三的…

vulhub中Apache APISIX Dashboard API权限绕过导致RCE(CVE-2021-45232)

Apache APISIX是一个动态、实时、高性能API网关&#xff0c;而Apache APISIX Dashboard是一个配套的前端面板。 Apache APISIX Dashboard 2.10.1版本前存在两个API/apisix/admin/migrate/export和/apisix/admin/migrate/import&#xff0c;他们没有经过droplet框架的权限验证&…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 2月5日,星期一

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年2月5日 星期一 农历腊月廿六 1、 证监会&#xff1a;依法严厉打击操纵市场、恶意做空、内幕交易等重大违法行为。 2、 夜间高铁开行&#xff01;多地火车站候车室开启通宵服务。 3、 气象台&#xff1a;5日晚至7日湘中以…

Prometheus部署监控报警

在容器环境中配置安装Prometheus部署企业微信容器报警Grafana展示 下载Prometheus &#xff08;监控Server端&#xff09; [rootPrometheus-Grafana prometheus]# mkdir /prometheus [rootPrometheus-Grafana prometheus]# docker run -d --name test -P prom/prometheus [ro…

数据与广告系列三十七:广告,商业化的高雅,中间商赚差价的无奈

作者黄崇远 『数据巢』 全文8872字 题图ssyer.com “ 商业化广告&#xff0c;看着其技术复杂又富有挑战性&#xff0c;业务覆盖行业的方方面面又似乎不可或缺&#xff0c;但究其本质&#xff0c;依然是中间商赚差价的生意而已&#xff0c;但细究其背后的深层原因&#xff0c;却…

深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之行存(一)

在当今的大数据时代&#xff0c;高效的数据检索和分析能力已成为许多应用程序的核心需求。Elasticsearch&#xff0c;作为一款强大的分布式搜索和分析引擎&#xff0c;正是为了满足这些需求而诞生的。它之所以能够在海量数据中实现毫秒级的搜索响应&#xff0c;以及灵活的数据分…

笔记本电脑的WIFI模块,突然不显示了,网络也连接不上

问题复现&#xff1a; 早上&#xff0c;在更新完笔记本电脑的系统之后&#xff0c;连网之后&#xff0c;网络突然直接断开&#xff0c;一查看&#xff0c;WiFi模块居然不见了&#xff0c;开机重启也是如此&#xff0c;这种情况常常出现在更新系统之后&#xff0c;WiFi模块驱动就…

RK3399平台开发系列讲解(内存篇)进程内存详解

🚀返回专栏总目录 文章目录 一、虚拟地址映射的物理内存1.1、物理内存1.2、虚拟内存1.2.1、用户态:低特权运行程序1.2.2、内核态:运行的程序需要访问操作系统内核数据二、PageCache三、指标查询命令沉淀、分享、成长,让自己和他人都能有所收获!😄 📢进程消耗的内存包…

docker proxy 【docker 代理】

第一种 创建代理配置文件 mkdir -p /etc/systemd/system/docker.service.d/ cat <<EOF > /etc/systemd/system/docker.service.d/http-proxy.conf Environment"HTTP_PROXYhttp://192.168.21.101:7890" Environment"HTTPS_PROXYhttp://192.168.21.1…

同城外卖跑腿app开发:重新定义城市生活

随着科技的发展和人们生活节奏的加快&#xff0c;同城外卖跑腿app应运而生&#xff0c;成为现代城市生活中的重要组成部分。本文将探讨同城外卖跑腿app开发的意义、市场需求、功能特点以及未来的发展趋势。 一、同城外卖跑腿app开发的意义 同城外卖跑腿app作为一种便捷的生活…

sqli.labs靶场(41-53关)

41、第四十一关 -1 union select 1,2,3-- -1 union select 1,database(),(select group_concat(table_name) from information_schema.tables where table_schemadatabase()) -- -1 union select 1,2,(select group_concat(column_name) from information_schema.columns wher…

0基础学习VR全景平台篇第141篇:如何制作卫星航拍全景

大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 很多人都看过或者拍摄过航拍全景&#xff0c;其效果相比于普通的地拍的确有着更加震撼的拍摄效果&#xff0c;但是受限于无人机高度&#xff0c;以及禁飞区等等限制&#xff0c;导致很多大场景无法展示完全&a…

Linux防火墙与iptables五表五链规则介绍

目录 一、防火墙基本认识 1. 安全技术 2. 防火墙分类 3. 防火墙工具介绍 二、iptables 1. 概述 2. 五表五链 3. 语法 3.1 基本语法 3.2 语法总结 4. 管理选项 5. 通用匹配 6. 控制类型 7. iptables应用 7.1 新增防火墙规则 7.2 查看规则表 7.3 黑白名单 7.4 …

C++ 调用lua 脚本

需求&#xff1a; 使用Qt/C 调用 lua 脚本 扩展原有功能。 步骤&#xff1a; 1&#xff0c;工程中引入 头文件&#xff0c;库文件。lua二进制下载地址&#xff08;Lua Binaries&#xff09; 2&#xff0c; 调用脚本内函数。 这里调用lua 脚本中的process函数&#xff0c;并…

canvas图片上设置镂空文字效果

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

C语言-3

定义指针 /*指针的概念:1.为了方便访问内存中的内容&#xff0c;给每一个内存单元&#xff0c;进行编号&#xff0c;那么我们称这个编号为地址&#xff0c;也就是指针。2.指针也是一种数据类型&#xff0c;指针变量有自己的内存&#xff0c;里面存储的是地址&#xff0c;也就是…