Spring 框架概述
Spring 使创建 Java 企业应用程序变得容易。它提供了在企业环境中使用 Java 语言所需的一切,并支持 Groovy 和 Kotlin 作为 JVM 上的替代语言,并且可以根据应用程序的需求灵活地创建多种体系结构。从 Spring Framework 5.0 开始,Spring 需要 JDK 8(Java SE 8),并且已经为 JDK 9 提供了现成的支持。 Spring 是分层的 Java SE/EE full-stack 轻量级开源框架,以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核,使用基本的 JavaBean 完成以前只可能由 EJB 完成的工作,取代了 EJB 臃肿和低效的开发模式。 Spring 是开源的。它拥有一个庞大而活跃的社区,可以根据各种实际用例提供持续的反馈。这帮助 Spring 在很长一段时间内成功地 Developing 了。
Spring优点
方便解耦,简化开发 Spring 就是一个大工厂,可以将所有对象的创建和依赖关系的维护交给 Spring 管理。 方便集成各大优秀框架 Spring 不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如 Struts2、Hibernate、MyBatis 等)的直接支持。 方便程序的测试 Spring 支持 JUnit4,可以通过注解方便地测试 Spring 程序。 AOP 编程的支持 Spring 提供面向切面编程,可以方便地实现对程序进行权限拦截和运行监控等功能。 声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无须手动编程。
Spring体系结构
Spring 框架采用分层架构,根据不同的功能被划分成了多个模块,这些模块大体可分为 Data Access/Integration、Web、AOP、Aspects、Messaging、Instrumentation、Core Container 和 Test,具体如下图所示:
Data Access/Integration(数据访问/集成)
数据访问/集成层包括 JDBC、ORM、OXM、JMS 和 Transactions 模块,具体介绍如下。
JDBC 模块:提供了一个 JDBC 的抽象层,大幅度减少了在开发过程中对数据库操作的编码。
ORM 模块:对流行的对象关系映射 API,包括 JPA、JDO、Hibernate 和 iBatis 提供了的集成层。
OXM 模块:提供了一个支持对象/XML 映射的抽象层实现,如 JAXB、Castor、XMLBeans、JiBX 和 XStream。
JMS 模块:指 Java 消息服务,包含的功能为生产和消费的信息。
Transactions 事务模块:支持编程和声明式事务管理实现特殊接口类,并为所有的 POJO。
[/code]
Web 模块
Spring 的 Web 层包括 Web、Servlet、Struts 和 Portlet 组件,具体介绍如下。
Web 模块:提供了基本的 Web 开发集成特性,例如多文件上传功能、使用的 Servlet 监听器的 IoC 容器初始化以及 Web 应用上下文。
Servlet模块:包括 Spring 模型—视图—控制器(MVC)实现 Web 应用程序。
Struts 模块:包含支持类内的 Spring 应用程序,集成了经典的 Struts Web 层。
Portlet 模块:提供了在 Portlet 环境中使用 MV C实现,类似 Web-Servlet 模块的功能。
[/code]
Core Container(核心容器)
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言。
[/code]
其他模块
Spring的其他模块还有 AOP、Aspects、Instrumentation 以及 Test 模块,具体介绍如下。
AOP 模块:提供了面向切面编程实现,允许定义方法拦截器和切入点,将代码按照功能进行分离,以降低耦合性。
Aspects 模块:提供与 AspectJ 的集成,是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块:提供了类工具的支持和类加载器的实现,可以在特定的应用服务器中使用。
Test 模块:支持 Spring 组件,使用 JUnit 或 TestNG 框架的测试。
[/code]
Spring拓展
Spring Boot与Spring Cloud
Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务。 Spring Cloud是基于Spring Boot实现的。 Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架。 Spring Boot使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置 , Spring Cloud很大的一部分是基于Spring Boot来实现,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。 SpringBoot在SpringClound中起到了承上启下的作用,如果你要学习SpringCloud必须要学习SpringBoot。
Spring IoC 容器 (IoC 也称为依赖项注入(DI),或DI是实现IoC的一种方法)
IoC容器概述
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入。 Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。 Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext。
BeanFactory beanFactory是一个Factory,用于管理bean的,有了一个Spring的beanFactory,我们就可以从spring中获取注册到其中的bean来使用。
ApplicationContext ApplicationContext 是 BeanFactory 的子接口,也被称为应用上下文。该接口的全路径为:org.springframework.context.ApplicationContext,它不仅提供了 BeanFactory 的所有功能,还添加了对 i18n(国际化)、资源访问、事件传播等方面的良好支持。 ApplicationContext 接口有两个常用的实现类:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext。 ClassPathXmlApplicationContext从类路径 ClassPath 中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作,具体如下所示。ApplicationContext applicationContext = new ClassPathXmlApplicationContext(String configLocation);
configLocation 参数用于指定 Spring 配置文件的名称和位置,如 applicationContext.xml。FileSystemXmlApplicationContext从指定的文件系统路径中寻找指定的 XML 配置文件,找到并装载完成 ApplicationContext 的实例化工作,具体如下所示。ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);
它与 ClassPathXmlApplicationContext 的区别是:在读取 Spring 的配置文件时,FileSystemXmlApplicationContext 不再从类路径中读取配置文件,而是通过参数指定配置文件的位置,它可以获取类路径之外的资源,如“D:/workspaces/applicationContext.xml”。
BeanFactory 和 ApplicationContext区别: BeanFactory在初始化容器时,并未实例化Bean,直到第一次访问某个Bean 时才实例目标Bean;而ApplicationContext 则在初始化应用上下文时就实例化所有单实例的Bean 。 在实际开发中,通常都选择使用 ApplicationContext,而只有在系统资源较少时,才考虑使用 BeanFactory。 (但是,它们都是通过 XML 配置文件加载 Bean 的。)
Spring入门程序
创建maven项目 在pom.xml导入jar包依赖
< dependencies>
< dependency>
< groupId> org.springframework groupId>
< artifactId> spring-webmvc artifactId>
< version> 5.2.12.RELEASE version>
dependency>
< dependency>
< groupId> junit groupId>
< artifactId> junit artifactId>
< version> 4.12 version>
dependency>
dependencies>
[/code]
编写接口
package com. xxx. mapper ;
public interface UserMapper {
public void hello ( ) ;
}
[/code]
编写接口实现类
package com. xxx. mapper ;
public class UserMapperImpl implements UserMapper {
@Override
public void hello ( ) {
System . out. println ( "Spring入门程序!" ) ;
}
}
[/code]
编写Spring核心配置文件applicationContext.xml
< 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
http://www.springframework.org/schema/beans/spring-beans.xsd" >
< beans>
< bean id = " hello" class = " com.xxx.mapper.UserMapperImpl" >
bean>
beans>
beans>
[/code]
测试
package com. xxx. mapper ;
import org. junit. Test ;
import org. springframework. context. ApplicationContext ;
import org. springframework. context. support. ClassPathXmlApplicationContext ;
public class helloTest {
@Test
public void helloTest1 ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
UserMapper hello = context. getBean ( "hello" , UserMapper . class ) ;
hello. hello ( ) ;
}
}
[/code]
测试结果
IoC创建对象的三种方式
编写实体类User:
public class User {
private String name;
public void setName ( String name) {
this . name= name;
}
public User ( ) {
System . out. println ( "无参构造方法执行了!" ) ;
}
public void print ( ) {
System . out. println ( "学生名字为:" + name) ;
}
}
[/code]
编写Spring核心配置文件:
< bean id = " user" class = " com.xxx.pojo.User" >
< property name = " name" value = " 小新" />
bean>
[/code]
测试以及结果:
@Test
public void helloTest2 ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
User user = context. getBean ( "user" , User . class ) ;
user. print ( ) ;
}
[/code]
编写实体类User:
public class User {
private String name;
public String getName ( ) {
return name;
}
public User ( String name) {
System . out. println ( "有参构造方法执行了!" ) ;
this . name = name;
}
public void print ( ) {
System . out. println ( "学生名字为:" + name) ;
}
}
[/code]
编写Spring核心配置文件:
< bean id = " user" class = " com.xxx.pojo.User" >
< constructor-arg value = " 小新2" index = " 0" />
bean>
[/code]
测试以及结果:
@Test
public void helloTest2 ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
User user = context. getBean ( "user" , User . class ) ;
user. print ( ) ;
}
[/code]
拓展:Spring核心配置文件有三种写法:
< bean id = " user" class = " com.xxx.pojo.User" >
< constructor-arg index = " 0" value = " 小新-index属性(0开始,按顺序)" />
< constructor-arg name = " name" value = " 小新-name属性" />
< constructor-arg type = " java.lang.String" value = " 小新-参数类型" />
bean>
[/code]
结果展示:
编写工厂类:
public class Factory {
public static User getStaticInstance ( ) {
return new User ( "小新2——静态方法创建对象" ) ;
}
public User getInstance ( ) {
return new User ( "小新3-实例方法创建对象" ) ;
}
}
[/code]
编写Spring核心配置文件:
< bean id = " factory" class = " com.xxx.mapper.Factory" />
< bean id = " staticFactory-user" class = " com.xxx.mapper.Factory" factory-method = " getStaticInstance" />
< bean id = " factory-user" factory-bean = " factory" factory-method = " getInstance" />
[/code]
测试以及结果: 静态方法:
@Test
public void helloTest4 ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
User user = context. getBean ( "staticFactory-user" , User . class ) ;
user. print ( ) ;
}
[/code]
实例方法:
@Test
public void helloTest3 ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
User user = context. getBean ( "factory-user" , User . class ) ;
user. print ( ) ;
}
[/code]
Spring依赖注入(DI)和Bean的作用域
什么是依赖注入:Spring 容器在创建被调用者的实例时,会自动将调用者需要的对象实例注入给调用者,这样,调用者通过 Spring 容器获得被调用者实例。
依赖注入主要有两种实现方式,分别是属性 setter 注入和构造方法注入,其中setter注入要求重点掌握。
属性 setter 注入(重点展开讲解) 指 IoC 容器使用 setter 方法注入被依赖的实例。通过调用无参构造器或无参 static 工厂方法实例化 bean 后,调用该 bean 的 setter 方法,即可实现基于 setter 的 DI。 构造方法注入 指 IoC 容器使用构造方法注入被依赖的实例。基于构造器的 DI 通过调用带参数的构造方法实现,每个参数代表一个依赖。
属性 setter 注入讲解: 环境搭建:(创建一个Student和Book类):
Student
package com. xxx. pojo ;
import java. util. * ;
public class Student {
private String name;
private Book book;
private String [ ] course;
private List < String > hobbies;
private Map < String , String > card;
private Set < String > fruit;
private String marriage;
private Properties info;
public Student ( ) {
}
public Student ( String name, Book book, String [ ] course, List < String > hobbies, Map < String , String > card, Set < String > fruit, String marriage, Properties info) {
this . name = name;
this . book = book;
this . course = course;
this . hobbies = hobbies;
this . card = card;
this . fruit = fruit;
this . marriage = marriage;
this . info = info;
}
@Override
public String toString ( ) {
return "Student{" +
"name='" + name + '\'' +
", book=" + book +
", course=" + Arrays . toString ( course) +
", hobbies=" + hobbies +
", card=" + card +
", fruit=" + fruit +
", marriage='" + marriage + '\'' +
", info=" + info +
'}' ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public Book getBook ( ) {
return book;
}
public void setBook ( Book book) {
this . book = book;
}
public String [ ] getCourse ( ) {
return course;
}
public void setCourse ( String [ ] course) {
this . course = course;
}
public List < String > getHobbies ( ) {
return hobbies;
}
public void setHobbies ( List < String > hobbies) {
this . hobbies = hobbies;
}
public Map < String , String > getCard ( ) {
return card;
}
public void setCard ( Map < String , String > card) {
this . card = card;
}
public Set < String > getFruit ( ) {
return fruit;
}
public void setFruit ( Set < String > fruit) {
this . fruit = fruit;
}
public String getMarriage ( ) {
return marriage;
}
public void setMarriage ( String marriage) {
this . marriage = marriage;
}
public Properties getInfo ( ) {
return info;
}
public void setInfo ( Properties info) {
this . info = info;
}
}
[/code]
Book
package com. xxx. pojo ;
public class Book {
private String name;
private int id;
public Book ( ) {
}
@Override
public String toString ( ) {
return "Book{" +
"name='" + name + '\'' +
", id=" + id +
'}' ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public int getId ( ) {
return id;
}
public void setId ( int id) {
this . id = id;
}
public Book ( String name, int id) {
this . name = name;
this . id = id;
}
}
[/code]
常量注入:
< bean class = " com.xxx.pojo.Student" id = " student" >
< property name = " name" value = " 小新" />
bean>
[/code]
Bean注入:
< bean class = " com.xxx.pojo.Book" id = " book" >
< property name = " name" value = " Java放弃" />
< property name = " id" value = " 100" />
bean>
< bean class = " com.xxx.pojo.Student" id = " student" >
< property name = " book" ref = " book" />
bean>
[/code]
数组注入:
< property name = " course" >
< array>
< value> 高数 value>
< value> 计算机网络 value>
< value> 数据库 value>
array>
property>
[/code]
List注入:
< property name = " hobbies" >
< list>
< value> 唱 value>
< value> 跳 value>
< value> Rap value>
list>
property>
[/code]
Map注入:
< property name = " card" >
< map>
< entry key = " 银行卡:" value = " 2501314" />
< entry key = " 身份证:" value = " 1314520" />
map>
property>
[/code]
Set注入:
< property name = " fruit" >
< set>
< value> 香蕉 value>
< value> 苹果 value>
< value> 雪梨 value>
set>
property>
[/code]
Null注入:
< property name = " marriage" >
< null/>
property>
[/code]
Properties注入:
< property name = " info" >
< props>
< prop key = " username" > 小新 prop>
< prop key = " password" > 520 prop>
props>
property>
[/code]
测试及结果展示:
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Student student = context. getBean ( "student" , Student . class ) ;
System . out. println ( student) ;
}
}
[/code]
Student{name=‘小新’, book=Book{name=‘Java放弃’, id=100}, course=[高数, 计算机网络, 数据库], hobbies=[唱, 跳, Rap], card={银行卡:=2501314, 身份证:=1314520}, fruit=[香蕉, 苹果, 雪梨], marriage=‘null’, info={password=520, username=小新}}
Process finished with exit code 0
p命名空间(以Book类举例) 导入约束 xmlns:p=“http://www.springframework.org/schema/p”
< bean id = " pBook" class = " com.xxx.pojo.Book" p: name= " Java懵懂" p: id= " 250" />
[/code]
测试及结果:
@Test
public void cpTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Book pBook = context. getBean ( "pBook" , Book . class ) ;
System . out. println ( pBook) ;
}
[/code]
c命名空间 导入约束 xmlns:c=“http://www.springframework.org/schema/c”
< bean id = " cBook" class = " com.xxx.pojo.Book" c: id= " 520" c: name= " Java入坑" />
[/code]
测试及结果:
@Test
public void cpTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Book cBook = context. getBean ( "cBook" , Book . class ) ;
System . out. println ( cBook) ;
}
[/code]
作用域种类
singleton(以Book举例) 单例模式,使用 singleton 定义的 Bean 在 Spring 容器中只有一个实例,这也是 Bean 默认的作用域。
< bean class = " com.xxx.pojo.Book" id = " scopeBook" scope = " singleton" >
< property name = " id" value = " 1" />
bean>
[/code]
@Test
public void scopeTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Book book1 = context. getBean ( "scopeBook" , Book . class ) ;
Book book2 = context. getBean ( "scopeBook" , Book . class ) ;
System . out. println ( book1. hashCode ( ) ) ;
System . out. println ( book2. hashCode ( ) ) ;
System . out. println ( book1== book2) ;
}
}
[/code]
prototype 原型模式,每次通过 Spring 容器获取 prototype 定义的 Bean 时,容器都将创建一个新的 Bean 实例,即每次调用getBean()时,相当于执行了一次new XxxBean()。
< bean class = " com.xxx.pojo.Book" id = " scopeBook" scope = " prototype" >
< property name = " id" value = " 1" />
bean>
[/code]
@Test
public void scopeTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Book book1 = context. getBean ( "scopeBook" , Book . class ) ;
Book book2 = context. getBean ( "scopeBook" , Book . class ) ;
System . out. println ( book1. hashCode ( ) ) ;
System . out. println ( book2. hashCode ( ) ) ;
System . out. println ( book1== book2) ;
}
}
[/code]
request 在一次 HTTP 请求中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Request 内有效。 session 在同一个 HTTP Session 中,容器会返回该 Bean 的同一个实例。而对不同的 HTTP 请求,会返回不同的实例,该作用域仅在当前 HTTP Session 内有效。 global Session 在一个全局的 HTTP Session 中,容器会返回该 Bean 的同一个实例。该作用域仅在使用 portlet context 时有效。
Spring 常用配置及属性
Spring自动装配
Bean 的装配可以理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种形式的 Bean的装配方式,如基于 XML 的 Bean 装配、基于Annotation 的 Bean 装配和自动装配等。之前的举例是通过XML的Bean装配的。接下来讲解自动装配。 自动装配就是指 Spring 容器可以自动装配(autowire)相互协作的 Bean 之间的关联关系,将一个 Bean 注入其他 Bean 的 Property 中。 Spring的自动装配需要从两个角度来实现: 1.组件扫描(component scanning) :spring会自动发现应用上下文中所创建的bean; 2.自动装配(autowiring) :spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
autowire 的属性和作用
环境搭建:(分别创建一个Student和Student2类,再创建一个Teacher类)
public class Student {
public void study ( ) {
System . out. println ( "Student类的方法study执行了" ) ;
}
}
[/code]
public class Student2 {
public void study ( ) {
System . out. println ( "Student2类的方法study执行了" ) ;
}
}
[/code]
public class Teacher {
private Student student;
private Student2 student2;
private String teach;
public Teacher ( ) {
}
@Override
public String toString ( ) {
return "Teacher{" +
"student=" + student +
", student2=" + student2 +
", teach='" + teach + '\'' +
'}' ;
}
public Student getStudent ( ) {
return student;
}
public void setStudent ( Student student) {
this . student = student;
}
public Student2 getStudent2 ( ) {
return student2;
}
public void setStudent2 ( Student2 student2) {
this . student2 = student2;
}
public String getTeach ( ) {
return teach;
}
public void setTeach ( String teach) {
this . teach = teach;
}
public Teacher ( Student student, Student2 student2, String teach) {
this . student = student;
this . student2 = student2;
this . teach = teach;
}
}
[/code]
使用autowire=“byName”:
< bean class = " com.xxx.pojo.Student" id = " student" />
< bean class = " com.xxx.pojo.Student" id = " student" />
< bean class = " com.xxx.pojo.Student2" id = " student2" />
< bean class = " com.xxx.pojo.Teacher" id = " teacher" autowire = " byName" >
< property name = " teach" value = " Java" />
bean>
[/code]
测试及结果:
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Teacher teacher = context. getBean ( "teacher" , Teacher . class ) ;
teacher. getStudent ( ) . study ( ) ;
teacher. getStudent2 ( ) . study ( ) ;
}
}
[/code]
若修改Student的bean id值不为student,如:
则会报空指针异常java.lang.NullPointerException at BeanTest.beanTest(BeanTest.java:24)
。因为按byName规则找不对应set方法,真正的setStudent就没执行,对象就没有初始化,所以调用时就会报空指针错误。
当一个bean节点带有 autowire byName的属性时:
将查找其类中所有的set方法名,例如setStudent,获得将set去掉并且首字母小写的字符串,即student。 去spring容器中寻找是否有此字符串名称id的对象,如果有,就取出注入;如果没有,就报空指针异常。
Spring注解开发
在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
[/code]
开启属性注解支持!
< context: annotation-config/>
[/code]
编写一个 Student类
public class Student {
private String name;
@Override
public String toString ( ) {
return "Student{" +
"name='" + name + '\'' +
'}' ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public Student ( ) {
}
public Student ( String name) {
this . name = name;
}
}
[/code]
编写Spring核心配置文件:
< bean class = " com.xxx.pojo.Student" id = " student" >
< property name = " name" value = " 小新" />
bean>
[/code]
测试及结果:
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Student student = context. getBean ( "student" , Student . class ) ;
System . out. println ( student) ;
[/code]
使用@Configuration和@Bean给容器中注册组件
编写一个配置类
@Configuration
public class AnnotationStudent {
@Bean
public Student student ( ) {
return new Student ( "小新" ) ;
}
}
[/code]
测试及结果:
@Test
public void beanTest ( ) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext ( AnnotationStudent . class ) ;
Student bean = context. getBean ( Student . class ) ;
String [ ] namesForType = context. getBeanNamesForType ( Student . class ) ;
for ( String s : namesForType) {
System . out. println ( s) ;
}
System . out. println ( bean) ;
}
}
[/code]
若在配置类中给@Bean设置一个value值,如@Bean("stu")
则测试结果为: 则我们在使用注解方式向Spring的IOC容器中注入JavaBean时,如果没有在@Bean注解中明确指定bean的名称,那么就会使用当前方法的名称来作为bean的名称;如果在@Bean注解中明确指定了bean的名称,那么就会使用@Bean注解中指定的名称来作为bean的名称。
使用@ComponentScan自动扫描组件并指定扫描规则 开启注解扫描,并删除之前配置文件中的bean
< context: component-scan base-package = " com.xxx" />
[/code]
在原有环境下创建一个com.xxx.service包,并创建一个Teacher类,并在类上添加一个@Service注解,同时,之前的Student类上也添加一个@Component注解
@Service
public class Teacher {
private Student student;
public void teach ( ) {
System . out. println ( "教授的学生是" + student) ;
}
@Override
public String toString ( ) {
return "Teacher{" +
"student=" + student +
'}' ;
}
public Student getStudent ( ) {
return student;
}
public void setStudent ( Student student) {
this . student = student;
}
public Teacher ( Student student) {
this . student = student;
}
public Teacher ( ) {
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
String [ ] beanDefinitionNames = context. getBeanDefinitionNames ( ) ;
for ( String definitionName : beanDefinitionNames) {
System . out. println ( definitionName) ;
}
}
}
[/code]
以上可以看到:在配置注解扫描后,只要在com.xxx包下的所有子包中,加上了@Repository(Dao)、@Service(service)、@Controller、(web)@Component注解的类都会被扫描到,并自动注入到Spring容器中。(其实上面四个功能,目前为止是一样的)
使用注解配置XML包扫描 我们可以在配置类中(前面的AnnotationStudent)使用@ComponentScan注解配置包扫描,由此代替xml中的
。先注释掉之前的xml方式的注解扫描,接着
@Configuration
@ComponentScan ( value = "com.xxx" )
public class AnnotationStudent {
@Bean
public Student student ( ) {
return new Student ( "小新" ) ;
}
}
[/code]
测试结果跟之前一样。因此,推荐以后都使用注解扫描就好了,Spring还是尽量用注解开发,MyBatis中还是用xml配置文件。
ComponentScan方法使用 excludeFilters()不包含哪些包、includeFilters()包含哪些包,使用includeFilters时,需要在XML配置文件中先配置use-default-filters="false"
,即禁用默认的扫描所有包过滤规则才能生效。另外,ComponentScan还是一个可重复注解的注解,因此可以在一个类上重复使用这个注解。
使用@Scope注解设置组件的作用域 通过在类中添加注解@scope注解设置作用域,如:
@Configuration
public class AnnotationStudent {
@Scope ( "prototype" )
@Bean
public Student student ( ) {
return new Student ( "小新" ) ;
}
}
[/code]
如果为false。
@Scope注解中的取值如下所示:
注解自动装配组件(@Resource是JDK自带的) @Autowired @Autowired注解可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。@Autowired注解可以放在类、接口以及方法上。等价于
@Autowired注解默认是优先按照类型去容器中找对应的组件,即:context.getBean(类名.class);
,如果找到多个相同类型的组件,那么是将属性名称作为组件的id,到IOC容器中进行查找,即:context.getBean("组件的id");
@Qualifier @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配,且Qualifier不能单独使用。
@Resource 是JDK自带的注解 可以按名称注入也可以按类型注入,默认是按名称注入,没有显式指定名称时,在spring容器中匹配与需要注入的bean属性名相同的bean,如果还不同,@Resource会找到一个主类型匹配而不是一个特定的命名bean。
懒加载@Lazy 懒加载就是Spring容器启动的时候,先不创建对象,在第一次使用(获取)bean的时候Xxx xxx = context.getBean(Xxx.class);
再来创建对象,并进行一些初始化。使用时,只需要在配置类的方法上加上@Lazy注解即可。
public class AnnotationStudent {
@Lazy
@Bean
public Student student ( ) {
System . out. println ( "在容器中添加对象!" ) ;
return new Student ( "小新" ) ;
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
System . out. println ( "容器创建完成!" ) ;
Student student = context. getBean ( Student . class ) ;
Student student1 = context. getBean ( Student . class ) ;
System . out. println ( student== student1) ;
}
}
[/code]
非懒加载模式(默认情况):bean在Spring容器启动的时候ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
就会被创建,并且还加载到Spring容器中去了。
@Configuration
public class AnnotationStudent {
@Bean
public Student student ( ) {
System . out. println ( "在容器中添加对象!" ) ;
return new Student ( "小新" ) ;
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
System . out. println ( "容器创建完成!" ) ;
}
}
[/code]
使用@Import注解给容器中快速导入一个组件 注册bean的方式通常有以下几种: 1.包扫描+给组件标注注解(@Controller、@Servcie、@Repository、@Component 2.@Bean注解 3.@Import注解(只作用在类上,可以在实际开发项目中导入别人的类并注册到容器中,这是两外两种无法做到的)例如在AnnotationStudent配置类上导入Teacher类对应的bean实例(id默认是组件的全类名) 4.使用FactoryBean接口(支持泛式)向Spring容器中注册bean
@Configuration
@Import ( Teacher . class )
public class AnnotationStudent {
@Bean
public Student student ( ) {
return new Student ( "小新" ) ;
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ( AnnotationStudent . class ) ;
String [ ] beanNamesForType = applicationContext. getBeanDefinitionNames ( ) ;
for ( String s : beanNamesForType) {
System . out. println ( s) ;
}
}
}
[/code]
当去除@Import后,输出结果为:
Bean生命周期 常意义上讲的bean的生命周期,指的是bean从创建到初始化,经过一系列的流程,最终销毁的过程,如下图所示。在Spring中,我们可以自己来指定bean的初始化和销毁的方法@Bean(initMethod = "自定义的初始化方法名",destroyMethod = "自定义的销毁方法名")
。当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。 自定义一个Life类:
public class Life {
public Life ( ) {
System . out. println ( "Life构造方法执行了!" ) ;
}
public void init ( ) {
System . out. println ( "Life初始化方法执行了!" ) ;
}
public void destroy ( ) {
System . out. println ( "Life销毁方法执行了!" ) ;
}
}
[/code]
@Configuration
public class AnnotationStudent {
@Bean ( initMethod = "init" , destroyMethod = "destroy" )
public Life life ( ) {
return new Life ( ) ;
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ( AnnotationStudent . class ) ;
System . out. println ( "容器创建完成!" ) ;
Life bean = applicationContext. getBean ( Life . class ) ;
}
}
[/code]
可以看到,对于单实例对象,先执行构造方法,再到初始化方法,而销毁方法执行需要显式关闭容器时候才执行applicationContext.close();
因此,我们可以自定义初始化方法和销毁方法处理配置数据源问题,在初始化的时候,会对很多的数据源的属性进行赋值操作;在销毁的时候,我们需要对数据源的连接等信息进行关闭和清理。
@Value注解为属性赋值 在Student类中的name属性上加上@Value注解,等价于配置文件中的
里的
,外面的bean是@Component
注解作用。
@Component
public class Student {
@Value ( "xiaoxin" )
private String name;
@Override
public String toString ( ) {
return "Student{" +
"name='" + name + '\'' +
'}' ;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public Student ( ) {
}
public Student ( String name) {
this . name = name;
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Student student = context. getBean ( "student" , Student . class ) ;
System . out. println ( student) ;
}
}
[/code]
使用@PropertySource加载配置文件 原始xml方式: 在resources包下创建一个applicationContext.properties配置文件,内容为键值对形式:name=xiaoxin password=888888
编写一个Property类,用于测试:
public class Property {
private String username;
private Integer password;
@Override
public String toString ( ) {
return "Property{" +
"username='" + username + '\'' +
", password=" + password +
'}' ;
}
public String getUsername ( ) {
return username;
}
public void setUsername ( String username) {
this . username = username;
}
public Integer getPassword ( ) {
return password;
}
public void setPassword ( Integer password) {
this . password = password;
}
public Property ( String username, Integer password) {
this . username = username;
this . password = password;
}
public Property ( ) {
}
}
[/code]
< context: annotation-config />
< context: component-scan base-package = " com.xxx" />
< context: property-placeholder location = " applicationContext.properties" />
< bean class = " com.xxx.pojo.Property" id = " property" >
< property name = " username" value = " ${name}" />
< property name = " password" value = " ${password}" />
bean>
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
Property property = context. getBean ( "property" , Property . class ) ;
System . out. println ( property. toString ( ) ) ;
}
}
[/code]
注解方式: 保留原applicationContext.properties配置文件 将Spring核心配置文件内容删除:只保留开启注解:
Property类完全使用注解代替:
@Configuration
@ComponentScan ( value = "com.xxx" )
@Component
@PropertySource ( "classpath:applicationContext.properties" )
public class Property {
@Value ( "${name}" )
private String username;
@Value ( "${password}" )
private Integer password;
@Override
public String toString ( ) {
return "Property{" +
"username='" + username + '\'' +
", password=" + password +
'}' ;
}
public String getUsername ( ) {
return username;
}
public void setUsername ( String username) {
this . username = username;
}
public Integer getPassword ( ) {
return password;
}
public void setPassword ( Integer password) {
this . password = password;
}
public Property ( String username, Integer password) {
this . username = username;
this . password = password;
}
public Property ( ) {
}
}
[/code]
public class BeanTest {
@Test
public void beanTest ( ) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext ( Property . class ) ;
Property bean = applicationContext. getBean ( Property . class ) ;
System . out. println ( bean. toString ( ) ) ;
}
}
[/code]
代理模式
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
案例:男孩相亲,想找女孩结婚,于是男孩找媒婆进行代理,媒婆代理介绍女孩同时,还要收取一定的介绍费。
public interface Marry {
void marry ( ) ;
}
[/code]
public class Girl {
private String name;
@Override
public String toString ( ) {
return "Girl{" +
"name='" + name + '\'' +
'}' ;
}
public Girl ( String name) {
this . name = name;
}
public Girl ( ) {
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
}
[/code]
public class Boy implements Marry {
private Girl girl;
public Boy ( Girl girl) {
this . girl = girl;
}
@Override
public void marry ( ) {
System . out. println ( "想跟" + girl. getName ( ) + "认识!" ) ;
}
}
[/code]
public class Proxy implements Marry {
private Boy boy;
public Proxy ( Girl girl) {
boy = new Boy ( girl) ;
}
@Override
public void marry ( ) {
boy. marry ( ) ;
}
public void earn ( ) {
System . out. println ( "媒婆收取介绍费" ) ;
}
}
[/code]
public class ProxyTest {
@Test
public void proxyTest ( ) {
Girl girl = new Girl ( ) ;
girl. setName ( "美女!" ) ;
Proxy proxy = new Proxy ( girl) ;
proxy. marry ( ) ;
proxy. earn ( ) ;
}
}
[/code]
Spring AOP
AOP AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
总的来说,AOP是指在程序的运行期间动态地将某段代码切入到指定方法、指定位置进行运行的编程方式。AOP的底层是使用动态代理实现的。
AOP中相关概念 横切关注点 :跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 … 切面(ASPECT) :横切关注点 被模块化 的特殊对象。即,它是一个类。 通知(Advice) :切面必须要完成的工作。即,它是类中的一个方法。 目标(Target) :被通知对象。 代理(Proxy) :向目标对象应用通知之后创建的对象。 切入点(PointCut) :切面通知 执行的 “地点”的定义。 连接点(JointPoint) :与切入点匹配的执行点。
SpringAOP中支持5种类型的Advice
Spring AOP的实现(3种) 导入依赖 在原有的maven的pom.xml文件中加上AOP织入依赖包
< dependency>
< groupId> org.aspectj groupId>
< artifactId> aspectjweaver artifactId>
< version> 1.9.4 version>
dependency>
[/code]
通过 Spring API 实现 编写业务接口及其实现类
public interface UserService {
public void add ( ) ;
public void delete ( ) ;
public void update ( ) ;
public void select ( ) ;
}
[/code]
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 ( "查询用户" ) ;
}
}
[/code]
public class BeforeAdvice implements MethodBeforeAdvice {
@Override
public void before ( Method method, Object [ ] args, Object target) throws Throwable {
System . out. println ( "前置通知的" + target. getClass ( ) . getName ( ) + "的" + method. getName ( ) + "方法被执行了" ) ;
}
}
[/code]
public class AfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning ( Object returnValue, Method method, Object [ ] args, Object target) throws Throwable {
System . out. println ( "后置通知的" + target. getClass ( ) . getName ( ) + "的" + method. getName ( ) + "执行了,返回值为:" + returnValue) ;
}
}
[/code]
public class InterceptAdvice implements MethodInterceptor {
@Override
public Object invoke ( MethodInvocation invocation) throws Throwable {
try {
System . out. println ( "环绕通知" + invocation. getMethod ( ) . getName ( ) + "——方法前执行的" ) ;
Method invocationMethod = ( Method ) invocation. proceed ( ) ;
System . out. println ( "环绕通知" + invocation. getMethod ( ) . getName ( ) + "——方法后执行的" ) ;
return invocationMethod;
} catch ( Throwable throwable) {
throwable. printStackTrace ( ) ;
}
return invocation;
}
}
[/code]
< bean id = " userService" class = " com.xxx.service.UserServiceImpl" />
< bean id = " beforeAdvice" class = " com.xxx.advice.BeforeAdvice" />
< bean id = " afterAdvice" class = " com.xxx.advice.AfterAdvice" />
< bean id = " interceptAdvice" class = " com.xxx.advice.InterceptAdvice" />
< bean id = " throwAdvice" class = " com.xxx.advice.ThrowAdvice" />
< aop: config>
< aop: pointcut id = " pointCut" expression = " execution(* com.xxx.service.UserServiceImpl.*(..))" />
< aop: advisor advice-ref = " beforeAdvice" pointcut-ref = " pointCut" />
< aop: advisor advice-ref = " afterAdvice" pointcut-ref = " pointCut" />
< aop: advisor advice-ref = " interceptAdvice" pointcut-ref = " pointCut" />
< aop: advisor advice-ref = " throwAdvice" pointcut-ref = " pointCut" />
aop: config>
[/code]
public class UserServiceImplTest {
@Test
public void myTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "beans.xml" ) ;
UserService userService = context. getBean ( "userService" , UserService . class ) ;
userService. delete ( ) ;
System . out. println ( "==============================" ) ;
UserService userService1 = context. getBean ( "userService" , UserService . class ) ;
userService1. add ( ) ;
System . out. println ( "==============================" ) ;
UserService userService2 = context. getBean ( "userService" , UserService . class ) ;
userService2. select ( ) ;
System . out. println ( "==============================" ) ;
UserService userService3 = context. getBean ( "userService" , UserService . class ) ;
userService3. update ( ) ;
}
}
[/code]
通过自定义类来实现
保留之前的业务类UserServiceImpl 编写自定义类DiyPointcut
public class DiyPointcut {
public void beforeAdvice ( ) {
System . out. println ( "前置通知" ) ;
}
public void afterAdvice ( ) {
System . out. println ( "后置通知" ) ;
}
public void interceptAdvice ( ProceedingJoinPoint joinPoint) {
System . out. println ( "方法" + joinPoint. getSignature ( ) . getName ( ) + "环绕通知前执行的语句" ) ;
Object [ ] args = joinPoint. getArgs ( ) ;
try {
Object proceed = joinPoint. proceed ( args) ;
} catch ( Throwable throwable) {
throwable. printStackTrace ( ) ;
}
System . out. println ( "方法" + joinPoint. getSignature ( ) . getName ( ) + "环绕通知后执行的语句" ) ;
}
}
[/code]
< bean id = " userService" class = " com.xxx.service.UserServiceImpl" />
< bean id = " diyPointcut" class = " com.xxx.diy.DiyPointcut" />
< aop: config>
< aop: aspect ref = " diyPointcut" >
< aop: pointcut id = " pointcut" expression = " execution(* com.xxx.service.UserServiceImpl.*(..))" />
< aop: before method = " beforeAdvice" pointcut-ref = " pointcut" />
< aop: after method = " afterAdvice" pointcut-ref = " pointcut" />
< aop: around method = " interceptAdvice" pointcut-ref = " pointcut" />
aop: aspect>
aop: config>
[/code]
public class UserServiceImplTest {
@Test
public void myTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "beans.xml" ) ;
UserService userService = context. getBean ( "userService" , UserService . class ) ;
userService. delete ( ) ;
System . out. println ( "==============================" ) ;
UserService userService1 = context. getBean ( "userService" , UserService . class ) ;
userService1. add ( ) ;
System . out. println ( "==============================" ) ;
UserService userService2 = context. getBean ( "userService" , UserService . class ) ;
userService2. select ( ) ;
System . out. println ( "==============================" ) ;
UserService userService3 = context. getBean ( "userService" , UserService . class ) ;
userService3. update ( ) ;
}
}
[/code]
通过自定义类来实现
编写注解实现的增强类AnnotationAdvice
@Aspect
public class AnnotationAdvice {
@Before ( "execution(* com.xxx.service.UserServiceImpl.*(..))" )
public void before ( ) {
System . out. println ( "前置通知" ) ;
}
@After ( "execution(* com.xxx.service.UserServiceImpl.*(..))" )
public void after ( ) {
System . out. println ( "后置通知" ) ;
}
@Around ( "execution(* com.xxx.service.UserServiceImpl.*(..))" )
public void around ( ProceedingJoinPoint joinPoint) throws Throwable {
System . out. println ( "环绕通知执行前" ) ;
System . out. println ( "签名:" + joinPoint. getSignature ( ) ) ;
Object proceed = joinPoint. proceed ( ) ;
System . out. println ( "环绕通知执行后" ) ;
System . out. println ( proceed) ;
}
}
[/code]
< context: component-scan base-package = " com.xxx.service" />
< context: annotation-config/>
[/code]
< aop: aspectj-autoproxy proxy-target-class = " false" />
< bean class = " com.xxx.service.UserServiceImpl" id = " userService" />
< bean id = " annotationAdvice" class = " com.xxx.annotation.AnnotationAdvice" />
[/code]
public class UserServiceImplTest {
@Test
public void myTest ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "beans.xml" ) ;
UserService userService = context. getBean ( "userService" , UserService . class ) ;
userService. delete ( ) ;
System . out. println ( "==============================" ) ;
UserService userService1 = context. getBean ( "userService" , UserService . class ) ;
userService1. add ( ) ;
System . out. println ( "==============================" ) ;
UserService userService2 = context. getBean ( "userService" , UserService . class ) ;
userService2. select ( ) ;
System . out. println ( "==============================" ) ;
UserService userService3 = context. getBean ( "userService" , UserService . class ) ;
userService3. update ( ) ;
}
}
[/code]
Spring事务管理及Spring整合MyBatis代码示例
Spring事务管理 什么是事务:事务就是把一系列的动作当成一个独立的工作单元,这些动作要么都执行,要么都不执行。 事务四个特性-ACID: 原子性(atomicity) 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用 一致性(consistency) 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中 隔离性(isolation) 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏 持久性(durability) 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中 Spring支持编程式事务管理和声明式的事务管理: 声明式事务管理 声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。 编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。 显然声明式事务管理要优于编程式事务管理,这正是Spring倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。 使用Spring管理事务,注意头文件的约束导入:
xmlns: tx= "http://www.springframework.org/schema/tx"
http: / / www. springframework. org/ schema/ tx
http: / / www. springframework. org/ schema/ tx/ spring- tx. xsd">
[/code]
< bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" />
bean>
[/code]
!-- Spring事务管理 -->
< bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" />
bean>
< bean id = " baseTransactionProxy" class = " org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract = " true" >
< property name = " transactionManager" ref = " transactionManager" />
< property name = " transactionAttributes" >
< props>
< prop key = " add*" > PROPAGATION_REQUIRED prop>
< prop key = " edit*" > PROPAGATION_REQUIRED prop>
< prop key = " remove*" > PROPAGATION_REQUIRED prop>
< prop key = " insert*" > PROPAGATION_REQUIRED prop>
< prop key = " update*" > PROPAGATION_REQUIRED prop>
< prop key = " del*" > PROPAGATION_REQUIRED prop>
< prop key = " *" > readOnly prop>
props>
property>
bean>
[/code]
< beans......>
......
< bean id = " bankService"
class = " footmark.spring.core.tx.declare.namespace.BankServiceImpl" >
< property name = " bankDao" ref = " bankDao" />
bean>
< tx: advice id = " bankAdvice" transaction-manager = " transactionManager" >
< tx: attributes>
< tx: method name = " transfer" propagation = " REQUIRED" />
tx: attributes>
tx: advice>
< aop: config>
< aop: pointcut id = " bankPointcut" expression = " execution(* *.transfer(..))" />
< aop: advisor advice-ref = " bankAdvice" pointcut-ref = " bankPointcut" />
aop: config>
......
beans>
[/code]
@Transactional 的声明式事务管理 启用tx的annotation:
< tx: annotation-driven transaction-manager = " transactionManager" />
[/code]
@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
编程式事务管理 编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
Spring事务的传播行为: 事务的第一个方面是传播行为(propagation behavior)。当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。Spring定义了七种传播行为:
事务的隔离级别: 事务的第二个维度就是隔离级别(isolation level)。
脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
[/code] Spring结合事务整合MyBatis示例
导入相关Jar包
< dependencies>
< dependency>
< groupId> junit groupId>
< artifactId> junit artifactId>
< version> 4.12 version>
< scope> test scope>
dependency>
< 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-webmvc artifactId>
< version> 5.2.12.RELEASE version>
dependency>
< dependency>
< groupId> org.aspectj groupId>
< artifactId> aspectjweaver artifactId>
< version> 1.9.4 version>
dependency>
< dependency>
< groupId> org.springframework groupId>
< artifactId> spring-jdbc artifactId>
< version> 5.3.7 version>
dependency>
< dependency>
< groupId> org.mybatis groupId>
< artifactId> mybatis-spring artifactId>
< version> 2.0.5 version>
dependency>
< dependency>
< groupId> log4j groupId>
< artifactId> log4j artifactId>
< version> 1.2.17 version>
dependency>
< dependency>
< groupId> org.testng groupId>
< artifactId> testng artifactId>
< version> RELEASE version>
< scope> compile scope>
dependency>
< dependency>
< groupId> log4j groupId>
< artifactId> log4j artifactId>
< version> 1.2.17 version>
< scope> compile scope>
dependency>
dependencies>
< build>
< plugins>
< plugin>
< groupId> org.apache.maven.plugins groupId>
< artifactId> maven-surefire-plugin artifactId>
< version> 2.12.4 version>
< configuration>
< argLine>
-Dfile.encoding=UTF-8
argLine>
configuration>
plugin>
plugins>
< resources>
< resource>
< directory> src/main/resources directory>
< includes>
< include> **/*.properties include>
< include> **/*.xml include>
includes>
< filtering> true filtering>
resource>
< resource>
< directory> src/main/java directory>
< includes>
< include> **/*.properties include>
< include> **/*.xml include>
includes>
< filtering> true filtering>
resource>
resources>
build>
[/code]
编写配置文件及加入日志 mybatis-config.xml
< configuration>
< settings>
< setting name = " mapUnderscoreToCamelCase" value = " true" />
< setting name = " logImpl" value = " LOG4J" />
settings>
< typeAliases>
< package name = " com.xxx.pojo" />
typeAliases>
< mappers>
< mapper resource = " com/xxx/mapper/UserMapper.xml" />
mappers>
configuration>
[/code]
spring-mybatis.xml
< 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/mybaits?serverTimezone=UTC& allowPublicKeyRetrieval=true& useSSL=false& characterEncoding=UTF-8" />
< property name = " username" value = " root" />
< property name = " password" value = " 123456" />
bean>
< bean id = " sqlSessionFactory" class = " org.mybatis.spring.SqlSessionFactoryBean" >
< property name = " dataSource" ref = " dataSource" />
< property name = " configLocation" value = " classpath:mybatis-config.xml" />
bean>
< bean id = " sqlSession" class = " org.mybatis.spring.SqlSessionTemplate" >
< constructor-arg index = " 0" ref = " sqlSessionFactory" />
bean>
< bean id = " transactionManager" class = " org.springframework.jdbc.datasource.DataSourceTransactionManager" >
< property name = " dataSource" ref = " dataSource" />
bean>
< tx: advice id = " transactionAdvice" transaction-manager = " transactionManager" >
< tx: attributes>
< tx: method name = " *" propagation = " REQUIRED" />
tx: attributes>
tx: advice>
< aop: config>
< aop: pointcut id = " transactionPointcut" expression = " execution(* com.xxx.mapper.*.*(..))" />
< aop: advisor advice-ref = " transactionAdvice" pointcut-ref = " transactionPointcut" />
aop: config>
[/code]
applicationContext.xml
< import resource = " spring-mybatis.xml" />
< bean id = " userMapperImpl_2" class = " com.xxx.mapper.UserMapperImpl_2" >
< property name = " sqlSessionFactory" ref = " sqlSessionFactory" />
bean>
[/code]
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/xxx.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
[/code]
编写接口及其实现类和配置对应的mapper.xml文件
UserMapper接口
public interface UserMapper {
public List < User > queryUser ( ) ;
int addUser ( User user) ;
int deleteUser ( int id) ;
}
[/code]
UserMapperImpl_2实现类
public class UserMapperImpl_2 extends SqlSessionDaoSupport implements UserMapper {
@Override
public List < User > queryUser ( ) {
return getSqlSession ( ) . getMapper ( UserMapper . class ) . queryUser ( ) ;
}
@Override
public int addUser ( User user) {
return getSqlSession ( ) . getMapper ( UserMapper . class ) . addUser ( user) ;
}
@Override
public int deleteUser ( int id) {
return getSqlSession ( ) . getMapper ( UserMapper . class ) . deleteUser ( id) ;
}
}
[/code]
UserMapper.xml
< mapper namespace = " com.xxx.mapper.UserMapper" >
< select id = " queryUser" resultType = " User" >
select * from user
select>
< insert id = " addUser" parameterType = " User" >
insert into user (id,last_name,email) values (#{id},#{lastName},#{email})
insert>
< delete id = " deleteUser" parameterType = " _int" >
delete from user where id = #{id}
delete>
mapper>
[/code]
测试及结果
手动设置错误,如在插入语句上写错insert为inserts
< insert id= "addUser" parameterType= "User" >
inserts into user ( id, last_name, email) values ( #{ id} , #{ lastName} , #{ email} )
< / insert>
[/code]
public class UserMapperTest {
static Logger logger = Logger . getLogger ( UserMapperTest . class ) ;
@Test
@Test
public void userMapperImpl_2 ( ) {
ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" ) ;
UserMapper userMapperImpl_2 = context. getBean ( "userMapperImpl_2" , UserMapper . class ) ;
userMapperImpl_2. addUser ( new User ( 12 , "xiaoxin" , "com@xiaoxin" ) ) ;
userMapperImpl_2. deleteUser ( 8 ) ;
for ( User user : userMapperImpl_2. queryUser ( ) ) {
System . out. println ( user) ;
}
}
}
[/code]
如果为插入语句错误,则项目不能正常插入,事务会回滚。 查看并刷新数据库表,没有变化。 接着将错误改正后,再次测试结果为: 成功添加和删除,事务保证了数据的一致性。查看数据库表为: 啊!终于搞完了,五万多字数,历时几天将我所学的Spring知识浓缩到一篇文章给大家了,当然啦,整理的主要目的是为了给自己复习所用的,只要学不死就往死里学,趁年轻,别等头发掉光了才去后悔当初为什么没有好好学,哈哈! 加油吧,骚年!
来源:https://blog.csdn.net/weixin_45304790/article/details/117807743 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!