这就是Spring

这就是Spring

本文将通过对 Spring 基础知识的总结,加深对 Spring 的理解与应用。

一、Spring 概述

Spring 的优势

方便解耦,简化开发
AOP编程的支持
声明式事务的支持
方便程序的测试
方便集成各种优秀框架
降低 JavaEE API 的使用难度
Java 源码是经典学习范例

Spring 的体系结构

核心容器

Sping 的开发包

官网https://spring.io/
下载地址(http://repo.springsource.org/libs-release-local/org/springframework/spring)

二、IOC 的概念和作用

这部分内容可查看之前的文章IOC基本原理及相关实现
对IOC有单独的阐述

三、使用 spring 的 IOC 解决程序耦合

创建持久层接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package gavino.spring.dao;

/**
* @author Yan weigang
*/
public interface IDemoDao {
void print();
}

package gavino.spring.dao.impl;
import gavino.spring.dao.IDemoDao;

/**
* @author Yan weigang
*/
public class DemoDaoImpl implements IDemoDao {
public void print() {
System.out.println("使用 spring 的 IOC 解决程序耦合");
}
}

创建业务层接口和实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package gavino.spring.service;

/**
* @author Yan weigang
*/
public interface IDemoService {
void print();
}

package gavino.spring.service.impl;
import gavino.spring.dao.IDemoDao;
import gavino.spring.dao.impl.DemoDaoImpl;
import gavino.spring.service.IDemoService;

/**
* @author Yan weigang
*/
public class DemoServiceImpl implements IDemoService {
IDemoDao dao = new DemoDaoImpl();//将通过ioc解决这种依赖关系
public void print() {
dao.print();
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
package gavino.spring.ui;

import gavino.spring.service.impl.DemoServiceImpl;

/**
* @author Yan weigang
*/
public class View {
public static void main(String[] args) {
DemoServiceImpl service = new DemoServiceImpl();//将通过ioc解决这种依赖关系
service.print();
}
}

基于 XML 的配置

在pom.xml 中导入相关依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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>lut.gavino</groupId>
<artifactId>day01_spring04_ioc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.9.RELEASE</version>
</dependency>
</dependencies>


</project>

在resources下创建一个任意名称的 xml 文件(不能是中文,我一般命名为bean.xml)
给配置文件导入约束

在之前的 Spring 开发包中,解压以 dist 结尾的压缩包以后,进入该包中找到 docs 文件夹并进入,然后选择 spring-framework-reference 文件进入,在该目录下找到 core.html 文件用浏览器打开,找到如下约束:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>

<!-- more bean definitions go here -->

</beans>

在 bean.xml 文件中导入约束以后
在配置文件中配置 service 和 dao,让 Spring 管理资源

1
2
3
4
5
6
7
8
9
10
11
<?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="demoDao" class="gavino.spring.dao.impl.DemoDaoImpl"></bean>
<!-- 配置 service -->
<bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl"></bean>

</beans>

修改前面的测试,测试配置是否成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package gavino.spring.ui;

import gavino.spring.dao.IDemoDao;
import gavino.spring.service.IDemoService;
import gavino.spring.service.impl.DemoServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* @author Yan weigang
*/
public class View {
public static void main(String[] args) {
// IDemoService service = new DemoServiceImpl();//将通过ioc解决这种依赖关系
// service.print();
//1.使用 ApplicationContext 接口,就是在获取 spring 容器
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//2.根据 bean 的 id 获取对象
IDemoDao dao = (IDemoDao) context.getBean("demoDao");
IDemoDao demoDao = context.getBean("demoDao", IDemoDao.class);
System.out.println(dao);
System.out.println(demoDao);

IDemoService service = context.getBean("demoService",IDemoService.class);
System.out.println(service);
service.print();
}
}

------------------------------------------------------------------------------------------
运行结果:
gavino.spring.dao.impl.DemoDaoImpl@5e3a8624
gavino.spring.dao.impl.DemoDaoImpl@5e3a8624
gavino.spring.service.impl.DemoServiceImpl@5c3bd550
使用 spring 的 IOC 解决程序耦合

Spring 基于 XML 的 IOC 细节

BeanFactory 是 Spring 的顶层接口

BeanFactory 和 ApplicationContext 的区别

  1. BeanFactory 才是 Spring 容器中的顶层接口。
    ApplicationContext 是它的子接口。
  2. BeanFactory 和 ApplicationContext 的区别:
    • 创建对象的时间点不一样。
      • ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。
      • BeanFactory:什么使用什么时候创建对象。

ApplicationContext 接口的实现类

  1. ClassPathXmlApplicationContext:
    它是从类的根路径下加载配置文件 推荐使用这种
  2. FileSystemXmlApplicationContext:
    它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  3. AnnotationConfigApplicationContext:
    当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

IOC 中 bean 标签和管理对象细节

  • bean 标签

    • 作用

      用于配置对象让 spring 来创建的。
      默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

    • 属性

      id:给对象在容器中提供一个唯一标识。用于获取对象。
      class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
      scope:指定对象的作用范围。
      * singleton :默认值,单例的.
      * prototype :多例的.
      * request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
      * session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
      * global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么
      globalSession 相当于 session.
      init-method:指定类中的初始化方法名称。
      destroy-method:指定类中销毁方法名称。

  • bean 的作用范围和生命周期

    • 单例对象:scope=”singleton”

      一个应用只有一个对象的实例。它的作用范围就是整个引用。
      生命周期:
      对象出生:当应用加载,创建容器时,对象就被创建了。
      对象活着:只要容器在,对象一直活着。
      对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

    • 多例对象:scope=”prototype”

      每次访问对象时,都会重新创建对象实例。
      生命周期:
      对象出生:当使用对象时,创建新的对象实例。
      对象活着:只要对象在使用中,就一直活着。
      对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

  • 实例化 Bean 的三种方式

    • 第一种方式:使用默认无参构造函数

      1
      2
      3
      4
      <!--在默认情况下:
      它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。
      -->
      <bean id="accountService" class="gavino.spring.service.impl.AccountServiceImpl"/>
    • 第二种方式:spring 管理静态工厂-使用静态工厂的方法创建对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      /**
      * 模拟一个静态工厂,创建业务层实现类
      */
      public class StaticFactory {
      public static IAccountService createAccountService(){
      return new AccountServiceImpl();
      } }
      <!-- 此种方式是:
      使用 StaticFactory 类中的静态方法 createAccountService 创建对象,并存入 spring 容器
      id 属性:指定 bean 的 id,用于从容器中获取
      class 属性:指定静态工厂的全限定类名
      factory-method 属性:指定生产对象的静态方法
      --> <bean id="accountService"
      class="gavino.spring.factory.StaticFactory"
      factory-method="createAccountService"></bean>
    • 第三种方式:spring 管理实例工厂-使用实例工厂的方法创建对象

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      /**
      * 模拟一个实例工厂,创建业务层实现类
      * 此工厂创建对象,必须现有工厂实例对象,再调用方法
      */
      public class InstanceFactory {
      public IAccountService createAccountService(){
      return new AccountServiceImpl();
      } }
      <!-- 此种方式是:
      先把工厂的创建交给 spring 来管理。
      然后在使用工厂的 bean 来调用里面的方法
      factory-bean 属性:用于指定实例工厂 bean 的 id。
      factory-method 属性:用于指定实例工厂中创建对象的方法。
      --> <bean id="instancFactory" class="gavino.spring.factory.InstanceFactory"></bean> <bean id="accountService"
      factory-bean="instancFactory"
      factory-method="createAccountService"></bean>

spring 的依赖注入

依赖注入的概念

spring中的依赖注入
#依赖注入:
Dependency Injection
#IOC的作用:
降低程序间的耦合(依赖关系)
#依赖关系的管理:
以后都交给spring来维护
#在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
#依赖关系的维护:
就称之为依赖注入。
#依赖注入:
*能注入的数据:有三类
基本类型和String
其他bean类型(在配置文件中或者注解配置过的bean)
复杂类型/集合类型
*注入的方式:有三种
第一种:使用构造函数提供
第二种:使用set方法提供
第三种:使用注解提供

构造函数注入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   private String name;//String类型
private Integer id;//基本数据类型
private Date time;//其它Bean类型

//提供构造函数
public DemoServiceImpl(String name, Integer id, Date time) {
this.name = name;
this.id = id;
this.time = time;
}

//测试方法
@Test
public void print() {
System.out.println("----------测试构造函数注入 基本数据类型和String-----------");
System.out.println("id:"+id+" name:"+name+" ");
System.out.println("----------测试构造函数注入 其它Bean类型-----------");
System.out.println("time:"+time);
}

bean.xml文件中的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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">

<!-- 配置 dao -->
<bean id="demoDao" class="gavino.spring.dao.impl.DemoDaoImpl"></bean>
<!-- 配置 service -->
<bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl">
<constructor-arg name="id" value="101"></constructor-arg>
<constructor-arg name="name" value="依赖注入"></constructor-arg>
<constructor-arg name="time" ref="date"></constructor-arg>
</bean>
<bean id="date" class="java.util.Date"></bean>
</beans>

在测试类中测试是否注入成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author Yan weigang
*/
public class DITest {
@Test
public void testConstructor(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IDemoService service = context.getBean("demoService", IDemoService.class);
service.print();
}
}

########################## 测试输出如下 ####################################
----------测试构造函数注入 基本数据类型和String-----------
id:101 name:依赖注入
----------测试构造函数注入 其它Bean类型-----------
time:Mon Aug 26 19:46:18 CST 2019
set 方法注入

注掉构造方法,提供相应的set方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* @author Yan weigang
*/
public class DemoServiceImpl implements IDemoService {
private String name;
private Integer id;
private Date time;
// IDemoDao dao = new DemoDaoImpl();//将通过ioc解决这种依赖关系
// private IDemoDao dao;
// public DemoServiceImpl(String name, Integer id, Date time) {
// this.name = name;
// this.id = id;
// this.time = time;
// }
public void setName(String name) {
this.name = name;
}
public void setId(Integer id) {
this.id = id;
}
public void setTime(Date time) {
this.time = time;
}
@Test
public void print() {
System.out.println("----------测试构造函数注入 基本数据类型和String-----------");
System.out.println("id:"+id+" name:"+name+" ");
System.out.println("----------测试构造函数注入 其它Bean类型-----------");
System.out.println("time:"+time);
}

}

修改bean.xml配置文件

1
2
3
4
5
6
<bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl">
<property name="id" value="020"></property>
<property name="name" value="set方法注入"></property>
<property name="time" ref="date"></property>
</bean>
<bean id="date" class="java.util.Date"></bean>

测试类中测试是否注入成功

1
2
3
4
5
6
7
8
9
10
11
    @Test
public void testConstructor(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
IDemoService service = context.getBean("demoService", IDemoService.class);
service.print();
}
============================= 测试输出如下 ====================================
----------测试构造函数注入 基本数据类型和String-----------
id:20 name:set方法注入
----------测试构造函数注入 其它Bean类型-----------
time:Mon Aug 26 20:01:38 CST 2019
使用 p 名称空间注入数据(本质还是调用 set 方法)

使用 p 名称空间注入,使用 p:propertyName 来注入数据,它的本质仍然是调用类中的set 方法实现注入功能。
依然提供相应的set方法,代码如在set 方法中注入一样
修改bean.xml配置文件:

1
xmlns:p="http://www.springframework.org/schema/p"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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"
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">

<!-- p名称空间注入 -->
<bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl" p:id="303" p:name="p名称空间注入" p:time-ref="date"/>
<bean id="date" class="java.util.Date"></bean>
</beans>

测试类中测试是否注入成功

1
2
3
4
5
++++++++++++++++++++++++++ 测试输出如下 +++++++++++++++++++++++++++++++++++++
----------测试构造函数注入 基本数据类型和String-----------
id:303 name:p名称空间注入
----------测试构造函数注入 其它Bean类型-----------
time:Mon Aug 26 20:10:11 CST 2019
注入集合属性

注入集合属性,用的也是set方法注入的方式,只不过变量的数据类型都是集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package gavino.spring.service.impl;
import gavino.spring.dao.IDemoDao;
import gavino.spring.dao.impl.DemoDaoImpl;
import gavino.spring.service.IDemoService;
import org.junit.Test;

import java.util.*;

/**
* @author Yan weigang
*/
public class DemoServiceImpl implements IDemoService {
private String[] str;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties props;
//提供set方法
public void setStr(String[] str) {
this.str = str;
}
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProps(Properties props) {
this.props = props;
}
@Test
public void print() {
System.out.println(Arrays.toString(str));
System.out.println(list);
System.out.println(set);
System.out.println(map);
System.out.println(props);
}
}

配置bean.xml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?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"
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">
<bean id="demoService" class="gavino.spring.service.impl.DemoServiceImpl">
<property name="str">
<array>
<value>数组元素1</value>
<value>数组元素2</value>
<value>数组元素3</value>
</array>
</property>
<property name="list" >
<list>
<value>列表A</value>
<value>列表B</value>
<value>列表C</value>
</list>
</property>
<property name="set">
<set>
<value>set 1</value>
<value>set 2</value>
<value>set 3</value>
</set>
</property>
<property name="map">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>bbb</value>
</entry>
</map>
</property>
<property name="props">
<props>
<prop key="username">root</prop>
<prop key="password">root</prop>
</props>
</property>
</bean>
</beans>

测试类中测试是否注入成功

1
2
3
4
5
6
============================ 测试输出如下 ===================================
[数组元素1, 数组元素2, 数组元素3]
[列表A, 列表B, 列表C]
[set 1, set 2, set 3]
{testA=aaa, testB=bbb}
{password=root, username=root}

四、基于注解的 IOC配置

环境搭建

明确: 注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

(一)导入相关依赖
(二)使用@Component 注解配置管理的资源
(三)创建 spring 的 xml 配置文件并开启对注解的支持
注意:
基于注解整合时,导入约束时需要多导入一个 context 名称空间下的约束。

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 告知 spring 创建容器时要扫描的包 -->
<context:component-scan base-package="gavino.spring"></context:component-scan>
</beans>

常用注解

  1. 用于创建对象的( 相当于:<bean id=”” class=””> )
    • @Component
      • 作用:
        把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
      • 属性:
        value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
    • @Controller 一般用于表现层的注解。
    • @Service 一般用于业务层的注解。
    • @Repository 一般用于持久层的注解。

注意
@Controller、@Service、@Repository 三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。
它们只不过是提供了更加明确的语义化。如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值时可以不写。

  1. 用于注入数据的
    ( 相当于:<property name=”” ref=””> <property name=”” value=””> )
    • @Autowired
      • 作用:
        自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。
    • @Qualifier
      • 作用:
        在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。
      • 属性:
        value:指定 bean 的 id。
    • @Resource
      • 作用:
        直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
      • 属性:
        name:指定 bean 的 id。
    • @Value
      • 作用:
        注入基本数据类型和 String 类型数据的
      • 属性:
        value:用于指定值
  2. 用于改变作用范围
    • @Scope( 相当于:<bean id=”” class=”” scope=””> )
      • 作用:
        指定 bean 的作用范围。
      • 属性:
        value:指定范围的值。
        取值:singleton prototype request session globalsession
  3. 和生命周期相关的( 相当于:<bean id=”” class=”” init-method=”” destroy-method=”” /> )
    • @PostConstruct
      • 作用:
        用于指定初始化方法。
    • @PreDestroy
      • 作用:
        用于指定销毁方法。
  4. 其他新注解
    • @Configuration
    • @ComponentScan
    • @Bean
    • @PropertySource
    • @Import

注意

关于Spring 和 XML 的选择问题
注解的优势
配置简单
XML的优势
修改时不用修改源码

五、Spring 整合 Junit

说在前面

spring整合Junit时,Junit4 和 Junit5 的代码形式并不一样

1
2
3
4
5
6
//@RunWith(SpringJUnit4ClassRunner.class)//junit4环境下
@ExtendWith(SpringExtension.class)//junit5环境下
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
......
}

Spring整合junit的配置

1、导入spring整合junit的jar(坐标)
2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的@Runwith(junit4环境下),若导入的是Junit5的坐标,则应替换为@ExtendWith
3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
*@ContextConfiguration的说明:
locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
classes:指定注解类所在地位置

六、AOP 的相关概念

连接点即方法
被增强的方法才是切入点,切入点一定是连接点,但连接点不一定是切入点
切面
切入点表达式

七、Spring 中的 AOP

八、Spring 中的 JdbcTemplate

JdbcTemplate的作用

九、Spring 中的事务控制

十、总结

评论