导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
spring官方文档地址:https://spring.io/projects/spring-framework
一. Container Overvie 容器概述
该org.springframework.context.ApplicationContext
接口代表Spring IoC容器,并负责实例化,配置和组装Bean。容器通过读取配置元数据来获取有关要实例化,配置和组装哪些对象的指令。配置元数据以XML,Java批注或Java代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。
二.HelloSpring
实例
package com.learn.spirng;
/**
* @author wb-zy215449
*/
public class Hello {
private String name;
public Hello() {
}
public Hello(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Hello{" +
"name='" + name + '\'' +
'}';
}
}
单元测试
@Test
public void testHello(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
Hello hello = applicationContext.getBean("hello", Hello.class);
System.out.println(hello);
}
实例初始化方法:
(1)无参构造方法
<?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="hello" class="com.learn.spirng.Hello"/>
</beans>
(2)有参构造(根据参数名)
<bean id="hello" class="com.learn.spirng.Hello">
<constructor-arg name="name" value="zhangsan"/>
</bean>
(3)有参构造(根据参数类型)
<bean id="hello" class="com.learn.spirng.Hello">
<constructor-arg type="java.lang.String" value="lisi"/>
</bean>
(4)根据参数下标
<bean id="hello" class="com.learn.spirng.Hello">
<constructor-arg index="0" value="wangwu"/>
</bean>
(5)属性注入
<bean id="hello" class="com.learn.spirng.Hello">
<property name="name" value="maliu"/>
</bean>
三. Bean的自动装配
自动装配是spring满足bean依赖的一种方式,spring会在上下文寻找并自动给bean装配属性
在spring中有三种装配的方式:
- 在xml中显示配置
- 在java中显示配置
- 隐式的自动装配bean【重要】
环境:
package com.learn.pojo;
public class Persion {
private Cat cat;
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Persion{" + "cat=" + cat +", dog=" + dog +", name='" + name + '\'' + '}';
}
}
1.byName自动装配
<bean class="com.learn.pojo.Cat"/>
<bean class="com.learn.pojo.Dog"/>
<bean id="persion" class="com.learn.pojo.Persion" autowire="byName">
<property name="name" value="zhangsan"/>
</bean>
2.byType自动装配
<bean class="com.learn.pojo.Cat"/>
<bean class="com.learn.pojo.Dog"/>
<bean id="persion" class="com.learn.pojo.Persion" autowire="byType">
<property name="name" value="zhangsan"/>
</bean>
注 : byName的时侯,需保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
byType的时候,需保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
3.使用注解实现自动配置
jdk1.5 支持的注解 spring2.5 支持的注解
使用须知:
- 导入约束:context约束
- 配置注解的支持
<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 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/> </beans>
package com.learn.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Persion {
// @Autowired(required = false)
@Autowired
private Cat cat;
@Autowired
@Qualifier(value = "dog222")
private Dog dog;
private String name;
public Cat getCat() {
return cat;
}
public void setCat(Cat cat) {
this.cat = cat;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Persion{" + "cat=" + cat +", dog=" + dog +", name='" + name + '\'' + '}';
}
}
<context:annotation-config/>
<bean id="cat111" class="com.learn.pojo.Cat"/>
<bean id="dog222" class="com.learn.pojo.Dog"/>
<bean id="dog333" class="com.learn.pojo.Dog"/>
<bean id="persion" class="com.learn.pojo.Persion"/>
测试代码:
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Persion persion = context.getBean("persion", Persion.class);
@Autowired
可以直接在属性上使用,也可以在set方式使用。 使用Autowired我们可以不用编写set方法,前提是自动装配的属性在IOC容器中存在,且名字符合byName
@Nullable 字段注解,说明这个字段可以为null @Autowired(required = false) 这个字段显示注解为false 表示该属性可以为null
如果@Autowired自动装配的环境复杂,自动装配无法通过一个Autowired完成时,我们可以使用@Qualifier(value = "***")配合Autiwired使用,指定一个唯一的bean注入。
四:使用注解开发
在spring4 之后,要使用注解开发,必须保证aop包导入
1.Bean
2.属性注入
package com.learn.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("zhangsan")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + '}';
}
}
3.衍生的注解
@Component注解有几个衍生的注解,在web开发中,会按照mvc三层架构分层
- dao @Repository
- service @Service
- controller @Controller
这四个注解功能一致,都代表将某个类注入spring容器
4.自动装配置
- @Autowired 自动装配,通过类型,名字 (若Autowired不能唯一自动装配上属性,需配合@Qualifier(value="***"))
- @Nullable 说明自动可weinull
- Resoure 自动装配,通过名字,类型
5.作用域
package com.learn.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(value = "prototype")
public class User {
private String name;
public String getName() {
return name;
}
@Value("zhangsan")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + '}';
}
}
五:使用java的方式配置spring
我们现在要完全不适用spring的xml配置,全权交给java生成, JavaConfig是spring的一个子项目,在spring4之后,成为了一个核心功能。
环境:
package com.learn.service.serviceImpl;
import com.learn.service.UserService;
public class UserServiceImpl implements UserService {
public String getOrderId() {
return "5694141854";
}
}
package com.learn.pojo;
import org.springframework.beans.factory.annotation.Value;
public class User {
private String name;
public String getName() {
return name;
}
@Value("zhangsan")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' +'}';
}
}
基于java生成配置
package com.learn.config;
import com.learn.pojo.User;
import com.learn.service.UserService;
import com.learn.service.serviceImpl.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;
//这个也会spring托管,注册到容器中,因为他本身为一个@Component
//@Configuration代表一个配置类,相当于之前的application.xml
@Configuration
@Import(PojoConfig2.class)
@ImportResource("application.xml")
public class PojoConfig {
//注册一个Bean,相当于我们之前写的Bean标签,
//这个方法的方法名相当于bean标签中的id属性,
// 这个方法的返回值相当于bean标签中的class属性
@Bean
public User getUser(){
return new User();
}
@Bean
public UserService getUserService(){
return new UserServiceImpl();
}
}
package com.learn.config;
import com.learn.dao.UserDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PojoConfig2 {
@Bean
public UserDao userDao(){
return new UserDao();
}
}
测试类
package com.learn.pojo;
import com.learn.config.PojoConfig;
import com.learn.dao.UserDao;
import com.learn.service.UserService;
import com.learn.service.serviceImpl.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class UserTest {
@Test
public void testConfigUser(){
ApplicationContext context = new AnnotationConfigApplicationContext(PojoConfig.class);
User user = context.getBean("getUser", User.class);
System.out.println(user.getName());
}
@Test
public void testConfigUserService(){
ApplicationContext context = new AnnotationConfigApplicationContext(PojoConfig.class);
UserService userService = context.getBean("getUserService", UserServiceImpl.class);
System.out.println(userService.getOrderId());
}
@Test
public void testConfigUserDao(){
ApplicationContext context = new AnnotationConfigApplicationContext(PojoConfig.class);
UserDao userDao = context.getBean("userDao", UserDao.class);
System.out.println(userDao);
}
}
六:AOP
1.代理模式
成员:
- 抽象角色:一般为接口或者抽象类
- 真实角色:被代理的角色
- 代理角色:代理真实角色,实际执行方法,可对真实角色属性增强
2.静态代理
抽象角色:
package service.facade;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
真实角色:
package service.impl;
import service.facade.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 select() {
System.out.println("查询数据库数据");
}
}
代理角色:
package service.proxy;
import service.facade.UserService;
import service.impl.UserServiceImpl;
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
//真实角色,通过set注入,被代理角色代理
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("增加");
userService.add();
}
public void delete() {
log("删除");
userService.delete();
}
public void update() {
log("修改");
userService.update();
}
public void select() {
log("查询");
userService.select();
}
//新增的方法,对真实角色属性增强
public void log(String msg){
System.out.println("执行了一条"+msg+"方法");
}
}
3.动态代理
抽象角色:
package service.facade;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
真实角色:
package service.impl;
import service.facade.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 select() {
System.out.println("查询数据库数据");
}
}
代理角色:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 功能描述: <br>
* 〈自动生成动态代理类〉
* @Param:
* @Return:
* @Author:
* @Date:
*/
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Object traget;
public void setTraget(Object traget) {
this.traget = traget;
}
//生成代理对象
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),traget.getClass().getInterfaces(),this);
}
//处理代理实例并返回结果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//动态代理的本质就是使用反射的机制实现
Object result = method.invoke(traget, args);
return result;
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
测试类:
import demo01.service.facade.UserService;
import demo01.service.impl.UserServiceImpl;
import demo03.ProxyInvocationHandler;
import org.junit.Test;
public class Client {
@Test
public void testProxyInvocationHandler(){
//真实对象
UserServiceImpl userService=new UserServiceImpl();
//代理角色:现在没有
ProxyInvocationHandler invocationHandler=new ProxyInvocationHandler();
//通过调用程序处理角色来处理我们调用的接口对象
invocationHandler.setTraget(userService);
//这里的proxy就是动态生成的,我们并没有写
UserService proxy = (UserService) invocationHandler.getProxy();
proxy.delete();
}
}
4.Spring AOP
成员:
- 切面(ASPECT) 被模块化的特殊对像,一个类
- 通知(Advice) 切面必须完成的公作,及类的一个方法
- 目标(Target) 被通知的对像
- 代理(Proxy) 代理角色,实际调用方法的执行
- 切入点(PoinCut)切面通知
- 链接点(JoinPoint) 与切人点匹配的执行点
使用Spring实现AOP的方法:
抽象角色:
package com.xx.service;
public interface UserService {
public void add();
public void delete();
public void update();
public void select();
}
真实角色:
package com.xx.service.impl;
import com.xx.service.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 select() {
System.out.println("查询数据库数据");
}
}
前置增强:
package com.xx.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BizLogBefore implements MethodBeforeAdvice {
/**
* 功能描述: <br>
* 〈〉
* @Param: [method 要执行的目标对象的方法, args 参数, o 目标对象]
* @Return: void
* @Author:
* @Date:
*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");
}
}
后置增强
package com.xx.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class BizLogAfter implements AfterReturningAdvice {
public void afterReturning(Object result, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法,返回结果为:" + result);
}
}
1.使用spring的api接口实现
需要引入的包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
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: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 id="userService" class="com.xx.service.impl.UserServiceImpl"/>
<bean id="bizLogBefore" class="com.xx.log.BizLogBefore"/>
<bean id="bizLogAfter" class="com.xx.log.BizLogAfter"/>
<!-- 配置aop:需要导入aop的约束 -->
<aop:config>
<!-- 切入点,expression:表达式,execution:(要执行的位置 * * * * *) -->
<aop:pointcut id="pointcut" expression="execution(* com.xx.service.impl.UserServiceImpl.*(..))"/>
<!-- 执行环绕增强 -->
<aop:advisor advice-ref="bizLogBefore" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="bizLogAfter" pointcut-ref="pointcut"/>
</aop:config>
</beans>
单元测试:
package com.xx.service.test;
import com.xx.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ServiceTest {
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.select();
}
}
2.使用自定义方法实现
自定义方法:
package com.xx.diy;
public class DiyPoinCut {
public void before(){
System.out.println("方法执行前");
}
public void after(){
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: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 id="userService" class="com.xx.service.impl.UserServiceImpl"/>
<bean id="bizLogBefore" class="com.xx.log.BizLogBefore"/>
<bean id="bizLogAfter" class="com.xx.log.BizLogAfter"/>
<!-- 配置aop:需要导入aop的约束 -->
<bean id="diy" class="com.xx.diy.DiyPoinCut"/>
<aop:config>
<!-- 自定义切面 ref:要引入的类 -->
<aop:aspect ref="diy">
<!-- 切入点 -->
<aop:pointcut id="myPointcut" expression="execution(* com.xx.service.impl.UserServiceImpl.*(..))"/>
<!-- 通知 -->
<aop:before method="before" pointcut-ref="myPointcut"/>
<aop:after method="after" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
单元测试:
package com.xx.service.test;
import com.xx.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ServiceTest {
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.select();
}
} 3.使用spring注解实现
注解切面方法
package com.xx.diy;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect//标注这个类是一个切面
public class AnnotationPoinCut {
// @Before("execution(* com.xx.service.impl.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前=====");
}
@After("execution(* com.xx.service.impl.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后");
}
@Around("execution(* com.xx.service.impl.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("方法环绕前执行");
Signature signature = joinPoint.getSignature();
Object proceed = joinPoint.proceed();//执行方法
System.out.println("signature:"+signature);
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: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 id="userService" class="com.xx.service.impl.UserServiceImpl"/>
<bean id="bizLogBefore" class="com.xx.log.BizLogBefore"/>
<bean id="bizLogAfter" class="com.xx.log.BizLogAfter"/>
<bean id="annotationPoinCut" class="com.xx.diy.AnnotationPoinCut"/>
<!-- 开启注解支持 JDK(默认 proxy-target-class="false") cglib(proxy-target-class="true") -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
单元测试:
package com.xx.service.test;
import com.xx.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ServiceTest {
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.select();
}
}
七.Spring整合Mybaties
需导入的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
1.普通整合
mybaties配置
<?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>
<package name="com.xx.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/taotao?serverTimezone=UTC"/>
<property name="username" value="username"/>
<property name="password" value="password"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.xx.mapper.UserMapper"/>
</mappers>
</configuration>
实例
package com.xx.pojo;
public class User {
private int id;
private String name;
private int age;
}
UserMapper
package com.xx.mapper;
import com.xx.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> getUsers();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xx.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from taotao.user;
</select>
</mapper>
注意pom文件中配置加载uUerMapper.xml资源
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
单元测试
package com.xx.test;
import com.xx.mapper.UserMapper;
import com.xx.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class MyBatiesTest {
@Test
public void test() throws IOException {
String resources="mybaties-config.xml";
InputStream inputStream= Resources.getResourceAsStream(resources);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.getUsers();
for (User user:users ) {
System.out.println(user);
}
}
}
2.Mybaties-Spring 整合
- 编写数据源配置
- SqlSessionFactiory
- SqlSessionTemplate
- 需要给接口加实现类
- 将自定义的实现类,注入mybaties中
- 单元测试
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- DataSource使用Spring的数据源代替Mybaties的配置 c3p0 dbcp druid
此处我们使用spring提供的JDBC-->
<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/taotao?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="Zh959024371"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定Mybaties配置文件 -->
<property name="configLocation" value="classpath:mybaties-config.xml"/>
<property name="mapperLocations" value="classpath:com/xx/mapper/UserMapper.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<bean id="userMapper" class="com.xx.mapper.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
</beans>
mybaties配置
<?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>
<package name="com.xx.pojo"/>
</typeAliases>
<!-- <settings>
<setting name="" value=""/>
</settings>-->
</configuration>
mapper接口实现类
package com.xx.mapper;
import com.xx.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;
import java.util.List;
public class UserMapperImpl implements UserMapper {
private SqlSessionTemplate sqlSessionTemplate;
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
public List<User> getUsers() {
return sqlSessionTemplate.getMapper(UserMapper.class).getUsers();
}
}
单元测试
package com.xx.test;
import com.xx.mapper.UserMapper;
import com.xx.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SqlSessionTemplateTest {
@Test
public void testSqlSessionTemplate(){
ApplicationContext context=new ClassPathXmlApplicationContext("spring-dao.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.getUsers()) {
System.out.println(user);
}
}
}
八.spring-Transaction
public interface TransactionDefinition {
int getPropagationBehavior(); // 传播行为。
int getIsolationLevel(); // 隔离级别。事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
int getTimeout(); // 事务必须在多少秒内完成。
boolean isReadOnly(); // 事务是否只读。事务管理器能够根据这个返回值进行优化,确保事务是只读的
}
1.Spring事务传播机制
事务的特性
- 原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
- 隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
- 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
2.Spring事务的配置方式
a. 编程式事务管理
编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
b.声明式事务管理
声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
3.事务的传播机制
常用的事务传播机制如下:
- PROPAGATION_REQUIRED Spring默认的传播机制,如果外层有事务, 支持当前事务,如果没有事务会创建一个新的事务
- PROPAGATION_REQUES_NEW 创建一个新的事务并挂起当前事务
- PROPAGATION_SUPPORT 支持当前事务,如果没有事务的话以非事务方式执行
- PROPAGATION_NOT_SUPPORT 以非事务方式执行,如果当前存在事务则将当前事务挂起
- PROPAGATION_NEVER 以非事务方式进行,如果存在事务则抛出异常
- PROPAGATION_MANDATORY 支持当前事务,如果没有事务抛出异常
- PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。
4.事务的隔离级别
a.数据库操作过程中可能出现的不确定情况
- 更新丢失(Lost update) 两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。
- 脏读(Dirty reads) 一个事务读取到了另一个事务未提交的数据操作结果。
- 不可重复读(Non-repeatable Reads)一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
- 幻读(Phantom Reads)事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据。
不可重复读的重点是修改某个记录字段,幻读的重点在于新增或者删除记录。
对于前者, 只需要锁住满足条件的记录。对于后者, 要锁住满足条件及其相近的记录。
“脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。
b.隔离级别
- 未提交读取(Read Uncommitted) Spring标识:ISOLATION_READ_UNCOMMITTED。允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
- 已提交读取(Read Committed) Spring标识:ISOLATION_READ_COMMITTED。允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
- 可重复读取(Repeatable Read) Spring标识:ISOLATION_REPEATABLE_READ。禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
- 序列化(Serializable) Spring标识:ISOLATION_SERIALIZABLE。提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
Spring中同时提供一个标识:ISOLATION_DEFAULT。表示使用后端数据库默认的隔离级别。大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。MySQL的默认隔离级别是Repeatable read。
导入依赖
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>
<version>3.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
</dependencies>
spring配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- DataSource使用Spring的数据源代替Mybaties的配置 c3p0 dbcp druid
此处我们使用spring提供的JDBC-->
<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/taotao?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定Mybaties配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/xx/mapper/UserMapper.xml"/>
</bean>
<!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>-->
<bean id="userMapper" class="com.xx.mapper.UserMapperImpl3">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
public interface TransactionDefinition {
int getPropagationBehavior(); // 传播行为。
int getIsolationLevel(); // 隔离级别。事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
int getTimeout(); // 事务必须在多少秒内完成。
boolean isReadOnly(); // 事务是否只读。事务管理器能够根据这个返回值进行优化,确保事务是只读的
}
-->
<!--更新丢失(Lost update)
两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了。比如CMS系统中,两个同时打开一篇文章进行修改,一个人先保存,另一个人后保存,后保存的就覆盖了先保存的那个人的内容,这就造成更新丢失。
脏读(Dirty reads):一个事务读取到了另一个事务未提交的数据操作结果。
不可重复读(Non-repeatable Reads):一个事务对同一行数据重复读取两次,但是却得到了不同的结果。
幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="select" isolation="READ_COMMITTED" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 注解驱动-->
<!--<tx:annotation-driven/>-->
<!-- 配置aop:需要导入aop的约束 -->
<aop:config>
<!-- 切入点,expression:表达式,execution:(要执行的位置 * * * * *) -->
<aop:pointcut id="pointcut" expression="execution(* com.xx.mapper.*.*(..))"/>
<!-- 执行增强 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
mybatis配置
<?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>
<package name="com.xx.pojo"/>
</typeAliases>
</configuration>
实体类
package com.xx.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
mapper接口
package com.xx.mapper;
import com.xx.pojo.User;
import java.util.List;
public interface UserMapper {
public List<User> getUsers();
public int addUser(User user);
public int deleteUserById(int id);
}
mapper实现类
package com.xx.mapper;
import com.xx.pojo.User;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import java.util.List;
public class UserMapperImpl3 extends SqlSessionDaoSupport implements UserMapper {
public List<User> getUsers() {
User user=new User(4,"马六",35);
UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
mapper.addUser(user);
mapper.deleteUserById(6);
return mapper.getUsers();
}
public int addUser(User user) {
return getSqlSession().getMapper(UserMapper.class).addUser(user);
}
public int deleteUserById(int id) {
return getSqlSession().getMapper(UserMapper.class).deleteUserById(id);
}
}
mapper sql
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xx.mapper.UserMapper">
<select id="getUsers" resultType="user">
select * from taotao.user;
</select>
<insert id="addUser" parameterType="user">
insert into taotao.user (id ,name ,age) values (#{id},#{name},#{age});
</insert>
<delete id="deleteUserById" parameterType="int">
delete from taotao.user where id=#{id};
</delete>
</mapper>
配置单元测试
package com.xx.test;
import com.xx.mapper.UserMapper;
import com.xx.pojo.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TransactionTest {
@Test
public void testConnection(){
ApplicationContext context=new ClassPathXmlApplicationContext("application.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
// userMapper.addUser(new User(5,"王五",30));
// userMapper.deleteUserById(5);
for (User user : userMapper.getUsers()) {
System.out.println(user);
}
}
}