Spring
1.1 简介
Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
-
Spring maven:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.0.3.RELEASE</version> </dependency>
1.2 优点
- spring是一个开源的免费的框架(容器)
- spring是一个轻量级、非入侵式的框架!
- 控制反转(IOC)、面向切面编程(AOP)!
- 支持事物管理、对框架整合的支持!
总结:spring就是一个轻量级的控制反转(IOC)和面向切面编程的框架!
2、set注入
-
在不使用set注入以前时,用户每新增一个需求一个业务dao,程序员都需要在实现类中new这个业务类,使得代码改动量极大。
-
使用set注入是在业务实现类xxxServiceImpl.java中添加用户属性setter方法,用该方式后,在业务层就只需要导入这个业务的接口实现类即可。
//使用new 的方式使得每次更改需求都要重新new新的实现接口类 //private UserDao userDao=new UserDaoImpl(); private UserDao userDao; //使用setter方法可以动态实现值的注入 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void getUser() { userDao.getUser(); }
测试类中代码就只需要改变业务实现类:
public class MyTest {
public static void main(String[] args) {
//不使用set之前的常规写法,每次new出要实现的业务实现类
UserService userService=new UserServiceImpl();
//使用set注入
((UserServiceImpl) userService).setUserDao(new UserDaoSqlImpl());
userService.getUser();
}
}
3、IOC控制反转
-
配置文件的主要内容:
<?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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!--使用spring来创建对象,在spring中这些都称为bean--> <bean id="User" class="com.yl.dao.UserDaoImpl"/> <bean id="UserSql" class="com.yl.dao.UserDaoSqlImpl"/> <bean id="ha" class="com.yl.service.UserServiceImpl"> <!-- ref:引用spring容器中的bean value:给对象属性赋值。例如给str=”spring“ --> <property name="userDao" ref="UserSql"/> </bean> </beans>
需要注意:
- property中的name属性是某个对象实例
- ref是引用spring容器中的bean
- value是给该对象属性赋值
- 依赖注入就是通过set方法实现
测试类中:
//使用注解则ClassPathXmlApplicationContext固定
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean得到指定的bean
HelloSpring hello = (HelloSpring) context.getBean("hello");
总结:
IOC核心就是:对象由spring来创建、管理、装配!
4、IOC创建对象方式
-
使用无参构造创建对象
<bean id="noCan" class="com.yl.pojo.User"> <property name="name" value="yl"/> </bean>
-
使用有参构造创建对象
-
使用下标赋值
<bean id="youCanIndex" class="com.yl.pojo.User"> <constructor-arg index="0" value="有参之下标方式"/> </bean>
-
通过类型赋值(多个参数不好办了,所以不建议使用)
<bean id="youCanType" class="com.yl.pojo.User"> <constructor-arg type="java.lang.String" value="有参之类型"/> </bean>
-
参数名
<bean id="youCanType" class="com.yl.pojo.User"> <constructor-arg name="name" value="有参之参数名"/> </bean>
-
总结:在配置文件加载的时候,容器中管理的对象就已经被实例化了!
5、Spring配置
5.1、别名
<!-- name:前面某个bean的id对应的变量名,alias:取的新别名-->
<alias name="user" alias="haha"/>
5.2、Bean的配置
<!--
id:bean的唯一标识符
class:bean 对象所对应的全限定名:包名+类型
name:也是别名,且可以用空格、逗号、分号隔开
-->
<bean id="user" class="com.yl.pojo.User" name="u1 u2,u3;u4">
<property name="name" value="str"/>
</bean>
5.3、import
这个import用于团队开发,可以将多个bean的配置文件,合并导入到总的applicationContext.xml中。重名的对象也会被合并。
6、依赖注入
6.1、构造器注入
6.2、set方式注入
- set依赖:所有的对象创建依赖于容器
- 注入:所有的对象属性由容器注入
项目:spring-04-di
实体类属性:
private String name;
private Address address;
private String[] books;
private List<String> hobby;
private Map<String,String> card;
private Set<String> games;
private String wife;
private Properties info;
配置文件中各种bean注入方法:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="address" class="com.yl.pojo.Address">
<property name="address" value="西华"/>
</bean>
<bean id="student" class="com.yl.pojo.Student">
<!-- 第一种:普通值注入,value-->
<property name="name" value="yl"/>
<!-- 第二种:bean注入,ref-->
<property name="address" ref="address"/>
<!-- 第三种:数组注入,value-->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
</array>
</property>
<!-- 第四种:List注入,value-->
<property name="hobby">
<list>
<value>听歌</value>
<value>唱歌</value>
<value>跳舞</value>
</list>
</property>
<!-- 第5种:Map注入,entry:key-value-->
<property name="card">
<map>
<entry key="身份证" value="1111111111123"/>
<entry key="学号" value="2109456465"/>
</map>
</property>
<!-- 第6种:set注入,set-value-->
<property name="games">
<set>
<value>幻塔</value>
<value>王者荣耀</value>
</set>
</property>
<!-- 第7种:null注入,null-->
<property name="wife">
<null/>
</property>
<!-- 第8种:Properties注入,-->
<property name="info">
<props>
<prop key="driver">123</prop>
<prop key="username">root</prop>
<prop key="url">456</prop>
<prop key="password">123546</prop>
</props>
</property>
</bean>
</beans>
测试:
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = (Student) context.getBean("student");
System.out.println(student.toString());
/*
Student{
name='yl',
address=Address{address='西华'},
books=[红楼梦, 西游记, 水浒传, 三国演义],
hobby=[听歌, 唱歌, 跳舞],
card={身份证=1111111111123, 学号=2109456465},
games=[幻塔, 王者荣耀],
wife='null',
info={password=123546, url=456, driver=123, username=root}}
*/
}
}
6.3、拓展注入方式
- p命名:
xmlns:p="http://www.springframework.org/schema/p"
bean的写法:
<bean id="user" class="com.yl.pojo.User" p:name="yl" p:age="18"/>
- c命名:
xmlns:c="http://www.springframework.org/schema/c"
bean的写法:
<bean id="user2" class="com.yl.pojo.User" c:name="yl2" c:age="19"/>
需要注意的是使用p,c注入时需要约束即xmlns路径,且c注入是需要有参构造而p注入需要无参构造。
1、单例模式,使用scope=singleton,此时即使创建多个对象也是调用的同一个bean,他们的结果就相同(spring默认机制)
2、原型模式,使用scope=prototype,get同一个bean,创建多个不同的对象
7、bean自动装配
7.1、byName自动装配
byName需要注入bean id需要和set方法后面名字相同,否则会装配失败
<!--
byName:会自动在容器上下文查找,和自己对象set方法后的值对应的bean id,就比如上面的cat、dog,
在前面的实体类中存在set方法后面的名字等于cat和dog,所以能够找到。
-->
<bean id="people" class="com.yl.pojo.People" autowire="byName">
<property name="name" value="小花"/>
</bean>
7.2、byType自动装配
<!-- byType:会自动在容器上下文查找和自己对象类型相同的bean,但byType必须保证该类型全局唯一-->
<bean id="people" class="com.yl.pojo.People" autowire="byType">
<property name="name" value="小花"/>
</bean>
7.3、使用注解实现自动装配
自动装配须知:
-
添加注解约束:
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
-
添加注解支持
<context:annotation-config/>
使用@Autowired可以不用编写set方法,前提是这个自动装配的属性在IOC(spring)容器中存在,且符合名字byName。
有多个对象时,可以使用@Qualifier(value=“”)来指定某个bean实现装配。
@Resource更强大,先去通过名字查找,再通过类型查找,都找不到才会报错,且存在@Resource(name=“”)的写法,用于明确某个bean。
8、使用注解开发
8.1、使用属性注解
在spring中使用注解开发须知:
1、需要在配置文件中导入aop的包:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9zMz4lPg-1663316208382)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220325094620826.png)]
2、添加扫描器,可以去扫描该包下所有的bean并生效:
<!-- 扫描包下所有的bean,不能写出具体某个实体类-->
<context:component-scan base-package="com.yl.pojo"/>
<!-- 自动装配驱动-->
<context:annotation-config/>
实体类pojo:
@Component
public class User {
//@Value也可以放在set方法上
@Value("yl2")
public String name;
}
测试:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//在使用注解时,由于配置文件中并没有bean,也就没有属性id,这里是使用该类名字的小写(类一般不重名)
User user = (User) context.getBean("user");
System.out.println(user.name);
}
8.2、衍生注解
@Component有几个衍生注解,,在web开发中就是按照mvc三层结构分。
- dao层:使用@Repository
- service层:使用@Service
- controller层:使用@Controller
这四个注解都是将某个类注册到sprng容器中,装配bean,每个层名字不同用于区分!
8.3、小结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n22othhI-1663316208384)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220325102519720.png)]
9、使用Java的方式配置spring
实体类:
package com.yl.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
private String name;
public String getName() {
return name;
}
@Value("小美")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类:
package com.yl.config;
import com.yl.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//@Configuration代表这是一个配置类,就和我们之前的bean.xml一样
//@ComponentScan("")用于扫描包,扫到的包都会生效
@Configuration
public class MyConfig {
//注册一个bean,就相当之前我们写的一个bean标签
//方法的名字就相当于bean标签中的id属性
//这个方法的返回值就相当于bean标签的class属性,返回类型
@Bean
User getUser(){
return new User();
}
}
测试类:
import com.yl.config.MyConfig;
import com.yl.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyTest {
@Test
public void test(){
//如果完全使用配置类去做,就只能使用AnnotationConfig 上下文来获取容器,通过配置类的class对象
ApplicationContext myConfig = new AnnotationConfigApplicationContext(MyConfig.class);
//这里的getUser就是配置类中的一个需要注入的方法名
User getUser = myConfig.getBean("getUser",User.class);
System.out.println(getUser.getName());
}
}
多个配置类就相当于多个bean,直接在配置类中添加@Import(xxx.class)即可
10、代理模式
代理模式是SpringAOP的底层
代理模式分类:
- 静态代理
- 动态代理
10.1、静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
- 客户:访问代理对象的人
对应代码:spring-08-proxy的demo01
代码步骤:
-
接口
public interface Rent { public void rent(); }
-
真实角色
public class Host implements Rent{ @Override public void rent() { System.out.println("房东要出租房子!"); } }
-
代理角色
public class Proxy implements Rent{ private Host host; public Proxy() { } //代理获取某个真实角色,去帮他做事 public Proxy(Host host) { this.host = host; } //处理真实角色的业务以及代理角色的一些附属操作 @Override public void rent() { host.rent(); seeHouse(); heTong(); fare(); } public void seeHouse(){ System.out.println("中介带你看房"); } public void fare(){ System.out.println("收取中介费!"); } public void heTong(){ System.out.println("签合同!"); } }
-
客户端访问代理角色
public class Client { public static void main(String[] args) { //创建那个真实角色(房东) Host host=new Host(); //将房东的事交给代理(中介)去做 Proxy proxy = new Proxy(host); proxy.rent(); } }
代理模式的好处:
- 可以使得真实角色的操作更加纯粹,不用去关注一些公共业务
- 公共业务就交给代理角色完成,实现了业务的分工!
- 公共业务发生扩展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色;代码量会翻倍~开发效率降低
10.2、加深理解
对应代码:spring-08-proxy的demo02
静态代理小结:
-
编写接口:接口中是真实角色想干的事情,但是真实角色不想自己亲自去干,太麻烦!
public interface UserService { public void add(); public void delete(); public void update(); public void query(); }
-
编写真实角色(接口实现类):是关于真实角色具体要干的事情(一般不会选择更改这里面的代码!!)
public class UserServiceImpl implements UserService{ @Override public void add() { System.out.println("增加了一个用户!"); } @Override public void delete() { System.out.println("删除了一个用户!"); } @Override public void update() { System.out.println("修改了一个用户!"); } @Override public void query() { System.out.println("查询了一个用户!"); } //宗旨就是不改变这个里面的代码,在代理中添加附属操作 }
-
编写代理(就好比中介):在代理中使用set方法绑定真实角色(使得有更多的真实角色也可以快捷更改),实现接口中的方法(就是帮真实角色要干的事情),在代理中可以有许多附属操作。
public class UserServiceProxy implements UserService{ private UserServiceImpl userService; //使用set方法使得客户端可以让代理想获取哪个UserServiceImpl都行 public void setUserService(UserServiceImpl userService) { this.userService = userService; } @Override public void add() { log("add"); userService.add(); } @Override public void delete() { log("delete"); userService.delete(); } @Override public void update() { log("update"); userService.update(); } @Override public void query() { log("query"); userService.query(); } //附属操作:日志等 public void log(String msg){ System.out.println("Debug使用了"+msg+"方法"); } }
-
编写客户端:创建代理对象,创建真实角色对象,代理绑定真实角色,将真实角色的事交给代理角色去办
public class Client { public static void main(String[] args) { //创建一个真实角色 UserServiceImpl userService = new UserServiceImpl(); //创建代理角色 UserServiceProxy userServiceProxy = new UserServiceProxy(); //将真实角色的事交给代理角色去办 userServiceProxy.setUserService(userService); userServiceProxy.query(); } }
10.3、动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口:JDK动态代理
- 基于类:cglib
- Java字节码实现:javassist
动态代理的好处:静态代理的好处都有且一个动态代理可以代理多个类,只要实现了同一个接口即可。(例如接口为UserService,而实现类有UserServiceImpl,UserServiceImpl2,他们都是实现的UserService,在客户端中只需要更改绑定的真实角色UserServiceImpl,UserServiceImpl2即可)。
动态代理处理程序:
public class ProxyInvocationHandler implements InvocationHandler {
//target:要代理的接口(真实角色),set方法就是来绑定要帮哪个真实角色做事
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成并得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果(就是相当于静态代理中的代理角色帮真实角色做事),这里是使用invoke方法来实现
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//动态代理的本质就是通过反射机制实现!method:接口中的一些方法
log(method.getName());
Object result=method.invoke(target,objects);
return result;
}
//代理程序中的其他附属操作
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}
客户端:
public class Client {
public static void main(String[] args) {
//真实角色
UserServiceImpl userService=new UserServiceImpl();
//代理角色,现在还不存在,需要我们使用代理角色程序生成
ProxyInvocationHandler pih=new ProxyInvocationHandler();
//绑定被代理的真实角色
pih.setTarget(userService);
//生成代理角色(接口是什么类型就转换成什么类型)
UserService proxy = (UserService) pih.getProxy();
//代理角色开始办事
proxy.query();
}
}
11、AOP
11.1、什么是AOP
面向切面编程
11.2、AOP在Spring中的作用
提供声明式事务;允许用户自定义切面
11.3、使用Spring实现AOP
使用AOP注入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
方式一:使用Spring的API接口
前置和后置方法:
public class log implements MethodBeforeAdvice {
@Override
//method:要执行的目标对象的方法
//args:参数
//target:目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("执行了"+target.getClass().getName()+"的方法:"+method.getName());
}
}
//后置
public class AfterLog implements AfterReturningAdvice {
@Override
//returnValue:返回值
public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"方法,结果为:"+returnValue);
}
}
配置文件applicationContext.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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 注册bean-->
<bean id="userService" class="com.yl.service.UserServiceImpl"/>
<bean id="log" class="com.yl.Log.log"/>
<bean id="afterLog" class="com.yl.Log.AfterLog"/>
<!--方式一:使用Spring原生接口-->
<!-- 配置aop:需要导入aop的约束-->
<aop:config>
<!-- 切入点:expression表达式,execution:要执行的位置-->
<aop:pointcut id="pointcut" expression="execution(* com.yl.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
测试类:【注意:动态代理 代理的是接口】
public class MyTest {
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//动态代理代理的是接口
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
方式二:自定义切入点类
编写切入类:包含要环绕添加的方法
public class DiyPointCut {
public void before(){
System.out.println("=============执行之前===========");
}
public void after(){
System.out.println("=============执行之后===========");
}
}
配置文件:
<!-- 方式二-->
<bean id="diy" class="com.yl.diy.DiyPointCut"/>
<aop:config>
<!-- 自定义切面,ref:要引用的类-->
<aop:aspect ref="diy">
<!-- 切入点-->
<aop:pointcut id="point" expression="execution(* com.yl.service.UserServiceImpl.*(..))"/>
<!-- method:要切入的方法,pointcut-ref:要切入的点 -->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
</aop:config>
方式三:使用注解实现AOP
<!--开启注解支持-->
<aop:aspectj-autoproxy/>
主要使用的几个注解:
@Before("execution(* com.yl.service.UserServiceImpl.*(..))")
@After("execution(* com.yl.service.UserServiceImpl.*(..))")
@Aroud("execution(* com.yl.service.UserServiceImpl.*(..))")
12、Spring整合mybatis
12.1、mybatis-spring
相关项目:spring-10-mybatis
12.2、第一种方式整合mybatis
导入相关的包、依赖
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!-- spring-webMvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.16</version>
</dependency>
<!-- Spring操作数据库还需要spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.16</version>
</dependency>
<!-- aop-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency>
<!-- spring-mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
</dependencies>
可能需要注意的:
<!-- maven的约定大于配置会导致我们自己写的配置无法导入或失效,需要更改配置-->
<build>
<resources>
<!-- 系统默认的路径为resources下-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<!-- 我们自己更改的路径为Java下的properties或者xml-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
配置文件,使用spring整合mybatis,不再使用SqlSessionFactory工具类,而是将它写入配置文件中:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- dataSource :数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="5418"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/yl/mapper/*.xml"/>
</bean>
<!-- sqlSessionTemplate-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<!-- 只能使用构造器注入sqlSessionFactory,因为没有set方法-->
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
测试就可以使用spring的方式进行了:
public class MyTest {
@Test
public void test() throws IOException {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User selectUser : userMapper.selectUsers()) {
System.out.println(selectUser);
}
}
}
总结步骤:
- 编写实体类pojo
- 编写实体类方法接口
- 编写接口实现类
- 编写sql语句的配置文件
- 整合mybatis(数据源、SqlSessionFactory、SqlSessionTemplate)
- 注入实现类bean
- 编写测试类
12.3、第一种方式整合mybatis
整合mybatis第二种方式:使用SqlSessionDaoSupport,直接继承使用,使用它连SqlSessionTemplate都不需要注入:
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> selectUsers() {
return getSqlSession().getMapper(UserMapper.class).selectUsers();
}
}
编写完实现类之后,只需要在applicationContext.xml中注入该实现类的bean即可,且它没有属性所以不需要注入sqlSession,只需要注入属性SqlSessionFactory即可:
<bean id="userMapper2" class="com.yl.mapper.UserMapperImpl2">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
13、声明式事务
13.1、事务简介
事务特点:
- 一组事务遵循要么全部成功,要么全部失败
- 事务在开发中很重要,涉及到数据的一致性问题
- 确保完整性和一致性
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会再受影响,被持久化的写到存储中
13.2、Spring中的事务管理
- 声明式事务:AOP
- 编写事务配置,引入需要
xmlns:tx="http://www.springframework.org/schema/tx"
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
编写配置文件
<!-- 配置声明式事务 dataSource要和jdbc数据库为同一个数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 结合AOP配置实现事务的织入-->
<!-- advice: 配置事务通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 表示给那些方法配置事务-->
<!-- 配置事务的传播特性:new propagation,默认REQUIRED-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="query" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务切入点-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.yl.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
事务一般是一个组,里面包含了许多方法,事务的作用也就是成功全部成功,失败全部失败:
public List<User> selectUsers() {
UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUser(4);
return mapper.selectUsers();
}
- 编程式事务:需要在代码中,进行事务的管理