通过知识点的讲解,帮助未入门的人快速掌握Spring的IOC和DI部分。
Spring 是一个开源的 Java 企业级应用开发框架,它的核心目标是简化企业级应用开发的复杂性,提供一站式的解决方案。

(1)核心层
(2)AOP层
(3)数据层
(4)Web层
(5)Test层
在Spring核心概念这部分内容中主要包含IoC/DI、IoC容器和Bean
概念:
IoC (Inversion of Control) 控制反转:对象的创建控制权由程序转移到外部,这种思想称为控制反转。
Bean:在Spring中提供了IoC容器,而用于充当外部,负责对象的创建、初始化等工作,被创建或被管理的对象在IoC容器中的统称为Bean。
DI(Dependency Injection)依赖注入:在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
目标:充分解耦
最终效果:使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有依赖关系
理解: 在没有学习IoC之前,我们写业务层示例如下

这导致了我们写新的数据层时,需要重新编译业务层,而我们通过外部提供对象,则不需要提供多个数据层,也不需要主要去new对象了。

在Spring中提供了IoC容器,存入了Service和Dao层的对象,并绑定依赖,当使用时,直接从IoC容器中获取,效果也是和上图一样的。
什么IOC/DI思想?
什么是IOC容器?
Spring创建了一个容器用来存放所创建的对象,这个容器就叫IOC容器
什么是Bean?
容器中所存放的一个个对象就叫Bean或Bean对象
思路分析
1.IoC容器管理什么? 答:主要管理项目中所使用到的类对象,比如(Service和Dao) 2.IoC容器如何将被管理的对象告知IoC容器? 答:使用配置文件 3.被管理的对象交给IoC容器,如何获取到IoC容器? 答:Spring框架提供相应的接口 4.IoC容器得到后,如何从容器中获取bean? 答:调用Spring框架提供对应接口中的方法 5.使用Spring导入哪些坐标? 答:用别人的东西,就需要在pom.xml添加对应的依赖
入门案例
1.导入Spring坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>2.定义Spring管理的类(接口)
public interface BookService {
public void save();
}public class BookServiceImpl implements BookService {
private BookDao bookDao = new BookDaoImpl();
public void save() {
bookDao.save();
}
}3.创建Spring配置文件,配置对应类作为Spring管理的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">
<!--1.导入spring的坐标spring-context,对应版本是5.2.10.RELEASE-->
<!--2.配置bean-->
<!--bean标签标示配置bean
id属性标示给bean起名字
class属性表示给bean定义类型-->
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>bean定义时id属性在同一个上下文中不能重复
4.初始化IoC容器(Spring核心容器/Spring容器),通过容器获取Bean
public class App2 {
public static void main(String[] args) {
//3.获取IoC容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//4.获取bean(根据bean配置id获取)
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}思路分析
1.要想实现依赖注入,必须要基于IoC管理Bean DI的入门案例要依赖于前面IoC的入门案例 2.Service中使用new形式创建的Dao对象是否保留? 答:需要删除掉,最终要使用IoC容器中的bean对象 3.Service中需要的Dao对象如何进入到Service中? 答:在Service中提供方法,让Spring的IoC容器可以通过该方法传入bean对象 4.Service与Dao间的关系如何描述? 答:使用配置文件
入门案例 1.删除使用new形式创建对象的代码
public class BookServiceImpl implements BookService {
//删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
//原代码
//private BookDao bookDao = new BookDaoImpl();
public void save() {
bookDao.save();
}
}2.提供依赖对象对应的setter方法
public class BookServiceImpl implements BookService {
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}3.配置service与dao之间的关系(xml)
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--配置service与dao的关系-->
<!--property标签表示配置当前bean的属性
name属性表示配置哪一个具体的属性
ref属性表示参照哪一个bean-->
<property name="bookDao" ref="bookDao"/>
</bean>bean的基础配置(前文使用过)
<bean id="" class=""/>其中,bean标签的功能、使用方式以及id和class属性的作用,如图所示

重点:bean标签的id和class属性的使用 注意:class 不能写成接口名如
BookDao,应填写实现类的类全名(完全限定类名),因为接口无法创建对象
问题:bean设置id时,id必须唯一,如果产生分歧如何解决?
答:可以通过bean的别名配置解决,别名的配置说明如下

步骤示例 1.配置别名 打开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">
<!--name:为bean指定别名,别名可以有多个,使用逗号,分号,空格进行分隔-->
<bean id="bookService" name="service service4 bookEbi" class="com.itheima.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<!--scope:为bean设置作用范围,可选值为单例singleton,非单例prototype-->
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope="prototype"/>
</beans>步骤2:根据名称容器中获取bean对象
public class AppForName {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//此处根据bean标签的id属性和name属性的任意一个值来获取bean对象
BookService bookService = (BookService) ctx.getBean("service4");
bookService.save();
}
}步骤3:运行程序即可
注意事项:
bean依赖注入的ref属性指定bean,必须在容器中存在,如果不存在,就会报错,报错为NoSuchBeanDefinitionException 同样,当看到这个错误,要意识到时id或者name无法获取
bean的作用范围是bean属性配置的重点内容,它的作用时可以改变IoC容器中对象为单例或非单例。

使用方法:
在Spring的配置文件中,修改<bean>的scope属性
<bean id="bookDao" name="dao" class="com.itheima.dao.impl.BookDaoImpl" scope=""/>将scope更改为singleton或prototype,前者代表单例,后者代表非单例
测试方法: 准备一个类,在main方法中验证
public class AppForScope {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao1 = (BookDao) ctx.getBean("bookDao");
BookDao bookDao2 = (BookDao) ctx.getBean("bookDao");
System.out.println(bookDao1);
System.out.println(bookDao2);
}
}当scope为singleton单例时
com.itheima.dao.impl.BookDaoImpl@4c1d9d4b
com.itheima.dao.impl.BookDaoImpl@4c1d9d4b当scope为prototype多例时
com.itheima.dao.impl.BookDaoImpl@4c1d9d4b
com.itheima.dao.impl.BookDaoImpl@7b227d8d思考知识点:
为什么bean默认为单例
bean为单例的意思是在Spring的IoC容器中只会有该类的一个对象 bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题
如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有的请求线程共用一个bean对象,会存在线程安全问题 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
哪些bean对象适合交给容器进行管理
表现层对象、业务层对象、数据层对象、工具对象
哪些bean对象不适合交给容器进行管理?
封装示例的域对象,因为会引发线程安全问题,所以不适合

bean本质上就是对象,创建bean使用构造方法完成。
通过提供可访问的构造方法,并配置到Spring容器即可
示例: 提供可访问的构造方法
public class BookDaoImpl implements BookDao {
/* public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
*/
public void save() {
System.out.println("book dao save ...");
}
}配置
<bean
id="bookDao"
class="com.itheima.dao.impl.BookDaoImpl"
/>测试类
public class AppForInstanceBook {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}注意:若无参构造方法不存在,会抛出异常BeanCreationException 默认提供无参构造;若仅保留有参构造,需显式声明无参构造或使用构造器注入
静态工厂实例化通常是用来兼容早期的老系统,所以了解为主。
示例: 创建一个工厂类OrderDaoFactory并提供一个静态方法
//静态工厂创建对象
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
return new OrderDaoImpl();
}
}配置
class:工厂类的类全名 factory-method:具体工厂类中创建对象的方法名
<bean
id="orderDao"
class="com.itheima.factory.OrderDaoFactory"
factory-method="getOrderDao"
/>测试类
public class AppForInstanceOrder {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}拓展内容:
工厂静态方法除了new对象,还可以做一些其他的业务操作,以打印语句为例:
public class OrderDaoFactory {
public static OrderDao getOrderDao(){
System.out.println("factory setup....");//模拟必要的业务操作
return new OrderDaoImpl();
}
}实例工厂常规配置较繁琐,了解即可
示例: 实例化工厂
public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}配置
第一行是创建实例化工厂对象 第二行是调用对象中的方法创建bean
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>测试类
public class AppForInstanceUser {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
}
}FactoryBean常被用在Spring整合框架中,所以需要理解掌握。
示例: 1.创建UserDaoFactoryBean的类,实现FactoryBean接口,重写方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所创建类的Class对象
public Class<?> getObjectType() {
return UserDao.class;
}
}2.配置
<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>3.测试类(不需要变)
public class AppForInstanceUser {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
}
}在FactoryBean接口中,有着三个方法
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}方法一:getObject(),重写后,在方法中进行对象创建并返回 方法二:getObjectType(),重写后,主要返回的是被创建类的Class对象 方法三:已经给了默认值,若重写此方法,可以控制是否单例,改为false,为非单例
bean是如何创建的
答:构造方法
Spring的IOC实例化对象的三种方式分别是
答:构造方法,静态方法,实例方法(含FactoryBean),主要掌握构造方法和FactoryBean
注:构造方法在类中默认会提供,但如果重写构造方法,默认的就会消失。因此,重写构造方法时,需要把默认的构造方法也重写。
bean最后一个相关知识是生命周期,对于这个知识点,主要围绕着bean生命周期控制来讲解
生命周期:从创建到消亡的完整过程 bean生命周期:bean从创建到消亡的完整过程 bean生命周期控制:从bean创建后到销毁前做一些事情
在这里有两种方法,第一种是自定义生命周期方法配置方式,第二种是实现接口的生命周期控制方式
有2个步骤,一步是提供生命周期控制方法,一步是配置生命周期控制方法 示例: 1.提供生命周期控制方法
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destroy(){
System.out.println("destroy...");
}
}2.配置生命周期控制方法
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>3.测试类
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();运行结果
init...
book dao save ...Spring提供了两个接口完成生命周期的控制,可以不用再配置init-method和destroy-method
实现方法:添加两个接口InitializingBean, DisposableBean并实现接口中的两个方法afterPropertiesSet和destroy即可
示例: 更改实现类,添加接口和方法
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}运行结果
init...
set .....
service init
book dao save ...
service destroy拓:afterPropertiesSet,直译
属性设置之后,在运行时,这个方法会在类种属性设置之后执行 拓:afterPropertiesSet,直译属性设置之后,在运行时,这个方法会在类中属性设置之后执行
在上述过程中,我们发现destroy方法并没有执行,这里涉及到如下知识点
Spring的IoC容器是运行在JVM中 获取main方法后,JVM启动,Spring加载配置文件生成IoC容器,获取bean对象并调方法执行 main方法执行完,JVM退出,但bean还没有销毁,无法执行destroy方法
那如何解决这个问题?我们可以通过close关闭容器或注册钩子关闭容器
由于ApplicationContext种没有close方法,因此我们需要把测试类中的ApplicationContext更换成ClassPathXmlApplicationContext即可
close()最早在ConfigurableApplicationContext中,是ApplicationContext的子类
ClassPathXmlApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");随后我们便可以调用ctx.close()方法,主动关闭容器了。
测试类示例
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//关闭容器
ctx.close();
}运行结果
init...
set .....
service init
book dao save ...
service destroy
destroy...原理:在容器未关闭之前,提前设置回调函数,让JVM在退出之前回调函数来关闭容器
示例: 调用ctx的registerShutdownHook()方法
ctx.registerShutdownHook();测试类:
public static void main( String[] args ) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
//注册关闭钩子函数,在虚拟机退出之前回调此函数,关闭容器
ctx.registerShutdownHook();
}
}运行结果
init...
set .....
service init
book dao save ...
service destroy
destroy...关于Spring种对bean生命周期提供了几种方式?
答:两种方式
init-method和destroy-method属性InitializingBean与DisposableBean接口,这种方式了解下即可。对于bean的生命周期控制在bean的整个生命周期中所处的位置
答:
关闭容器的两种方式
答:ConfigurableApplicationContext是ApplicationContext的子类
在了解相关知识点之前,有两个问题需要思考
向一个类中传递数据的方式有几种?
答:普通方法(set方法)和构造方法
依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,但bean运行如果需要数字或字符串呢?
可以将数字或字符串分成引用类型和简单类型(基本数据结构和字符串)区分
Spring基于以上知识点,提供了两种注入方式,一种是setter注入,一种是构造器注入,他们又分为了注入简单类型和引用类型
引用类型在入门案例已经使用过,不过多讲解 示例 在bean中定义引用类型属性并提供可访问的set方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
//setter注入需要提供要注入对象的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}配置中使用property标签和ref属性注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
</bean>输出结果
book service save ...
book dao save ...
user dao save ...在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
//setter注入需要提供要注入对象的set方法
public void setConnectionNum(int connectionNum) {
this.connectionNum = connectionNum;
}
//setter注入需要提供要注入对象的set方法
public void setDatabaseName(String databaseName) {
this.databaseName = databaseName;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}配置中使用property标签value属性注入简单类型数据
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>输出结果
book service save ...
book dao save ...mysql,100
user dao save ...在实现类中主要就是从set方法改为构造方法去传参,主要体现在配置不同 示例: 在Bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!-- 根据构造方法参数名称注入-->
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>这里的ref指向的是参数,如果改成BookServiceImpl(BookDao bookDao1, UserDao userDao1),则配置中name指向userDao1和bookDao1
运行结果
book service save ...
book dao save ...
user dao save ...在实体类添加构造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}配置
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="666"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>输出结果
book service save ...
book dao save ...mysql,666
user dao save ...当构造函数中方法的参数名变化后,配置文件中的name属性也要跟着变,这两块存在紧耦合,如何解决。

方式一:删除name属性,添加type属性,按照类型注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg type="int" value="10"/>
<constructor-arg type="java.lang.String" value="mysql"/>
</bean>方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg index="1" value="100"/>
<constructor-arg index="0" value="mysql"/>
</bean>介绍完两种参数的注入方式,具体我们该如何选择呢?
在配置文件的编写配置上较麻烦,我们可以通过自动装配去解决这个问题
什么是依赖自动装配?
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
基于自动装配方式,我们可以分为按类型(常用)、按名称、按构造方法、不启用自动装配四种
使用方法:主要是通过改动配置文件 示例: Bean以之前的setter注入为例
public class BookServiceImpl implements BookService{
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
}在配置文件中更改,去除<property>,在<bean>中添加autowire属性
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>autowire属性可以按照类型(byType),可以按照名称(byName) 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
这里比较简单,直接放示例 示例: 实现类
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void save() {
System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List" + list);
System.out.println("遍历Set" + set);
System.out.println("遍历Map" + map);
System.out.println("遍历Properties" + properties);
}
//setter....方法省略,自己使用工具生成
}配置中添加对应实现类和类型数据
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>注入List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>注入Set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>注入Map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>注入Properties类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>测试类
public static void main( String[] args ) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}运行结果
book dao save ...
遍历数组:[100, 200, 300]
遍历List[itcast, itheima, boxuegu, chuanzhihui]
遍历Set[itcast, itheima, boxuegu]
遍历Map{country=china, province=henan, city=kaifeng}
遍历Properties{country=china, province=henan, city=kaifeng}property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写
<array>、<list>、<set>、<map>、<props>标签 List的底层也是通过数组实现的,所以<list>和<array>标签是可以混用 集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少
这篇文章到此就结束了,如果解决了你的问题,欢迎点赞+关注+评论,支持一下