Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入。下面的部分对在 Spring 框架中所有可用的模块给出了详细的介绍。
Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。

核心容器
核心容器由 spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring 表达式语言,Spring Expression Language)等模块组成,它们的细节如下:

数据访问/集成层包括 JDBC,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
JDBC 模块提供了 JDBC 抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
Web
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
Test模块
Test 模块:Spring 支持 Junit 和 TestNG 测试框架,而且还额外提供了一些基于 Spring 的测试功能,比如在测试 Web 框架时,模拟 Http 请求的功能。
其他
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
控制反转IOC(Inversion of Control) 是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值, 依赖的管理。
ApplicationContext 是 BeanFactory 的子接口,也被称为 Spring 上下文。
Application Context 是 spring 中较高级的容器。和 BeanFactory 类似,它可以加载配置文件中定义的 bean,将所有的 bean 集中在一起,当有请求的时候分配 bean。 另外,它增加了企业所需要的功能,比如,从属性文件中解析文本信息和将事件传递给所指定的监听器。这个容器在 org.springframework.context.ApplicationContext interface 接口中定义。
ApplicationContext 包含 BeanFactory 所有的功能,一般情况下,相对于 BeanFactory,ApplicationContext 会更加优秀。当然,BeanFactory 仍可以在轻量级应用中使用,比如移动设备或者基于 applet 的应用程序。
最常被使用的 ApplicationContext 接口实现:
当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。
Spring 框架支持以下五个作用域,分别为 singleton、prototype、request、session 和 global session,5种作用域说明如下所示,
注意,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。
| 作用域 | 描述 |
|---|---|
| singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
| prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
| request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
| session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
singleton 是默认的作用域,也就是说,当定义 Bean 时,如果没有指定作用域配置项,则 Bean 的作用域被默认为 singleton。
当将一个 bean 定义设置为 singleton 作用域的时候,Spring IoC 容器只会创建该 bean 定义的唯一实例。Singleton 是单例类型,就是在创建起容器时就同时自动创建了一个 bean 的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton 作用域是 Spring 中的缺省作用域。
验证:
在pom.xml中添加依赖
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.6.RELEASEversion>
dependency>
dependencies>
创建Student实体类
public class Student {
public Student() {
System.out.println("Student类的无参构造");
}
public void showScope() {
System.out.println("Student的作用域是singleton");
}
}
创建applicationContext.xml
<bean id="student1" class="com.xin.pojo.Student" scope="singleton">bean>
测试:
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
Student stu1=(Student) context.getBean("student1");
Student stu2=(Student) context.getBean("student1");
stu1.showScope();
stu2.showScope();
System.out.println(stu1==stu2);
}
结果:

当一个 bean 的作用域为 Prototype,表示一个 bean 定义对应多个对象实例。Prototype 作用域的 bean 会导致在每次对该 bean 请求(将其注入到另一个 bean 中,或者以程序的方式调用容器的 getBean() 方法)时都会创建一个新的 bean 实例。Prototype 是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
验证
Student实体类
public class Student {
public Student() {
System.out.println("Student类的无参构造");
}
public void showScope1() {
System.out.println("Student的作用域是prototype");
}
}
applicationContext.xml
<bean id="student2" class="com.xin.pojo.Student" scope="prototype">bean>
测试:
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
Student stu3=(Student) context.getBean("student2");
Student stu4=(Student) context.getBean("student2");
stu3.showScope1();
stu4.showScope1();
System.out.println(stu3==stu4);
}

Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系。
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。根据注入方式的不同,常用的有两类:set 注入、构造注入。
(1)简单类型
实体类:
public class School {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "School{" +
"name='" + name + '\'' +
", address=" + address +
'}';
}
applicationContext.xml
<bean id="school" class="com.xin.pojo.School">
<property name="name" value="北京大学">property>
<property name="address" value="北京">property>
bean>
测试:
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//取出对象
School school=(School) context.getBean("school");
System.out.println(school);
}
结果:

(2)引用类型
实体类
public class Student {
private String name;
private Integer age;
private School school;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", school=" + school +
'}';
}
applicationContext.xml
<bean id="school" class="com.xin.pojo.School">
<property name="name" value="北京大学">property>
<property name="address" value="北京">property>
bean>
<bean id="student" class="com.xin.pojo.Student">
<property name="name" value="李四">property>
<property name="age" value="18">property>
<property name="school" ref="school">property>
bean>
测试:
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//取出对象
Student stu=(Student) context.getBean("student");
System.out.println(stu);
}
结果:

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。在实体类中必须提供相应参数的构造方法。
< constructor-arg />标签中用于指定参数的属性有:
(1)使用构造方法的参数名称注入值
public School(String name, String address) {
this.name = name;
this.address = address;
}
public Student(String name, int age, School school) {
this.name = name;
this.age = age;
this.school = school;
}
<bean id="school" class="com.xin.pojo.School">
<constructor-arg name="name" value="清华大学">constructor-arg>
<constructor-arg name="address" value="北京">constructor-arg>
bean>
<bean id="student" class="com.xin.pojo.Student">
<constructor-arg name="name" value="李四">constructor-arg>
<constructor-arg name="age" value="18">constructor-arg>
<constructor-arg ref="school">constructor-arg>
bean>
(2)使用构造方法的参数索引下标注入值
<bean id="student" class="com.xin.pojo.Student">
<constructor-arg index="0" value="李四">constructor-arg>
<constructor-arg index="1" value="18">constructor-arg>
<constructor-arg index="2" ref="school">constructor-arg>
bean>
(3)不指定名称和下标索引的注入
<bean id="student" class="com.xin.pojo.Student">
<constructor-arg value="李四">constructor-arg>
<constructor-arg value="18">constructor-arg>
<constructor-arg ref="school">constructor-arg>
bean>
注意:此种方式的注入一定要按类中构造方法的参数的顺序来进行注入。
现在如果你想传递多个值,如 Java Collection 类型 List、Set、Map 和 Properties,应该怎么做呢。为了处理这种情况,Spring 提供了四种类型的集合的配置元素,如下所示:
| 元素 | 描述 |
|---|---|
| < list> | 它有助于连线,如注入一列值,允许重复。 |
| < set> | 它有助于连线一组值,但不能重复。 |
| < map> | 它可以用来注入名称-值对的集合,其中名称和值可以是任何类型。 |
| < props> | 它可以用来注入名称-值对的集合,其中名称和值都是字符串类型。 |
你可以使用或来连接任何 java.util.Collection 的实现或数组。
你会遇到两种情况(a)传递集合中直接的值(b)传递一个 bean 的引用作为集合的元素。
实体类:
public class JavaCollection {
List list;
Set set;
Map map;
Properties prop;
public List getList() {
System.out.println("List:"+list);
return list;
}
public void setList(List list) {
this.list = list;
}
public Set getSet() {
System.out.println("Set:"+set);
return set;
}
public void setSet(Set set) {
this.set = set;
}
public Map getMap() {
System.out.println("Map:"+map);
return map;
}
public void setMap(Map map) {
this.map = map;
}
public Properties getProp() {
System.out.println("Properties:"+prop);
return prop;
}
public void setProp(Properties prop) {
this.prop = prop;
}
applicationContext.xml
<bean id="javaCollection" class="com.xin.pojo.JavaCollection">
<property name="list">
<list>
<value>javavalue>
<value>jsvalue>
<value>mysqlvalue>
list>
property>
<property name="set">
<set>
<value>javavalue>
<value>jsvalue>
<value>mysqlvalue>
set>
property>
<property name="map">
<map>
<entry key="1" value="java">entry>
<entry key="2" value="js">entry>
<entry key="3" value="mysql">entry>
map>
property>
<property name="prop">
<props>
<prop key="one">javaprop>
<prop key="two">jsprop>
<prop key="three">mysqlprop>
props>
property>
bean>
测试:
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
//取出对象
JavaCollection jc=(JavaCollection) context.getBean("javaCollection");
jc.getList();
jc.getSet();
jc.getMap();
jc.getProp();
}
结果:

注入 Bean 引用
下面的 Bean 定义将帮助你理解如何注入 bean 的引用作为集合的元素。甚至你可以将引用和值混合在一起。
<bean id="..." class="...">
<property name="addressList">
<list>
<ref bean="address1"/>
<ref bean="address2"/>
<value>Pakistanvalue>
list>
property>
<property name="addressSet">
<set>
<ref bean="address1"/>
<ref bean="address2"/>
<value>Pakistanvalue>
set>
property>
<property name="addressMap">
<map>
<entry key="one" value="INDIA"/>
<entry key ="two" value-ref="address1"/>
<entry key ="three" value-ref="address2"/>
map>
property>
bean>
如果你需要传递一个空字符串作为值,
<bean id="..." class="exampleBean">
<property name="email" value=""/>
bean>
相当于 Java 代码:exampleBean.setEmail(“”)。
如果你需要传递一个 NULL 值
<bean id="..." class="exampleBean">
<property name="email"><null/>property>
bean>
相当于 Java 代码:exampleBean.setEmail(null)。
自动装配指的就是使用将 Spring 容器中的 bean 自动的和我们需要这个 bean 的类组装在一起。
对于引用类型属性的注入,也可不在配置文件中显示的注入。可以通过为标签设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属性)。根据自动注入判断标准的不同,可以分为两种:
Spring Beans自动装配 byName
(1)byName 方式自动注入
当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。


<bean id="school" class="com.xin.pojo.School">
<property name="name" value="北京大学">property>
<property name="address" value="北京">property>
bean>
<bean id="student" class="com.xin.pojo.Student" autowire="byName">
<property name="name" value="李四">property>
<property name="age" value="18">property>
bean>
Spring Beans自动装配 byType
(2)byType 方式自动注入
使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类, 要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了
<bean id="school" class="com.xin.pojo.School">
<property name="name" value="北京大学">property>
<property name="address" value="北京">property>
bean>
<bean id="student" class="com.xin.pojo.Student" autowire="byType">
<property name="name" value="李四">property>
<property name="age" value="18">property>
bean>
注解默认情况下在 Spring 容器中不打开。因此需要在Spring 配置文件中启用它。
(1)< context:annotation-config />:仅能够在已经在已经注册过的bean上面起作用。对于没有在spring容器中注册的bean,它并不能执行任何操作。
(2)< context:component-scan base-package=“XX.XX”/> :除了具有@autowire,@resource等注入功能之外,还具有自动将带有@Component,@Service,@Repository等注解的对象注册到spring容器中的功能。
如果同时使用这两个配置会不会出现重复注入的情况呢?
答案:因为< context:annotation-config />和 < context:component-scan>同时存在的时候,前者会被忽略。如@autowire,@resource等注入注解只会被注入一次!
一旦 被配置后,你就可以开始注解你的代码,表明 Spring 应该自动连接值到属性,方法和构造函数
@Required 注解应用于 bean 属性的 setter 方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常。
我们使用最多的注解应该就是 @Autowired 注解了。这个注解的功能就是为我们注入一个定义好的 bean。
@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值,可能会有这样一种情况,当你创建多个具有相同类型的 bean 时,并且想要用一个属性只为它们其中的一个进行装配,在这种情况下,你可以使用 @Qualifier 注解和 @Autowired 注解通过指定哪一个真正的 bean 将会被装配来消除混乱

需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

Spring 提供了对jdk 中@Resource 注解的支持。@Resource 注解既可以按名称匹配Bean, 也可以按类型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 方法上。
(1)byType 注入引用类型属性
@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean, 则会按照类型进行 Bean 的匹配注入。

(2)byName 注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

@Component :创建所有对象都可以使用此注解,除了控制器,业务逻辑层,数据访问层的对象
@Controller:创建控制器层的对象,此对象可以接收用户请求,返回处理结果
@Service:创建业务逻辑层的对象,此对象可施事务控制,向上给控制器返回数据,向下调用数据访问层
@Repository:创建数据访问层的对象 ,对数据库中的数据进行增删改查操作
1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。用 AOP 减少重复代码,专注业务实现。
Spring支持AOP的编程,常用的有以下几种:
Spring的AOP利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。
总结:AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。”
(1)切面(Aspect)
切面泛指交叉业务逻辑,或是公共的,通用的业务。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。
(2)连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3)切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(4)目标对象(Target)
目标对象指 将要被增强 的对象。 即包含主业 务逻辑的 类的对象。 上例中 的
BookServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然, 不被增强,也就无所谓目标不目标了。
(5)通知(Advice)
通知表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
切入点定义切入的位置,通知定义切入的时机。
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便, 而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
AspectJ 中常用的通知有四种类型:
(1)前置通知@Before
(2)后置通知@AfterReturning
(3)环绕通知@Around
(4)最终通知@After
(5)定义切入点@Pointcut(了解)
AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

解释:
modifiers-pattern 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分
以上表达式共 4 个部分可简化如下:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中访问权限、异常类型表示可省略部分,各部分间用空格分开。
在其中可以使用以下符号:

举例:
1.添加maven依赖
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.2.5.RELEASEversion>
dependency>
(2)引入 AOP 约束
在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签, 均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。
AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。
(1)@Before前置通知实现
第一步:定义接口和实现类
public interface SomeService {
public void doSome();
public String show(String name,int age);
}
@Service("someService")
public class SomeServiceImpl implements SomeService{
@Override
public void doSome() {
System.out.println("执行了业务方法doSome()");
}
@Override
public String show(String name, int age) {
System.out.println("show():"+"姓名:"+name+",年龄:"+age);
return name;
}
}
第二步:定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。
@Aspect//交给AspectJ框架去识别切面类,来进行切面方法的调用
@Component
public class MyAspectj {
/**
* 前置通知中的切面方法的规范
* 1)访问权限是public
* 2)没有返回值void
* 3)切面方法的名称自定义
* 4)切面方法可以没有参数,如果有也是固定的类型JoinPoint
* 5)使用@Before注解表明是前切功能
* 6)@Before的参数:
* value:指定切入点表达式
* public String doSome(String name, int age)
*/
//@Before(value="execution(public void com.xin.demo.SomeServiceImpl.doSome())")
//@Before(value = "execution(* com.xin.demo.SomeServiceImpl.*(String,int))")
// @Before(value = "execution(* *...SomeServiceImpl.*(..))")
//@Before(value = "execution(* *.*(..))")
@Before(value="execution(* com.xin.demo.*.*(..))")
public void myAspect() {
System.out.println("前置日志");
}
}
第三步:声明目标对象切面类对象
在pom.xml文件中配置
<context:component-scan base-package="com.xin.demo">context:component-scan>
Step4:注册 AspectJ 的自动代理
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
< aop:aspectj-autoproxy/>的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的。从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。其工作原理是,< aop:aspectj-autoproxy/>通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。
第五步:测试
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService someService1=(SomeService) context.getBean("someService");
System.out.println(someService1.getClass());
someService1.doSome();
System.out.println("返回值:"+someService1.show("李四",12));
}
结果:

在测试用例中,
SomeService someService1=(SomeService)context.getBean(“someService”);
System.out.println(someService1.getClass());
得到的结果是com.sun.proxy.$Proxy14,底层实现的是JDK动态代理。如果使用
SomeServiceImpl someService1=(SomeServiceImpl) context.getBean(“someService”);
System.out.println(someService1.getClass());
得到的结果是java.lang.ClassCastException: com.sun.proxy.$Proxy14 cannot be cast to com.xin.demo.SomeServiceImpl。原因:不能用接口的实现类(SomeServiceImplImpl)来转换Proxy的实现类,因为代理对象和实现类对象是同级的,应该用共同的接口来转换。
若想要底层使用cjlib代理,只需要将标签< aop:aspectj-autoproxy>< /aop:aspectj-autoproxy>中添加proxy-target-class即可
<aop:aspectj-autoproxy proxy-target-class="true">aop:aspectj-autoproxy>
proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。首先说明下proxy-target-class="true"和proxy-target-class="false"的区别,为true则是基于类的代理将起作用(需要cglib库),为false或者省略这个属性,则标准的JDK 基于接口的代理将起作用。
proxy-target-class在spring事务、aop、缓存这几块都有设置,其作用都是一样的。
进行测试:
@Test
public void test1() {
//由spring容器进行对象的创建
//如果想从spring容器中取出对象,则要先创建容器对象,并启动才可以取对象
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
SomeService someService1=(SomeService) context.getBean("someService");
System.out.println(someService1.getClass());
SomeServiceImpl someService2=(SomeServiceImpl) context.getBean("someService");
System.out.println(someService2.getClass());
someService1.doSome();
System.out.println("返回值:"+someService1.show("李四",12));
}
结果:

SomeService someService1=(SomeService) context.getBean(“someService”);
System.out.println(someService1.getClass());
SomeServiceImpl someService2=(SomeServiceImpl) context.getBean(“someService”);
System.out.println(someService2.getClass());
可以看到,someService1.getClass()和someService2.getClass()得到的结果是一样的,class
com.xin.demo.SomeServiceImpl$$ EnhancerBySpringCGLIB$ $8cd333bc,说明底层使用的是cjlib代理,因为cjlib代理是使用子类来进行代理的,可以使用父类SomeServiceImpl来接,也可以使用接口SomeService(子类间接实现SomeService接口)
(2)@Before 前置通知-方法有 JoinPoint 参数
在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。
不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。
@Aspect
@Component
public class MyAspectj {
// @Before(value="execution(public void com.xin.demo.SomeServiceImpl.doSome())")
@Before(value="execution(* *..demo.*.*(..))")
public void myAspect(JoinPoint joinPoint) {
System.out.println("目标方法的签名:"+joinPoint.getSignature());
System.out.println("目标方法的所有参数:"+ Arrays.toString(joinPoint.getArgs()));
System.out.println("前置日志");
}
}

(3)@AfterReturning 后置通知-注解有 returning 属性
在目标方法执行之后执行。**由于是目标方法之后执行,所以可以获取到目标方法的返回值。**该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以,被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。
接口方法:
public interface SomeService {
String doSome(String name, int age);
Student change();
}
实现方法:
@Component
public class SomeServiceImpl implements SomeService {
@Override
public String doSome(String name, int age) {
System.out.println(name+"doSome方法被调用 (主业务方法)");
return "abcd";
}
@Override
public Student change() {
return new Student("张三");
}
}
定义切面:
@Aspect //交给AspectJ框架扫描识别切面类
@Component
public class MyAspect {
/**
* 后置通知切面方法的规范
* 1)访问权限是public
* 2)切面方法没有返回值void
* 3)方法自定义
* 4)切面方法可以没有参数,如果有参数则是目标方法的返回值,也可以包含参数JoinPoint,它必须是第一个参数
* 5)使用@AfterReturning注解
* 6)参数value:指定切入点表达式
* returning:指定目标方法返回值的形参名称,此名称必须与切面方法的参数名称一致.
*/
@AfterReturning(value = "execution(* com.bjpowernode.s02.SomeServiceImpl.*(..))",returning = "obj")
public void myAfterReturning(Object obj){
System.out.println("后置通知..........");
//改变目标方法的返回值
if(obj != null){
if(obj instanceof String){
String s = ((String) obj).toUpperCase();//转为大写
System.out.println("在切面方法中的输出:"+s);
}
if(obj instanceof Student){
((Student) obj).setName("李四");
System.out.println("在切面方法中的输出"+(Student)obj);
}
}
}
}
测试类:
@Test
public void test01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someServiceImpl");
System.out.println(someService.getClass());
String s = someService.doSome("张三",22);
System.out.println("在测试类中输出目标方法的返回值---"+s);
}
运行结果:

如果目标方法的返回值是8种基本数据类型或String类型,则目标方法的返回值不可改变
如果目标方法的返回值是引用类型,则可改变
@Test
public void test03(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s02/applicationContext.xml");
SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someServiceImpl");
System.out.println(someService.getClass());
Student stu = someService.change();
System.out.println("在测试类中输出目标方法的返回值---"+stu);
}
运行结果:

(4)@Around 环绕通知-增强方法有 ProceedingJoinPoint参数
在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。**若目标方法有返回值,则该方法的返回值就是目标方法的返回值。**最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。
接口:
public interface SomeService {
public String doSome(String name,int age);
}
实现类:
@Service
public class SomeServiceImpl implements SomeService{
@Override
public String doSome(String name, int age) {
System.out.println("业务功能doSome()方法执行----");
return "abcd";
}
切面:
@Aspect
@Component
public class MyAspect {
/**
* 环绕通知方法的规范:
* 访问权限:public
* 切面方法有返回值,此返回值就是目标方法的返回值
* 方法名称自定义
* 方法有参数,此参数是目标方法
* 回避异常:Throwable
* 使用@Around注解声明是环绕通知
* 参数:value:指定切入点表达式
* @param pjp
* @return
* @throws Throwable
*/
@Around(value = "execution(* com.xin.service.s03.*.*(..))")
public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
//前切功能增强
System.out.println("环绕通知中前切功能----");
//调用目标方法
/**
* obj:目标方法的返回值
* pjp:目标方法本身
* proceed():调用目标方法
* getArgs():相当于传参
*/
Object obj = pjp.proceed(pjp.getArgs());
//后切功能增强
System.out.println("环绕通知中后切功能----");
return obj.toString().toUpperCase();
}
}
测试:
@Test
public void test() {
ApplicationContext ac=new ClassPathXmlApplicationContext("s03/applicationContext.xml");
SomeService s=(SomeService) ac.getBean("someServiceImpl");
System.out.println(s.getClass());
String s1 = s.doSome("张三", 12);
System.out.println(s1);
}
运行结果:

(5)@After 最终通知
无论目标方法是否抛出异常,该增强均会被执行。
接口方法:
public interface SomeService {
String doSome(String name, int age);
}
方法实现:
@Component
public class SomeServiceImpl implements SomeService {
@Override
public String doSome(String name, int age) {
System.out.println(name+"doSome方法被调用 (主业务方法)");
System.out.println(1/0);
return "abcd";
}
}
定义切面:
@Aspect
@Component
public class MyAspect {
/**
* 最终方法的规范
* 1)访问权限是public
* 2)切面方法没有返回值void
* 3)方法名称自定义
* 4)方法可以没有参数,也可以有,则JoinPoint.
* 5)使用@After注解
* 6)参数:value:指定切入点表达式
*/
@After(value = "execution(* com.bjpowernode.s04.SomeServiceImpl.*(..))")
public void myAfter(){
System.out.println("最终通知被执行.............");
}
}
测试类:
@Test
public void test01(){
ApplicationContext ac = new ClassPathXmlApplicationContext("s04/applicationContext.xml");
SomeServiceImpl someService = (SomeServiceImpl) ac.getBean("someServiceImpl");
System.out.println(someService.getClass());
String s = someService.doSome("张三",22);
System.out.println("在测试类中输出目标方法的返回值---"+s);
}
运行结果:

(6)@Pointcut 定义切入点别名
当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
@Aspect
@Component
public class MyAspect {
/**
* 最终方法的规范
* 1)访问权限是public
* 2)切面方法没有返回值void
* 3)方法名称自定义
* 4)方法可以没有参数,也可以有,则JoinPoint.
* 5)使用@After注解
* 6)参数:value:指定切入点表达式
*/
@After(value = "mycut()")
public void myAfter(){
System.out.println("最终通知被执行.............");
}
@Before(value = "mycut()")
public void myBefore(){
System.out.println("前置通知被执行.............");
}
@AfterReturning(value = "mycut()",returning = "obj")
public void myAfterReturning(Object obj){
System.out.println("后置通知被执行.............");
}
//给切入点表达式起别名
@Pointcut(value = "execution(* com.bjpowernode.s04.SomeServiceImpl.*(..))")
public void mycut(){}
}
<aop:config>
<aop:pointcut id="ponitcut" expression="execution(* com.xin.service.SomeServiceImpl.*(..))"/>
<aop:aspect id="myaspect" ref="myaspect">
<aop:before method="myBefore" pointcut-ref="ponitcut">aop:before>
<aop:after-returning method="myAfterRuturning" pointcut-ref="ponitcut" returning="obj">aop:after-returning>
<aop:after method="myAfter" pointcut-ref="ponitcut">aop:after>
<aop:after-throwing method="myAfterThrowing" pointcut-ref="ponitcut" throwing="ex">aop:after-throwing>
<aop:around method="myAround" pointcut-ref="ponitcut">aop:around>
aop:aspect>
aop:config>

在使用普通的 JDBC 数据库时,就会很麻烦的写不必要的代码来处理异常,打开和关闭数据库连接等。但 Spring JDBC 框架负责所有的低层细节,从开始打开连接,准备和执行 SQL 语句,处理异常,处理事务,到最后关闭连接。
所以当从数据库中获取数据时,你所做的是定义连接参数,指定要执行的 SQL 语句,每次迭代完成所需的工作。
Spring JDBC 提供几种方法和数据库中相应的不同的类与接口。我将给出使用 JdbcTemplate 类框架的经典和最受欢迎的方法。这是管理所有数据库通信和异常处理的中央框架类。
第一步:设置依赖
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-ormartifactId>
<version>5.3.1version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.6.RELEASEversion>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.16version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.12version>
dependency>
dependencies>
第二步:配置spring-jdbc.xml
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource">property>
bean>
beans>
jdbc.properties文件
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/imooc_reader?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
jdbc.username=root jdbc.password=root
第三步:创建Student实体类
public class Student {
private Integer id;
private String name;
private Integer age;
private String addr;
public Student() {
}
public Student(Integer id, String name, Integer age, String addr) {
this.id = id;
this.name = name;
this.age = age;
this.addr = addr;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", addr='" + addr + '\'' +
'}';
}
测试:
//因为当前的测试用例需要依托与SpringIOC容器,
//JUnit4在运行的时候,会自动的初始化IOC容器,此时就可以通过注入的方式直接获取IOC容器中的bean
@RunWith(SpringJUnit4ClassRunner.class)
//JUnit4初始化的时候,IOC容器就会根据spring-jdbc.xml配置文件完成初始化的工作
@ContextConfiguration("classpath:spring-jdbc.xml")
public class AppTest
{
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
//添加
public void testInsert() {
String sql="insert into student values(null,?,?,?)";
jdbcTemplate.update(sql,"李明",2,"海南");
}
@Test
//修改
public void testUpdate() {
String sql="update student set age=? where id=?";
jdbcTemplate.update(sql,0,2);
}
@Test
//删除
public void testDelete() {
String sql="delete from student where id=?";
jdbcTemplate.update(sql,4);
}
@Test
//查询数据个数
public void testCount() {
String sql="select count(*) from student";
Integer integer = jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("count="+integer);
}
@Test
//查询数据信息
public void testList() {
String sql="select * from student";
List<Student> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Student.class));
for(Student stu:list) {
System.out.println(stu);
}
}
@Test
//根据id查询Student对象
public void testSelectById() {
String sql="select * from student where id=?";
Student student = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Student.class), 1);
System.out.println(student);
}
@Test
//批量增加
public void testBatchAdd() {
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"小明", 1,"天"};
Object[] o2 = {"小龙",2, "南"};
Object[] o3 = {"小林", 3,"地"};
Object[] o4 = {"小李",4, "北"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
batchArgs.add(o4);
String sql="insert into student values(null,?,?,?)";
jdbcTemplate.batchUpdate(sql,batchArgs);
}
@Test
public void testBatchDelete() {
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {1};
Object[] o2 = {2,};
Object[] o3 = {3,};
Object[] o4 = {4,};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
batchArgs.add(o4);
String sql="delete from student where age=?";
jdbcTemplate.batchUpdate(sql,batchArgs);
}
}

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring 来管理。所以,该整合只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。
实现 Spring 与 MyBatis 的整合。常用的方式:扫描的 Mapper 动态代理。Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插上 mybatis,两个框架就是一个整体。
事务原本是数据库中的概念,在实际项目的开发中,进行事务的处理一般是在业务逻辑层, 即 Service 层。这样做是为了能够使用事务的特性来管理关联操作的业务。
在 Spring 中通常可以通过以下两种方式来实现对事务的管理:
(1)使用 Spring 的事务注解管理事务
(2)使用 AspectJ 的 AOP 配置管理事务(声明式事务管理)

MySQL:mysql默认的事务处理级别是’REPEATABLE-READ’,也就是可重复读
Oracle:oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。
默认系统事务隔离级别是READ COMMITTED,也就是读已提交

总结:
常用
PROPAGATION_REQUIRED:必被包含事务
PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不单开事务
PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
不常用
PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
PROPAGATION_NESTED:嵌套事务

Spring中事务的实现有两种方式,一种是基于xml文件的实现,一种是基于注解方式实现。在SSM的开发中,多使用注解方式实现事务的处理。
第一步:添加依赖
<dependencies>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.11version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.6version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>1.3.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.17version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.12version>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
第二步:创建com.xin.mapper包
public interface AccountsMapper {
//增加账户信息
/*private int aid;
private String aname;
private String acontent;*/
@Insert("insert into accounts values(#{aid},#{aname},#{acontent})")
int insert(Accounts accounts);
}
创建service
public interface AccountsService {
int insert(Accounts accounts);
}
创建serviceImpl
@Service
@Transactional(propagation= Propagation.REQUIRED,//事务的传播特性
noRollbackForClassName = "ArithmeticException",//指定发生什么异常不回滚,使用的是异常的名称
noRollbackFor = ArithmeticException.class,//指定发生什么异常不回滚,使用的是异常的类型
rollbackForClassName = "",//指定发生什么异常必须回滚,使用的是异常的名称
rollbackFor = ArithmeticException.class,//指定发生什么异常必须回滚,使用的是异常的类型
readOnly = false,//默认是false,如果是查询操作,必须设置为true
timeout = -1,//连接超时设置,默认是-1,表示永不超时
isolation = Isolation.DEFAULT//使用数据库自己的隔开级别
)
public class AccountsServiceImpl implements AccountsService {
@Autowired
private AccountsMapper accountsMapper;
@Override
public int insert(Accounts accounts) {
int num=0;
num=accountsMapper.insert(accounts);
System.out.println("账户增加成功!num="+num);
//手工抛出异常
System.out.println(1/0);
return num;
}
第三步:创建配置文件
jdbc.properties
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8
jdbc.username=root jdbc.password=root
mybatis的配置文件
SqlMapConfig.xml
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
configuration>
Spring 的配置文件
applicationContext_mapper.xml
<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 https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}">property>
<property name="url" value="${jdbc.url}">property>
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource">property>
<property name="configLocation" value="SqlMapConfig.xml">property>
<property name="typeAliasesPackage" value="com.xin.pojo">property>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.xin.mapper">property>
bean>
beans>
applicationContext_service.xml
<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" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<import resource="applicationContext_mapper.xml">import>
<context:component-scan base-package="com.xin.service.impl">context:component-scan>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>
beans>
测试:
@Test
public void testAccounts() {
ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext_service.xml");
AccountsService usersService=(AccountsService) ac.getBean("accountsServiceImpl");
int num=usersService.insert(new Accounts(4,"zhangsan4","账户安全2"));
}