文章目录
一、Spring概述
概念:Spring是一个轻量级的开源框架,是一个具有控制反转(IOC)和面向切片编程(AOP)特点的容器。
组成:
- Spring Core:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是
BeanFactory
,它是工厂模式的实现。BeanFactory
使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。 - Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文
包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 - Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText和 POI。
二、IOC
1、概述
IOC:传统的程序设计由开发者主动在程序中获取依赖对象,这样造成了对象之间的紧耦合,如果程序功能需要变动,那么将进行大量的修改。IOC思想是将设计好的对象交给容器控制,由容器来统一管理对象,而不是在对象内部直接控制,这样就实现了对象之间的松耦合。
DI:IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过依赖注入(DI)来实现的。Spring会在对象需要某个依赖对象时进行依赖对象的注入,本质上这是由反射实现的。
简单来说,控制反转(IoC)是一种设计思想,依赖注入(DI)实现IoC的一种方法。
进一步来看,控制反转IoC(Inversion of Control)就是将创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
2、第一个Spring程序
用一个简单的例子来理解一下Spring的IoC思想。
- Java类文件
package com.lee.dao;
public interface UserDao {
}
package com.lee.dao.impl;
import com.lee.dao.UserDao;
public class UserDaoMysqlImpl implements UserDao {
}
package com.lee.dao.impl;
import com.lee.dao.UserDao;
public class UserDaoOracleImpl implements UserDao {
}
package com.lee.service;
public interface UserService {
}
package com.lee.service.impl;
import com.lee.dao.UserDao;
import com.lee.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
- 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"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象,由spring创建和管理-->
<bean id="userDaoImpl1" class="com.lee.dao.impl.UserDaoMysqlImpl"/>
<bean id="userDaoImpl2" class="com.lee.dao.impl.UserDaoOracleImpl"/>
<bean id="userServiceImpl" class="com.lee.service.impl.UserServiceImpl">
<!--IoC:通过依赖注入实现装配不同的对象-->
<property name="userDao" ref="userDaoImpl1"/>
</bean>
</beans>
- 测试代码
import com.lee.pojo.Address;
import com.lee.pojo.Hello;
import com.lee.pojo.Student;
import com.lee.service.impl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Testsuite {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");
System.out.println(userServiceImpl);
}
}
可以看出,我们只需要在xml配置文件中进行修改就能实现不同的操作 , 而不用改动程序。 因此,IoC实现了对象由Spring 来创建,管理和装配 !
3、Bean的创建
Bean即为IoC容器初始化、装配及管理的对象,是实现IoC的根本。在Spring中bean有三种装配机制:
- 在spring的xml配置文件中显示配置
- 隐式的bean发现机制和自动装配
- 在java中显示配置
接下来逐一进行介绍
1)创建对象
测试类
package com.lee.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Hello {
private String name;
public void showName(){
System.out.println(name);
}
}
- 通过无参构造方法
<!--通过无参构造进行属性注入-->
<bean id="hello" class="com.lee.pojo.Hello">
<property name="name" value="hello,spring"/>
</bean>
- 通过有参构造方法
<!--通过有参构造注入,三种方式:根据参数下标/参数名字/参数类型-->
<bean id="hello2" class="com.lee.pojo.Hello">
<!-- <constructor-arg index="0" value="lee"/>-->
<!-- <constructor-arg name="name" value="lee"/>-->
<constructor-arg type="java.lang.String" value="lee"/>
</bean>
2)配置
- 别名
在Spring配置文件中利用alias标签为bean设置别名:
<alias name="userT" alias="userNew"/>
- bean的配置
<!--id 是bean的标识符,要唯一,
如果没有配置id,name就是默认标识符
如果配置id,又配置了name,那么name是别名
name可以设置多个别名,可以用逗号,分号,空格隔开
如果不配置id和name,可以根据applicationContext.getBean(.class)获取对象;
class是bean的全限定名=包名+类名 -->
<bean id="hello" name="hello2 h2,h3;h4" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
- 配置文件导入
Spring配置文件中可以通过import标签导入其他spring配置文件:
<import resource="{path}/beans.xml"/>
4、依赖注入
依赖注入是指由容器来设置和装配Bean对象所依赖的资源。
- 构造器注入(即创建对象)
- set注入
set注入要求必须注入的属性有set方法,方法名满足set+属性名(首字母大写)
注:boolean类型属性为isXxx
Student类
package com.lee.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbys;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
public void setName(String name) {
this.name = name;
}
public void setAddress(Address address) {
this.address = address;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public void setCard(Map<String, String> card) {
this.card = card;
}
public void setGames(Set<String> games) {
this.games = games;
}
public void setWife(String wife) {
this.wife = wife;
}
public void setInfo(Properties info) {
this.info = info;
}
public void show(){
System.out.println("name=" + name
+ "\naddress=" + address
+ "\nbooks=");
for(String book:books){
System.out.println("<<" + book + ">>\t");
}
System.out.println("\nhobby: " + hobbys.toString());
System.out.println("card: " + card);
System.out.println("games: " + games);
System.out.println("wife: " + wife);
System.out.println("info: " + info);
}
}
Address类
package com.lee.pojo;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
public class Address {
private String address;
public String getAddress(){
return address;
}
public void setAddress(String address){
this.address = address;
}
public void show(){
System.out.println(address);
}
}
配置文件beans.xml
<!--student类依赖注入(set)-->
<!--设置引用类address-->
<bean id="addr" class="com.lee.pojo.Address">
<property name="address" value="NanChang"/>
</bean>
<bean id="student" class="com.lee.pojo.Student">
<!--常量注入-->
<property name="name" value="lee"/>
<!--bean注入-->
<property name="address" ref="addr"/>
<!--数组注入-->
<property name="books">
<array>
<value>think in java</value>
<value>mysql</value>
<value>think in jvm</value>
</array>
</property>
<!--list注入-->
<property name="hobbys">
<list>
<value>sing song</value>
<value>play ball</value>
<value>study</value>
</list>
</property>
<!--map注入-->
<property name="card">
<map>
<entry key="china bank" value="1212-5645-4894-489"/>
<entry key="people bank" value="7899-4865-1234-456"/>
</map>
</property>
<!--set注入-->
<property name="games">
<set>
<value>ball game</value>
<value>plane game</value>
<value>shot game</value>
</set>
</property>
<!--null值注入-->
<property name="wife"><null/></property>
<property name="info">
<props>
<prop key="sno">201520320114</prop>
<prop key="cno">67</prop>
</props>
</property>
</bean>
- 简化注入方式
P命名空间注入
<!--xmlns:p="http://www.springframework.org/schema/p" p命名空间注入约束(依然需要set方法,可视为set方式的语法糖)-->
<bean id="addr2" class="com.lee.pojo.Address" p:address="NanJing"/>
C命名空间注入
<!-- xmlns:c="http://www.springframework.org/schema/c" c命名空间注入约束(需要有参构造,可视为有参构造方式的语法糖)-->
<bean id="addr3" class="com.lee.pojo.Address" c:address="ShangHai"/>
注释部分的代码是加在头文件的约束文件
5、Bean的自动装配
Spring的自动装配主要实现的两个操作:
- 组件扫描:Spring会自动发现应用上下文中所创建的bean;
- 自动装配:Spring自动满足bean之间的依赖
实体类文件
package com.lee.pojo;
public class Dog {
public void shout(){
System.out.println("wang...");
}
}
package com.lee.pojo;
public class Cat {
public void shout(){
System.out.println("miao...");
}
}
package com.lee.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
@NoArgsConstructor
@AllArgsConstructor
public class User {
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getUsername() {
return username;
}
//bean对象可以为空
@Autowired(required = false)
private Cat cat;
@Autowired
@Qualifier(value = "dog2")
//@Resource
private Dog dog;
private String username;
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解支持-->
<context:annotation-config/>
<!--当注解注入时设置了require为false,可以不注册bean(bean对象即为空指针)-->
<bean id="cat" class="com.lee.pojo.Cat"/>
<bean id="dog" class="com.lee.pojo.Dog"/>
<!--结合Autowired使用Qualifier注解根据name进行自动装配-->
<bean id="dog2" class="com.lee.pojo.Dog"/>
<!--使用Resource注解根据name进行自动装配,无匹配在根据type进行查找-->
<!--使用Autowired注解实现自动注入-->
<bean id="user" class="com.lee.pojo.User"/>
<!--通过名字实现自动注入,需要注入属性id与变量名相同-->
<!-- <bean id="user" class="com.lee.pojo.User" autowire="byName">-->
<!-- <property name="username" value="lee"/>-->
<!-- </bean>-->
<!--通过类型实现自动注入,需要注入属性类型唯一(不能有另一个同类型不同id)-->
<!-- <bean id="user" class="com.lee.pojo.User" autowire="byType">-->
<!-- <property name="username" value="lee"/>-->
<!-- </bean>-->
</beans>
1)byName实现
对应配置文件中以下注释部分
<!--通过名字实现自动注入,需要注入属性id与变量名相同-->
<bean id="user" class="com.lee.pojo.User" autowire="byName">
<property name="username" value="lee"/>
</bean>
实现步骤
- 查找类中所有的set方法名,获取set后首字母小写的字符串
- 在Spring容器中寻找是否有对应该字符串名称的bean对象id
- 有则注入;否则报空指针异常
注:需要有set方法
2)byType实现
对应配置文件中以下注释部分
<!--通过类型实现自动注入,需要注入属性id与变量名相同-->
<bean id="user" class="com.lee.pojo.User" autowire="byType">
<property name="username" value="lee"/>
</bean>
实现步骤
- 直接在Spring容器中寻找是否有对应该bean中属性类型的bean对象id
- 有则注入;否则报空指针异常
注:同一类型的对象需要在容器中保证唯一性
3)使用注解
Spring2.5后开始全面支持注解,可以通过注解来自动完成属性对象的注入。
使用注解需要在Spring配置文件中引入context文件头并开启注解支持。
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--开启属性注解支持-->
<content:annotation-config/>
- @Autowired
User类部分代码
//在依赖属性对象或set方法上加@Autowired注解
//按byType方式进行自动匹配
//require为false表明bean对象可以为空
@Autowired(required = false)
private Cat cat;
- @Qualifier
User类部分代码
//可位于依赖属性对象或set方法上
//需要与@Autowired一起使用,加上@Qualifier可以根据byName的方式进行自动装配
//value中的值为依赖bean对象的id
@Autowired
@Qualifier(value = "dog2")
private Dog dog;
- @Resource
User类部分代码
//可位于依赖属性对象或set方法上
//如果有默认的name属性,则按byName的方式进行查找装配
//未找到或无默认则进行默认Name查询
//以上都不成功,则按byType方式进行自动装配
@Resource
//@Resource(name="dog2")
//默认名称为dog
private Dog dog;
6、注解开发
使用注解开发需要先引入spring-aop,用Maven或手动导入依赖包。
步骤:
首先在spring配置文件中进行配置,需要引入context头文件和注解扫描标签,如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lee.pojo"/>
</beans>
声明注解类,使用@Component
package com.lee.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//Component后面为bean id,value后为属性值
@Component("user")
public class User {
//@Value("lee")
public String name;
//public String name = "lee";
@Value("lee")
public void setName(String name){
this.name = name;
}
}
spring为了便于区分,针对不同层设置了功能相同的不同注解名称
- @Controller:web层
- @Service:service层
- @Repository:dao层
同时,如果对象之间存在组合关系,那么可以利用自动装配注解,参考上节内容。
7、基于java类进行配置
事实上,我们可以完全不借助spring配置文件进行bean的配置,这需要用到JavaConfig,它通过java类的方式提供Bean的定义信息,是spring的一个子项目。
步骤:
- 使用@Configuration注解需要作为配置的类,表示该类将定义Bean的元数据
- 使用@Bean注解相应的方法,该方法名默认就是Bean的名称,该方法返回值就是Bean的对象。
- AnnotationConfigApplicationContext或子类进行加载基于java类的配置
package com.lee.pojo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//对应bean id
@Configuration("dog")
//导入其他配置类
@Import(MyConfig.class)
public class Dog {
@Bean
public Dog dog(){
return new Dog();
}
}
Spring提供了一个AnnotationConfigApplicanContext类,能够直接通过标注@Configuration的Java类启动Spring容器,测试代码如下:
import com.lee.pojo.Dog;
import com.lee.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSuite {
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(Dog.class);
Dog dog = (Dog)context.getBean("dog");
System.out.println(dog.toString());
}
}
注:使用bean注解的方法不能是private、final、static修饰的。
至此,三种Bean的配置方式都已经介绍完毕,可参考Spring bean配置的三种方式(XML、注解、Java类深入了解。
8、Bean的作用域和生命周期
作用域:
Bean支持多种作用域,这里考虑singleton、prototype、request、session 和 global session 五种。
- singleton:在spring Ioc容器中仅存在一个bean实例,bean以单例方式存在,为默认值
配置:在bean标签中使用scope属性,或使用@Scope注解
<bean id="ServiceImpl" class="com.lee.test.ServiceImpl" scope="singleton">
或@Scope("singleton")
- prototype:每次从容器中调用bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()
配置:在bean标签中使用scope属性,或使用@Scope注解
<bean id="ServiceImpl" class="com.lee.test.ServiceImpl" scope="prototype">
或@Scope("prototype")
- request:每次Http请求都会创建一个新的Bean,该作用于仅适用于WebApplicationContext环境
- session:同一个Http Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境
- globalSession:一般作用于Portlet
注:后面三个使用场景仅在web环境中,同样可采用定义scope属性的方式进行配置。但基于注解的配置方式与前面的稍有不同,形式如下:
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.INTERFACES)
可以发现,@Scope 同时还有一个 proxyMode 属性,他被设置成了 ScopedProxyMode.INTERFACES 。这个属性解决了将会话或者请求作用域的 Bean 注入成单例 Bean 中所遇到的问题。
生命周期:
如上图所示,spring提供了相关接口和方法供开发者进行实现,这种实现会穿插在Bean的生命周期的各个部分。
想详细了解这部分内容可以查阅Spring中bean的作用域与生命周期。
三、AOP
1、代理模式
静态代理:使用代理角色来代理真实角色,客户端通过代理角色来进行业务操作。
抽象角色
public interface Rent{
public void rent();
}
真实角色
public class Host implements Rent{
public void rent(){
system.out.println("租房子");
}
}
代理角色
public class Proxy implements Rent{
private Host host;
public Proxy(){}
public Proxy(Host host){
this.host = host;
}
public void rent(){
seeHouse();
host.rent();
fare();
}
}
public void seeHouse(){
}
public void fare(){
}
客户角色
public class Client{
public static void main(String[] args){}
//创建真实角色
Host host = new Host();
//将真实角色进行代理
Proxy proxy = new Proxy(host);
//通过代理角色进行业务操作
proxy.rent();
}
静态代理完成了业务的分工,使得公共业务发生扩展是能够更加集中方便。但其缺点是增加了代理类,开发效率降低,因此提出了动态代理。
动态代理
抽象角色
package com.lee.aop;
public interface Rent {
void rent();
}
真实角色
package com.lee.aop;
public class Host implements Rent{
public void rent() {
System.out.println("房屋出租");
}
}
代理角色
package com.lee.aop;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//代理的是接口
private Rent rent;
public void setRent(Rent rent){
this.rent = rent;
}
//生成代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//proxy:代理类
//method:代理类调用处理程序对象的方法
//调用方法并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
seeHouse();
Object result = method.invoke(rent, args);
fare();
return result;
}
//代理类特有方法
public void seeHouse(){
System.out.println("带租客看房");
}
public void fare(){
System.out.println("收中介费");
}
}
客户角色
package com.lee.aop;
public class Client {
public static void main(String[] args) {
//真实对象
Host host = new Host();
//生成代理实例
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//
pih.setRent(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
这里的动态代理使用了位于java.lang.reflect包下提供的Proxy类和InvocationHandler接口,通过这两个可以生成JDK动态代理类或动态代理对象。代理对象代理的是接口,这也是动态代理的核心。从本质上来说,动态代理是通过反射实现的。
如果将代理对象设置为object,也可以得到一个通用的动态代理实现类,如下:
package com.lee.utils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil implements InvocationHandler {
private Object target;
public void setTarget(Object target){
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result = method.invoke(target, args);
return result;
}
//自定义扩展功能
public void log(String methodName){
System.out.println("执行了" + methodName + "方法");
}
}
动态代理弥补了静态代理的缺点,由于代理的是接口,因此动态代理可以代理多个类,并且可以轻松完成公共业务的扩展。
2、AOP概述
AOP(面向切片编程),是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP的一些概念:
- 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要
- 关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
- 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。
- 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
- 目标(Target):被通知对象。
- 代理(Proxy):向目标对象应用通知之后创建的对象。
- 切入点(PointCut):切面通知 执行的 “地点”的定义。
- 连接点(JointPoint):与切入点匹配的执行点。
- 织入:将切面与其他对象连接创建复合对象
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice增强方式:
Spring AOP同样定义了六种切片类型和三种切面类型,由于几乎不使用,在这里不作赘述。
可移步Spring AOP就是这么简单查看详细内容
3、AOP实现
需要aspectjweaver依赖包,用Maven或手动导入
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
Spring提供了3种类型的AOP支持:
- 基于代理的经典SpringAOP:需要实现接口,手动创建代理
- 纯POJO切面:使用XML配置,aop命名空间
- @AspectJ注解驱动的切面:使用注解的方式,这是最简洁和最方便的
假设service层存在UserService的接口及实现类
package com.lee.service;
public interface UserService {
void add();
void delete();
void update();
void search();
}
package com.lee.service;
import org.springframework.stereotype.Component;
@Component("userService")
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加用户");
}
public void delete() {
System.out.println("删除用户");
}
public void update() {
System.out.println("更新用户");
}
public void search() {
System.out.println("查询用户");
}
}
方式一:实现接口
spring配置文件
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lee.service"/>
<context:component-scan base-package="com.lee.aop"/>
<!--aop切片的配置-->
<!--方式一:通过SpringAPI实现-->
<aop:config>
<!--切入点-->
<aop:pointcut id="pointcut" expression="execution(* com.lee.service.UserServiceImpl.*(..))"/>
<!--执行环绕 advice-ref执行方法, pointcut-ref切入点-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
package com.lee.aop;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class AfterLog implements AfterReturningAdvice {
//method:要执行的目标对象的方法
//objects:要被调用的方法参数
//o1:目标对象
//o:返回值
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了" + o1.getClass().getName() + "的" + method.getName() + "方法,返回值" + o);
}
}
package com.lee.aop;
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//objects:要被调用的方法参数
//object:目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
这里通过实现
AfterReturningAdvice
和MethodBeforeAdvice
定义了前置增强和后置增强类。
方式二 :通过自定义类,使用xml配置信息
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lee.service"/>
<context:component-scan base-package="com.lee.aop"/>
<!--aop切片的配置-->
<!--方式二:自定义类实现-->
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="diyPointcut" expression="execution(* com.lee.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="diyPointcut"/>
<aop:after method="after" pointcut-ref="diyPointcut"/>
</aop:aspect>
</aop:config>
自定义增强类
package com.lee.aop;
import org.springframework.stereotype.Component;
@Component("diy")
public class DiyPointcut {
public void before(){
System.out.println("-------方法执行前------");
}
public void after(){
System.out.println("-------方法执行后-------");
}
}
方式三:注解实现
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.lee.service"/>
<context:component-scan base-package="com.lee.aop"/>
<!--aop切片的配置-->
<!--方式三:由一个注解实现的增强类实现(增加支持注解的配置)-->
<aop:aspectj-autoproxy/>
</beans>
自定义增强类
package com.lee.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class AnnotationPointcut {
@Before("execution(* com.lee.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("=====(自定义) 方法执行前======");
}
@After("execution(* com.lee.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("=====(自定义) 方法执行后======");
}
@Around("execution(* com.lee.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable{
System.out.println("==环绕前==");
System.out.println("签名:" + jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("==环绕后==");
System.out.println(proceed);
}
}
四、整合Mybatis
1、Mybatis-Spring
整合Mybatis和Spring需要添加mybatis-spring依赖,可通过maven或手动导入。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
mybatis配置文件: mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--引入资源文件进行属性配置,也可以直接用property直接进行配置-->
<!-- <properties resource="db.properties">-->
<!-- <property name="password" value="062433"/>-->
<!-- </properties>-->
<!--相关设置,这里配置了缓存(默认)和日志-->
<!-- <settings>-->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/>-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- </settings>-->
<!--声明类型的别名-->
<typeAliases>
<typeAlias type="com.lee.pojo.User" alias="User"/>
</typeAliases>
<!--类型处理器(typeHandlers):未设置-->
<!--对象工厂(objectFactory):未设置-->
<!--插件(plugins):未设置-->
<!--配置环境变量:事务管理器、数据源-->
<!-- <environments default="development">-->
<!-- <environment id="development">-->
<!-- <transactionManager type="JDBC"/>-->
<!-- <dataSource type="POOLED">-->
<!-- <property name="driver" value="${driver}"/>-->
<!-- <property name="url" value="${url}"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="${password}"/>-->
<!-- </dataSource>-->
<!-- </environment>-->
<!-- </environments>-->
<!--数据库厂商标识(databaseIdProvider):未配置-->
<!--映射器:Mapper.xml文件注册到Mybatis核心配置文件中才能起作用-->
<!-- <mappers>-->
<!-- <!–<mapper resource="com/lee/dao/UserMapper.xml"/>–>-->
<!-- <!–下面两个务必保证接口名和配置文件名一致–>-->
<!-- <mapper class="com.lee.dao.UserMapper"/>-->
<!-- <!–<package name="com.lee.dao"/>–>-->
<!-- </mappers>-->
</configuration>
这里的mybatis配置文件只设置了别名,其他属性也可进行相应设置,不过任何环境配置
<environments>
,数据源<DataSource>
和 MyBatis 的事务管理器<transactionManager>
都会被忽略,这部分交给mybatis-spring进行处理。
SqlSessionFactoryBean
在mybatis-spring中,可使用SqlSessionFactoryBean
来创建SqlSessionFactory
,在spring配置文件书写如下代码:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!--关联Mybatis配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--如果mybatis中未指定映射文件,则在此处可通过mapperLocations属性来进行指定-->
<property name="mapperLocations" value="classpath:com/lee/dao/*.xml"/>
</bean>
从上面可以看出,SqlSessionFactory
需要一个dataSource属性对象,这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。一个示例如下:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="062433"/>
</bean>
1)SqlSessionTemplate
SqlSessionTemplate
是mybatis-spring的核心,是SqlSession
的一个实现,因此可以在代码中完全可以替代在使用的SqlSession
。SqlSessionTemplate
是线程安全的,可以被多个 DAO 或映射器所共享使用。- 当调用 SQL 方法时(包括由
getMapper()
方法返回的映射器中的方法),SqlSessionTemplate
将会保证使用的SqlSession
与当前 Spring 的事务相关。此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 DataAccessExceptions。 - 由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用
SqlSessionTemplate
来替换 MyBatis 默认的DefaultSqlSession
实现。 - 在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题。
可以使用 SqlSessionFactory
作为构造方法的参数来创建 SqlSessionTemplate
对象,在spring配置文件中注册如下bean:
<!--注册sqlSessionTemplate,关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
在配置了SqlSessionTemplate
对象后,就可以根据上面的id(SqlSession
)将SqlSessionTemplate
注入给对象。
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSession" ref="sqlSession" />
</bean>
2)SqlSessionDaoSupport类
除了使用SqlSessionTemplate
外,通过继承SqlSessionDaoSupport
类也可以获取SqlSession
来进行数据库操作。SqlSessionDaoSupport
需要通过属性设置一个 sqlSessionFactory
或 SqlSessionTemplate
。如果两个属性都被设置了,那么 SqlSessionFactory
将被忽略。
SqlSessionDaoSupport
是一个抽象的支持类,用来提供SqlSession
。调用getSqlSession()
方法可以得到一个SqlSessionTemplate
,之后可以用于执行 SQL 方法。
<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
<!--设置sqlSessionTemplate-->
<!--<property name="sqlSessionTemplate" ref="sqlSession" />-->
</bean>
2、整合实践
pojo层文件
User
package com.lee.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
dao层文件
UserMapper.class
package com.lee.dao;
import com.lee.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectUser();
int addUser(User user);
int deleteUserById(int id);
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lee.dao.UserMapper">
<!--这里相当于实现了UserMapper接口,注意resultType使用了别名-->
<select id="selectUser" resultType="User">
select * from mybatis.user
</select>
<insert id="addUser" parameterType="User">
insert into user(id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUserById" parameterType="int">
deletes from user where id=#{id}
</delete>
</mapper>
UserMapperImpl.class
package com.lee.dao;
import com.lee.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component("userMapperImpl")
public class UserMapperImpl implements UserMapper{
// 方式一
@Autowired
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(5,"shang","520520"));
mapper.deleteUserById(5);
System.out.println("=====查找完成=====");
return mapper.selectUser();
}
//方式二(继承SqlSessionDaoSupport)
// public List<User> selectUser() {
// UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
// return mapper.selectUser();
// }
public int addUser(User user) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println("=====添加完成=====");
return mapper.addUser(user);
}
public int deleteUserById(int id) {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println("=====删除成功=====");
return mapper.deleteUserById(id);
}
}
utils包 (非必要)
LogUtil (织入日志)
package com.lee.utils;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class LogUtil{
@Before("execution(* com.lee.dao.UserMapperImpl.*(..))")
public void before(){
System.out.println("=====(自定义) 方法执行前======");
}
@After("execution(* com.lee.dao.UserMapperImpl.*(..))")
public void after(){
System.out.println("=====(自定义) 方法执行后======");
}
}
resources文件夹
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--声明类型的别名-->
<typeAliases>
<typeAlias type="com.lee.pojo.User" alias="User"/>
</typeAliases>
</configuration>
mybatis-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.lee.dao"/>
<context:component-scan base-package="com.lee.utils"/>
<!--引入属性文件-->
<context:property-placeholder ignore-unresolvable="true" location="classpath:db.properties" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/lee/dao/*.xml"/>
</bean>
<!--注册sqlSessionTemplate,关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--方式二-->
<!-- <bean id="userMapperImpl" class="com.lee.dao.UserMapperImpl">-->
<!-- <property name="sqlSessionFactory" ref="sqlSessionFactory"/>-->
<!-- </bean>-->
</beans>
利用
SqlSessionTemplate
和SqlSessionDaoSupport
的两种方式都已经在代码中体现。
3、事务管理
Spring的事务管理分为编程式事务管理和声明式事务管理,编程式事务管理指通过编码的方式实现事务管理,声明式事务基于AOP,将业务逻辑与事务处理解耦。声明式事务对代码侵入较少,在实际使用中使用比较广泛。这里仅讨论声明式事务管理,想了解更多内容请移步Spring事务管理-编程式事务、声明式事务。
头文件约束
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
配置事务管理器
<!--配置事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
配置事务通知
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="selectUser" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
事务的传播特性
propagation_requierd
:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这
个事务中,这是最常见的选择。
propagation_supports
:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory
:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new
:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported
:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never
:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested
:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与
propagation_required
类似的操作
配置aop织入事务
!--配置aop-->
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* com.lee.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>
将事务补充进上一小节的mybatis-spring配置文件,得到如下代码
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.lee.dao"/>
<context:component-scan base-package="com.lee.utils"/>
<!--配置aop-->
<aop:config>
<aop:pointcut id="txpointcut" expression="execution(* com.lee.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txpointcut"/>
</aop:config>
<aop:aspectj-autoproxy/>
<!--配置事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="selectUser" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="062433"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/lee/dao/*.xml"/>
</bean>
<!--注册sqlSessionTemplate,关联sqlSessionFactory-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!--利用构造器注入-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--方式二-->
<!-- <bean id="userMapperImpl" class="com.lee.dao.UserMapperImpl">-->
<!-- <property name="sqlSessionFactory" ref="sqlSessionFactory"/>-->
<!-- </bean>-->
</beans>
测试文件:
import com.lee.dao.UserMapper;
import com.lee.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestSuite {
@Test
public void selectUserTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("mybatis-spring.xml");
UserMapper userMapper = (UserMapper) context.getBean("userMapperImpl");
User user = new User(5, "shang", "520520");
int result = userMapper.addUser(user);
System.out.println(result);
}
}
将
UserMapper.xml
中SQL语句中delete改成deletes,运行上述测试代码,可以发现由于事务管理,数据修改失败。但如果不添加事务,会成功增加数据。