Spring5
1.Spring
1.1、Spring简介
-
Srping是一个轻量级的**控制反转(IoC)和面向切面(AOP)**的容器框架
-
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版本
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<!--Spring和MyBatis整合需要的jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
官网:https://spring.io/projects/spring-framework#overview
下载地址:https://repo.spring.io/release/org/springframework/spring/
GitHub:https://github.com/spring-projects/spring-framework
1.2、优点
- 开源免费
- 轻量级、非侵入式
- 控制反转(IOC)、面向切面(AOP)
- 支持事务的处理、支持其它框架整合(大杂烩)
1.3、组成
2、IOC(控制反转)
将对象的控制权交给用户,业务层不再自己new对象。为用户提供setter器,被动等待接受用户new的对象
3、HelloSpring
3.1、配置元数据基本结构
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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- dao层bean的配置 -->
<bean id="studentDao" class="com.zmy.dao.StudentDao">
</bean>
<bean id="teacherDao" class="com.zmy.dao.TeacherDao">
</bean>
<!-- service层bean的配置 -->
<bean id="personService" class="com.zmy.service.PersonService">
<!--value:基本类型+String ref:复杂类类型-->
<property name="personDao" ref="teacherDao"/>
</bean>
</beans>
3.2、Dao层
public interface PersonDao {
void doSomething();
}
public class StudentDao implements PersonDao{
@Override
public void doSomething() {
System.out.println("学生要学习");
}
}
public class TeacherDao implements PersonDao{
@Override
public void doSomething() {
System.out.println("老师要教学");
}
}
3.3、获取bean并使用
@Test
public void testHello(){
//拿到容器上下文对象
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
PersonService personService = (PersonService) classPathXmlApplicationContext.getBean("personService");
personService.serviceOne();
}
4、Spring配置说明
4.1、alias(别名)
<beans>
<bean id="student" class="com.zmy.pojo.Student">
<constructor-arg name="name" value="18岁的我"/>
<constructor-arg name="age" value="18"/>
</bean>
<alias name="student" alias="ppp"/>
</beans>
测试
public void testHello(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Student student = (Student) applicationContext.getBean("ppp");
System.out.println(student.toString());
}
4.2、Bean
<!--
id:名字
class:类型全限定名 包名+类名
name:别名 可取多个 ',' ' ' ';'都可分割
scope:作用域(不指定时,默认单例) singleton=单例 prototype=原型(每次get会新建一个实例)
-->
<bean id="student" class="com.zmy.pojo.Student" name="student2,student3 student4;student5" scope="singleton">
<constructor-arg type="java.lang.String" value="名字第一个"/>
<constructor-arg type="int" value="1"/>
<constructor-arg type="java.lang.String" value="名字第二个"/>
</bean>
4.3、Import
合并多个xml
<import resource="beans.xml">
5、依赖注入(DI)
5.1、构造器注入
5.1.1、使用无参构造函数构造
默认使用无参构造函数构造,此时需要保证有无参构造函数,否则报错
public class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
<bean id="student" class="com.zmy.pojo.Student">
<!--property设置的值通过set方法赋值-->
<property name="name" value="学生set名字"/>
<property name="age" value="18"/>
</bean>
5.1.2、使用有参构造函数构造
5.1.2.1、通过下标传参
student类,没有无参构造函数
package com.zmy.pojo;
public class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
bean配置
<bean id="student" class="com.zmy.pojo.Student">
<constructor-arg index="0" value="学生构造器名字"/>
<constructor-arg index="1" value="0"/>
</bean>
5.1.2.2、通过类型传参
student类
package com.zmy.pojo;
public class Student{
private String name1;
private String name2;
private int age;
public Student(String name1, String name2,int age) {
this.name1 = name1;
this.name2 = name2;
this.age = age;
}
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name1='" + name1 + '\'' +
"name2='" + name2 + '\'' +
", age=" + age +
'}';
}
}
bean配置
<bean id="student" class="com.zmy.pojo.Student">
<!--每种类型根据前后顺序为第一个String类型参数,第二个String类型参数...-->
<!--第一个出现的String类型,为参数列表中的第一个String类型参数的值-->
<constructor-arg type="java.lang.String" value="名字第一个"/>
<constructor-arg type="int" value="1"/>
<!--第二个出现的String类型,为参数列表中的第二个String类型参数的值-->
<constructor-arg type="java.lang.String" value="名字第二个"/>
</bean>
5.1.2.3、通过参数名传参
student类
package com.zmy.pojo;
public class Student{
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
bean配置
<bean id="student" class="com.zmy.pojo.Student">
<constructor-arg name="name" value="18岁的我"/>
<constructor-arg name="age" value="18"/>
</bean>
总结:在配置文件加载的时候,所有配置的bean都会被创建唯一一个对象。每次get对象都是同一个实例。
5.2、Set方式注入
实体类MyClass
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class MyClass {
private int id;
private String name;
private Properties props;
Teacher teacher;
List<Student> students;
Map<String,Teacher> mapTeacher;
Set<Teacher> teachers;
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
public Map<String, Teacher> getMapTeacher() {
return mapTeacher;
}
public void setMapTeacher(Map<String, Teacher> mapTeacher) {
this.mapTeacher = mapTeacher;
}
public Properties getProps() {
return props;
}
public void setProps(Properties props) {
this.props = props;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
@Override
public String toString() {
return "MyClass{" +
"id=" + id +
", name='" + name + '\'' +
", props=" + props +
", teacher=" + teacher +
", students=" + students +
", mapTeacher=" + mapTeacher +
", teachers=" + teachers +
'}';
}
}
实体类Student
public class Student{
private String name;
private int age;
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name1='" + name + '\'' +
", age=" + age +
'}';
}
}
实体类Teacher
public class Teacher{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
bean配置
基本类型+String
引用的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">
<!-- pojo层bean的配置 -->
<bean id="student" class="com.zmy.pojo.Student" scope="prototype">
<property name="name" value="XXXX"/>
<property name="age" value="18"/>
</bean>
<bean id="teacher" class="com.zmy.pojo.Teacher" scope="prototype">
<property name="name" value="XXXX"/>
<property name="age" value="53"/>
</bean>
<bean id="myClass" class="com.zmy.pojo.MyClass">
<property name="name" value="XXX"/>
<property name="id" value="13"/>
<property name="teacher" ref="teacher"/>
<property name="props">
<props>
<prop key="p1">v1</prop>
<prop key="p2">v2</prop>
</props>
</property>
<property name="students">
<list>
<ref bean="student"/>
<ref bean="student"/>
<ref bean="student"/>
</list>
</property>
<property name="mapTeacher">
<map>
<entry key="teacher1" value-ref="teacher"/>
</map>
</property>
<property name="teachers">
<set>
<ref bean="teacher"/>
</set>
</property>
</bean>
</beans>
输出结果
MyClass{id=13, name='XXX', props={p2=v2, p1=v1}, teacher=Teacher{name='XXXX', age=53}, students=[Student{name1='XXXX', age=18}, Student{name1='XXXX', age=18}, Student{name1='XXXX', age=18}], mapTeacher={teacher1=Teacher{name='XXXX', age=53}}, teachers=[Teacher{name='XXXX', age=53}]}
false
5.3、扩展方式注入
使用p标签或者c标签
p标签=properties c标签=constructor-arg
使用p或者c标签需要导入对应的命名空间
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
<!--需要有无参构造函数-->
<bean id="teacher" class="com.zmy.pojo.Teacher" p:name="XXX" p:age="53">
<!--需要有有参构造函数-->
<bean id="teacher" class="com.zmy.pojo.Teacher" c:name="XXX" c:age="53">
6、Bean的作用域
scope
- singleton 单例,每次get都是同一个实例,默认为单例,容器关闭时销毁
- 在Spring容器创建初始化时加载每一个singleton实例。
- 懒加载(@Lazy):可以设置为容器初始化时候不创建,而在第一次获取bean的时候再创建,以后每次获取不会再创建。
- prototype 原型,每次get都会新创建一个实例,容器不会销毁
- 在每次获取bean的时候new一个新的bean对象放于容器之中,每次都new一个新的。
- request
- session
7、Bean的自动装配
Spring会在上下文中自动寻找bean,自动给bean装配属性
7.1、byName自动装配
<bean id="cat" class="Cat"/>
<bean id="dog" class="Dog"/>
<!--手动set注入装配-->
<bean id="people" class="People">
<property name="name" value="XX">
<property name="cat" ref="cat">
<property name="dog" ref="dog">
</bean>
<!--自动装配-->
<bean id="people" class="People" autowire="byName">
<property name="name" value="XX">
</bean>
byName会自动在容器上下文中寻找set方法指定的beanid
eg:public void setCat(){…} 会寻找id为Cat的bean
7.2、byType自动装配
每个类的bean只能有一个
<!--自动装配-->
<bean id="people" class="People" autowire="byType">
<property name="name" value="XX">
</bean>
7.3、注解实现自动装配
-
Spring从2.5开始支持注解,JDK从1.5开始支持注解
-
使用注解的前提条件
1、导入约束 (context约束)
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
2、开启注解支持
<context:annotation-config/>
3、@Autowired和@Qualifier
在需要自动装配的属性上 或者 set方法上或者构造器上添加**@Autowired**注解,自动寻找xml中的bean(根据类型而非id)
也可以增加@Qualifier(“dog”)注解,则会根据id去寻找对应的bean
4、@Resource(name=“XXX”)
等同于@Autowired + @Qualifier
- 示例代码:
<?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
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/>
<!-- pojo层bean的配置 -->
<bean id="cat" class="com.zmy.pojo.Cat" />
<bean id="dog" class="com.zmy.pojo.Dog" />
<bean id="person" class="com.zmy.pojo.Person">
<property name="name" value="XXX"/>
</bean>
</beans>
package com.zmy.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
private String name;
@Autowired
//寻找类型为Cat的bean
private Cat cat;
@Autowired
//寻找id为dog的bean
@Qualifier("dog")
private Dog dog;
public Person() {
}
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = 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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
8、使用注解开发
使用注解开发,需要增加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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解,并扫描指定包下的注解,可实现注册Bean-->
<context:component-scan base-package="com.zmy.pojo"/>
</beans>
8.1、注册Bean
使用注解**@Component(value = “XXX”)**进行注册Bean,XXX为Bean的id
8.2、属性的注入
使用注解**@Value(“zmy”)**实现简单属性的注入
package com.zmy.pojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Person {
@Value("zmy")
private String name;
@Autowired
private Cat cat;
@Autowired
private Dog dog;
public Person() {
}
public Person(String name, Cat cat, Dog dog) {
this.name = name;
this.cat = cat;
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = 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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", cat=" + cat +
", dog=" + dog +
'}';
}
}
package com.zmy.pojo;
import org.springframework.stereotype.Component;
@Component
public class Dog {
public void shout(){
System.out.println("wang");
}
}
package com.zmy.pojo;
import org.springframework.stereotype.Component;
@Component
public class Cat {
public void shout(){
System.out.println("miao");
}
}
8.3、@Component的衍生注解
在web项目中,由于mvc三层架构,不同层会使用不同的注解注册Bean,功能一致
-
dao层
@Repository
-
service
@Service
-
controller
@Controller
8.4、作用域
@Scope(“singleton”)
9、使用JavaConfig代替xml注册Bean
定义JavaConfig类来代替xml配置文件,JavaConfig类可以做所有xml能够做的配置,增加**@Configuration**注解
9.1、注册Bean、自定义初始化销毁方法
@Bean
- 标注在方法上,注册一个bean
//类型为返回值,id为方法名(或通过value指定)
@Bean(value="bean1")
public Person person(){
return new Person();
}
- 通过initMethod和destroyMethod定义初始化或者销毁方法
//initMethod和destroyMethod定义初始化或者销毁方法
@Bean(value="bean1",initMethod = "f1",destroyMethod = "f2")
public Person person(){
return new Person();
}
- 通过实现接口InitializingBean,DisposableBean的方式,重写初始化(afterPropertiesSet)、销毁(destroy)方法
public class Person implements InitializingBean,DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("diy init...");
}
@Override
public void destroy() throws Exception {
System.out.println("diy destroy...");
}
}
- 通过JSR250注解:@PostConstruct(初始化后)@PreDestroy(销毁前)注解,标注在方法上
public class Dog {
@PostConstruct
public void init(){
System.out.println("PostConstruct...");
}
@PreDestroy
public void destroy(){
System.out.println("PreDestroy...");
}
}
- 通过BeanPostProcessor接口(bean的后置处理器),需要注册该bean。所有bean的初始化前后都会调用
public class MyBeanPostProcessor implements BeanPostProcessor {
//bean的初始化方法调用之前调用该方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization");
return bean;
}
//bean的初始化方法调用之后调用该方法
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization");
return bean;
}
}
BeanPostProcessor工作原理
便利遍历所有的BeanPostProcessor,挨个执行postProcessBeforeInitialization,一旦有一个返回null,就跳出循环,不会继续执行其他的postProcessBeforeInitialization
populateBean//给bean进行属性赋值(调用setter等方法)
initializeBean//初始化
applyBeanPostProcessorsBeforeInitialization
invokeInitMethods//执行初始化方法
applyBeanPostProcessorsAfterInitialization
9.2、条件注入
@Condition
//该类作为@Condition注解的参数注入。
//重写matches方法,当matches返回true时,条件成立并注入,否则不注入
public class WindowsConditions implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if(property.toLowerCase().contains("windows"))
return true;
return false;
}
}
@Conditional(WindowsConditions.class)
public Person person(){
return new Person();
}
9.3、@Import注册
1、直接导入一个或多个类
2、使用ImportSelector
**批量导入多个类:批量导入MyImportSelector中selectImports方法返回的所有bean(MyImportSelector本身不会被导入,检测如果instanceof ImportSelector,那么不注册,进而递归处理selectImports返回的全类名数组)**
-
ConfigrationClassParser.processImports:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
-
ConfigrationClassParser.processConfigurationClass:实际注册一个bean的方法
3、ImportBeanDefinitionRegistrar
手动注册,需要实现ImportBeanDefinitionRegistrar接口,重写registerBeanDefinitions方法
示例代码:
package com.zmy.javaconfig;
import com.zmy.pojo.Dog;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
//配置扫描包
@ComponentScan(value = "com.zmy.pojo")
//导入另外一个配置类
@Import({XXX1.class,XXX2.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})//导入
public class JavaConfig {
//@Bean("dog"),指定名字,不指定默认为函数名
@Bean
public Dog dog01(){
return new Dog();
}
}
//该类必须实现ImportSelector接口,并重写selectImports方法。该方法返回的全类名数组的每一个类,都将被注册一个bean
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
//AnnotationMetadata获取当前注解标注类的所有注解信息
return new String[]{"com.zmy.pojo.Dog","com.zmy.pojo.Person"};
}
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(Person.class);
registry.registerBeanDefinition("personXXX",beanDefinition);
}
}
4、FactoryBean
@Configuration
public class Config1 {
//最终注入到容器中的类型是MyFactoryBean中getObject方法返回的类型的bean
@Bean
public MyFactoryBean myFactoryBean(){
return new MyFactoryBean();
}
}
//该类必须实现FactoryBean接口,重写三个方法
public class MyFactoryBean implements FactoryBean<Person> {
@Override
public boolean isSingleton() {
return true;
}
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return Person.class;
}
}
applicationContext.getBean("myFactoryBean")//获取的bean时Person类型
applicationContext.getBean("&myFactoryBean")//获取的是MyFactoryBean类型的对象
10、AOP(面向切面)
AOP底层是使用代理模式实现的,所以首先要了解代理模式。
代理模式分为:
- 静态代理
- 动态代理
10.1、静态代理
eg:结婚案例。
- 结婚对象 : 实现 结婚接口,实现结婚方法
- 婚庆公司 : 实现结婚接口,拥有结婚对象,结婚方法中调用结婚对象的结婚方法,并且有一些自己的操作
- 结婚接口 :结婚方法
静态代理的好处:
- 使真实对象只需关心核心业务
- 公共业务交给代理去做
静态代理的缺点:
- 每一个真实对象都需要一个代理,代码量翻倍。
10.2、动态代理
学习动态代理之前,先了解两个java.lang.reflect包下的类
-
Proxy 类
- public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException 该方法返回一个interfaces接口的代理类对象
-
InvocationHandler 接口
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
实现了该接口的类的对象,作为参数传递给newProxyInstance方法
-
获取代理工具类
package com.zmy.pojo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的真实对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//通过Proxy.newProxyInstance,返回target的代理类对象 : $Proxy0
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
//1、$Proxy0的代码由JVM生成
//2、$Proxy0有一个私有属性,InvocationHandler h,在Proxy.newProxyInstance的第三个参数,(我们传入的this,即当前类的对象)
// $Proxy0类对象在调用被代理类方法时,内部实现仅调用了h的invoke方法(就是该invoke方法),所以h必须有方法invoke,而我们传入的this当前类对象的类实现了InvocationHandler接口,保证了该点
// invoke调用时传入的参数为参数为:this, 被代理类方法, null
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//调用被代理的真实对象的方法method
Object object = method.invoke(target,args);
after();
return object;
}
private void after() {
System.out.println("结婚后的收尾!");
}
private void before() {
System.out.println("结婚前的准备!");
}
}
Marry接口
public interface Marry {
void HappyMarry();
}
You类
public class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("结婚了!");
}
}
测试类
@Test
public void test(){
You you = new You();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(you);
Marry proxy = (Marry) proxyInvocationHandler.getProxy();
proxy.HappyMarry();
}
参考自:https://www.jb51.net/article/129142.htm
10.3、Spring实现Aop
实现Aop需要导入依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
10.3.1、使用Spring的API接口(implements XXXAdvice,自动识别为切面类)
1、业务类及接口
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.zmy.service;
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 select() {
System.out.println("查询了一个用户");
}
}
2、执行环绕类
- 必须直接或者间接继承Advice接口
- MethodBeforeAdvice为前置增加,MethodAfterAdvice为后置增加。。。
package com.zmy.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before:" + method.getName());
}
}
3、配置文件,需要导入aop的约束。
-
xmlns:aop=“http://www.springframework.org/schema/aop”
-
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
<?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.zmy.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.zmy.log.BeforeLog"/>
<!--aop配置-->
<aop:config>
<!--配置切入点,expression表达式-->
<aop:pointcut id="pointcut" expression="execution(* com.zmy.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加,advice-ref引用的执行环绕类,必须直接或者间接继承Advice接口 pointcut-ref引用的切点-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
4、测试类
@Test
public void testHello(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
}
expression表达式
10.3.2、自定义类(无需实现原有接口,手动配置为切面类)
1、业务类及接口
public interface UserService {
void add();
void delete();
void update();
void select();
}
package com.zmy.service;
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 select() {
System.out.println("查询了一个用户");
}
}
2、执行环绕类
public class Log {
public void before(){
System.out.println("before method");
}
public void after(){
System.out.println("after method");
}
}
3、配置文件
<?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">
<!--<context:component-scan base-package="com.zmy.pojo"/>-->
<!--<context:annotation-config/>-->
<bean id="userService" class="com.zmy.service.UserServiceImpl"/>
<bean id="log" class="com.zmy.log.Log"/>
<aop:config>
<aop:pointcut id="pointcut1" expression="execution(* com.zmy.service.UserServiceImpl.*(..))"/>
<!--使用aop:aspect标签定义一个切面类,该类的方法可以配置为前置增强方法或后置。。。-->
<aop:aspect ref="log">
<!--配置前置增强方法-->
<aop:before method="before" pointcut-ref="pointcut1"/>
<!--配置后置增强方法-->
<aop:after method="after" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>
</beans>
4、测试
@Test
public void testHello(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.add();
}
10.3.3、使用注解实现
重要!!!需要开启自动扫描切面类注解配置:aop:aspectj-autoproxy/
1、切面类
@Aspect
public class AnnocationPointCut {
//第一种方式,指定哪些方法被切入
@Around("execution(* com.zmy.service.UserServiceImpl.*(..))")
//环绕增强可以使用参数ProceedingJoinPoint,获取当前执行的函数签名信息,日志可用
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
jp.proceed();
System.out.println(jp.getSignature());
System.out.println("环绕后");
}
@Before("execution(* com.zmy.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("执行前");
}
@After("execution(* com.zmy.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("执行后");
}
//第二种方式,被该注解(InsertValidate)标注的方法被切入,可以在controller层方法标注改注解
@Pointcut("@annotation(com.zmy.InsertValidate)")
public void pointcut() {}
@Around("pointcut()")
public Result<List<InsReceiptResult>> around(ProceedingJoinPoint point) {
Object[] args = point.getArgs();
if (args.length > 1) {
throw new IllegalArgumentException("@InsertValidate使用位置错误");
}
Object proceed = null;
Object arg = args[0];
if (arg instanceof InsReceiptParam) {
InsReceiptParam param = (InsReceiptParam) arg;
//修改新增参数
args[0] = insertValidService.validateInsertField(param);
try {
proceed = point.proceed(args);
} catch (Throwable throwable) {
logger.error(throwable.toString());
if (throwable instanceof RuntimeException) {
throw (RuntimeException) throwable;
} else {
System.out.println(throwable.toString());
}
}
}
return (Result<List<InsReceiptResult>>) proceed;
}
}
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InsertValidate {
String value() default "";
}
2、业务类和接口
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 select() {
System.out.println("查询了一个用户");
}
}
public interface UserService {
void add();
void delete();
void update();
void select();
}
3、配置文件
<?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">
<!--开启自动扫描切面注解
proxy-target-class="false" : 默认为false,JDK基于接口方式实现的动态代理
proxy-target-class="true" : cglib基于子类方式实现的动态代理
-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
<bean id="userService" class="com.zmy.service.UserServiceImpl"/>
<!--注册该Bean的时候,扫描到切面类注解配置-->
<bean id="annocationPointCut" class="com.zmy.log.AnnocationPointCut"/>
</beans>
4、测试
@Test
public void testHello() throws NoSuchMethodException {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.delete();
}
11、Spring整合MyBatis
mybatis-spring.jar主要为整合作用
1、需要用到的jar包依赖
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
2、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>
<!--<settings>-->
<!--<setting name="logImpl" value="LOG4J"/>-->
<!--</settings>-->
<typeAliases>
<package name="com.zmy.pojo"/>
</typeAliases>
</configuration>
3、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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置DataSource,由mybatis的数据源转为使用Spring的驱动管理数据源(DriverManagerDataSource),MyBatis数据源配置去掉-->
<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/school?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--使用Spring的驱动管理数据源(DriverManagerDataSource)-->
<property name="dataSource" ref="dataSource"/>
<!--注册Mapper-->
<property name="mapperLocations" value="classpath:com/zmy/mapper/*Mapper.xml"/>
<!--绑定mybatis配置文件,配置文件移除了数据源,移除了mapper的注册-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
4、Mapper接口和配置文件
public interface UserMapper {
List<User> selectUsers();
}
<?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.zmy.mapper.UserMapper">
<select id="selectUsers" resultType="user">
select * from user;
</select>
</mapper>
5、实体类如下
public class User {
private int id;
private String name;
private String pwd;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
6、测试
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SqlSessionTemplate sqlSession = context.getBean("sqlSession", SqlSessionTemplate.class);
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.selectUsers();
for (User user : users) {
System.out.println(user);
}
}
12、Spring事务
Spring可选的事务管理器有
12.1、 编程式事务管理
编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
12.2、 声明式事务管理
声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
代码示例:
1、使用xml配置
测试:
需要导入Spring-test包,否则未加载spring配置文件,未创建容器,@Autowired不能使用
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class MyTest {
@Autowired
UserService userService;
@Test
public void test(){
userService.handleUser();
}
}
service
@Service
public class UserService {
@Autowired
SqlSessionTemplate sqlSession;
public void handleUser(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.addUser(new User(4,"zhangyiming","999999"));
userMapper.deleteUser(3);
}
}
mapper
public interface UserMapper {
List<User> selectUsers();
int addUser(User user);
int deleteUser(@Param("id")int id);
}
<?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.zmy.mapper.UserMapper">
<select id="selectUsers" resultType="user">
select * from user;
</select>
<insert id="addUser" parameterType="user">
insert into school.user(id, name, pwd) values(#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">
delete from school.user where id = #{id}
</delete>
</mapper>
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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zmy"/>
<context:annotation-config/>
<!--配置DataSource,由mybatis的数据源转为使用Spring的驱动管理数据源(DriverManagerDataSource),MyBatis数据源配置去掉-->
<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/school?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--使用Spring的驱动管理数据源(DriverManagerDataSource)-->
<property name="dataSource" ref="dataSource"/>
<!--注册Mapper-->
<property name="mapperLocations" value="classpath:com/zmy/mapper/*Mapper.xml"/>
<!--绑定mybatis配置文件,配置文件移除了数据源,移除了mapper的注册-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--配置声明式事务-->
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--使用jdbc的数据源和jdbc的数据源事务管理器-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--通知,我的理解是增加try catch块,关闭自动提交,进行rollback回滚或者commit的增强操作-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes >
<!--rollback-for/no-rollback-for:设置回滚/不回滚的异常类型-->
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<!--事务切入-->
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.zmy.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
2、使用注解实现
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/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
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zmy"/>
<context:annotation-config/>
<!--配置DataSource,由mybatis的数据源转为使用Spring的驱动管理数据源(DriverManagerDataSource),MyBatis数据源配置去掉-->
<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/school?characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--使用Spring的驱动管理数据源(DriverManagerDataSource)-->
<property name="dataSource" ref="dataSource"/>
<!--注册Mapper-->
<property name="mapperLocations" value="classpath:com/zmy/mapper/*Mapper.xml"/>
<!--绑定mybatis配置文件,配置文件移除了数据源,移除了mapper的注册-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--配置sqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<!--配置声明式事务-->
<!--事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--使用jdbc的数据源和jdbc的数据源事务管理器-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--增加对注解的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
在方法上使用@Transactional注解标注为事务
@Transactional
public void handleUser(){
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.addUser(new User(4,"zhangyiming","999999"));
userMapper.deleteUser(3);
}
12.3、事务的传播机制
事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。
常用的事务传播机制如下:
- PROPAGATION_REQUIRED
Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行 - PROPAGATION_REQUES_NEW
该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可 - PROPAGATION_SUPPORT
如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务 - PROPAGATION_NOT_SUPPORT
该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码 - PROPAGATION_NEVER
该传播机制不支持外层事务,即如果外层有事务就抛出异常 - PROPAGATION_MANDATORY
与NEVER相反,如果外层没有事务,则抛出异常 - PROPAGATION_NESTED
该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
12.4、事务的隔离级别
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED(读未提交) | 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。 |
ISOLATION_READ_COMMITTED(读已提交) | (Oracle 默认级别)允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。 |
ISOLATION_REPEATABLE_READ(可重复读) | (MYSQL默认级别)对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。 |
ISOLATION_SERIALIZABLE(串行化) | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。 |