目录
1-1Spring大概了解
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术
官网 : Spring | Home
maven项目pom依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
1.优点
1、Spring是一个开源免费的框架 , 容器 .
2、Spring是一个轻量级的框架 , 非嵌入式的 .
3、控制反转 Ioc, 面向切面 Aop
4、对事物的支持 , 对框架的支持
一句话概括:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
2.组成
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
- 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
- Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
- Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
- Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
- Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
- Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
- Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
3.IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方(spring容器),所谓控制反转就是:由spring来控制和创建这些对象。
何为解耦合:
IoC是Spring框架的核心内容,使用多种方式完美的实现了IoC,可以使用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IoC。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
Bean通常指的就是Java的实现类对象
控制反转是一种通过描述(XML或注解)并通过第三方(其他的软件或框架如Tomcat,spring等等)去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)
1-2 如何使用Spring
1.导入Springjar包
跟其他的框架一样,spring也需要一个jar包,我们需要导入相应的jar包才能使用,直接去官网下载或者通过maven的pom文件下载也是可以的.
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
2.编写一个实体类
这就是一个普通的类,用来演示Ioc容器怎样实现这个类的
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
3.配置spring的xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!--spring的规范-->
<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">
<!--bean就是java对象 , 由Spring创建和管理
这里的 id 就是这个被创建的java对象在spring ioc容器中的名字
我们正常创建一个对象是new出来的 Hello h = new Hello(); 但是在spring中
它会根据我们提供的这个 class 中的全限定名称找到我们要使用的java类,使用反射机制
创建这个类的类对象,并且存储在ioc容器中,像map那样的方式放入进去
map.put("hello",new Hello);
这样spring在使用的时候只要通过这个id就能找到这个类对象,就可以使用了
-->
<bean id="hello" class="com.kuang.pojo.Hello">
<!--property是用来在xml配置文件里设置该类的字段值的
通过name:来设置那个字段的value:值-->
<property name="name" value="Spring"/>
</bean>
</beans>
4.运行代码
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
String config = "beans.xml";
//获取spring的ioc容器对象实例,我们需要的对象就存放在这个容器中
//通过这个ClassPathXmlApplicationContext实现类对象读取xml配置文件得到ioc容器
//ApplicationContext就是ioc容器的接口
ApplicationContext a = new ClassPathXmlApplicationContext(config);
//容器对象.getBean(需要得到的对象在容器中的 id )
//这样可以得到你需要对应id的对象,但是返回是object的所以需要向下转型才能使用
Hello hello = (Hello) a.getBean("hello");
hello.show();
}
- Hello 对象是谁创建的 ? hello 对象是由Spring创建的
- Hello 对象的字段是怎么设置的 ? hello 对象的字段是由Spring容器设置的,在Bean里面使用了property标签来设定给定字段的值
这个过程就叫控制反转 :
- 控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 比如new一个对象,而使用Spring后 , 对象是由Spring通过反射来创建的
- 反转 : 程序本身不创建对象 , 而变成被动的接收对象
如何返回ioc容器的一些参数
spring提供了两个方法可以返回ioc容器中对象的数量和id,
getBeanDefinitionCount():返回容器中的对象数量
getBeanDefinitionName():返回一个包含ioc中所有对象名的String数组
//返回ioc中的对象总数
System.out.println(a.getBeanDefinitionCount());
//返回所有对象的id名
System.out.println(a.getBeanDefinitionNames());
int count = a.getBeanDefinitionCount();
String[] id = a.getBeanDefinitionNames();
1-3Spring注入概念
1.Set注入
什么是set注入,按照传统的方式我们在一个类里给私有字段赋值的话只能使用set方法赋值,而spring可以在xml文件里帮助我们进行赋值,其原理也是通过反射来使用类中的set赋值,但是这样可以实现解耦合.
在使用set注入的时候必须保证该注入字段拥有了set方法,否则会抛出异常.
而spring寻找set方法是通过set+字段的名字找到该set方法的,不会区分大小写.
<!--简单字段的赋值-->
<bean id="xxx" class="xx.xxx.xxx">
<!--赋值要按照xml格式填写,值必须要用" "起来-->
<property name="需要赋值的字段名" value="需要赋的值"/>
</bean>
<!--引用字段的赋值-->
<bean id="xxx" class="xxx.xxx.xx">
<property id="xxxxx" ref="需要赋的值的bean id">
</bean>
类中的字段分为简单字段和引用字段,简单字段通常是基本类行,String也在这属于简单字段,而类对象则为引用对象.
这里其实也只是单单的使用了set方法,跟平时使用set没啥区别,但是有了很大的不同,我们只需要修改xml配置文件就能修改需要赋的值或者对象.
2.Spring的自动注入byName/byType
使用xml配置文件手工注入set是可行的,但是这在有大量类的情况下就有点捉襟见肘了,所以spring也提供了两种自动注入的方式,byName(按照名称)和byType(按照类样式)来实现自动注入
而我们如果要使用自动注入则要在需要注入的bean标签里面加上atuowire="注入方式",来寻找我们需要的注入对象;
示例:
类:
class School{
//一个类字段
Student student;
}
class Student{
//student里面的字段
String name;
int age;
//set.....方法
}
如果通过byName去自动注入,则需要studen的bean对象id和School中的Student类字段id相同,这样spring才能找到需要注入的对象.
如果是通过byType去字动注入,则需要class是Student或者其子类,接口实现类,就可以找到注入对象,对名称没有要求.
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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--按照所需对象的对象名称来寻找注入对象-->
<bean id="student" class="study.ssm.sp.Student">
<property name="name" value="V"/>
<property name="age" value="21"/>
</bean>
<!--使用byName,按照所需对象的id匹配注入-->
<bean id="school" class="study.ssm.sp.School" autowire="byName">
<property name="name" value="zuen"/>
<property name="address" value="皮尔特洛夫下城区"/>
------------------------------------------分割线--------------------------------------------
<!--按照所需对象的类类型来寻找注入对象-->
<bean id="xxxxxx" class="study.ssm.sp.Student">
<property name="name" value="Jinx"/>
<property name="age" value="19"/>
</bean>
<!--使用byType,按照所需对象的id匹配注入-->
<bean id="school" class="study.ssm.sp.School" autowire="byType">
<property name="name" value="zuen"/>
<property name="address" value="皮尔特洛夫下城区"/>
</bean>
</beans>
1-4 注解
类注解的使用
使用spring的ioc容器,除了可以在xml配置文件中创建对象,也可以使用注解来代替xml中的bean标签,注解开发使开发更加简单并且具有可读性.
Component注解的使用:@Component(value="id名字")
使用这个注解跟xml中的bean标签同理,可以创建一个对象放入到spring的ioc容器中使用,后面的值为在ioc中的对象名,被这个注释的类表示一个模块,并且不用去创建xml这个bean标签.
//这是一个注解,表示被注释的这个类是spring的一个模块
@Component("在ioc中的对象名")
public class TT{
}
有了这个注释后,就可以省略bean标签的繁琐方式了,但是使用注解的话必须要让spring知道这个类被注解了,那么就需要用到spring的扫描器:
<context:component-scan base-package="被注解类的全限定名或者是想扫描的包名"/>
这样spring在启动的时候就可以扫描到我们需要注解的类,到后面这个xml标签也可以编程注解写到程序里,@ComponentScan注解.
@ComponentScan(basePackages = {"扫描到的包名", "other.pkg"})
public class Application { ... }
不论是component-scan标签,还是@ComponentScan注解。它们扫描或解析的bean只能是Spring内部所定义的,比如@Component、@Service、@Controller或@Repository。如果有一些自定义的注解,比如@Consumer、这个注解修饰的类是不会被扫描到的。这个时候我们就得自定义扫描器完成这个操作。
其他的注解类型:
@Service:注解逻辑层类
@Repository:注解持久层类
@Contorller:注解控制层类
@Component:注解不属于上面三种类的其他类类型
如果要给类里面的字段赋值,传统的xml配置方法是使用property来赋值,而使用注解则可以使用@Value("赋的值")并且不需要有set方法就可以赋值,而如果是引用类型的注解则使用@AtuoWirte来赋值.
@AtuoWired默认是byType去寻找这个对象,如果要使用byName去寻找的话要在下面加上@Qualifier("需要匹配的id名字")
@Component("LOL")
public class TT{
//将Jinx这个字符串赋给这个name
@Value("Jinx")
private String name;
@Value("19")
private int age;
//给Hope类赋一个hope对象
//AtuoWired有一个required默认值为true,为false如果没有找到可以注入的类不会报错
//如果没有修改默认为true,如果没找到可以注入的类就会抛出异常
@AtuoWired
Hope hope;
//按照byName方式去查找
@AtuoWired
@Quelifier("V")
Hope hope;
}
还有一个是Java包提供的注解方法是byName类型@Resource,这个用法跟Atuowired用法相同,放在需要赋值的对象上,而查找的就是需要赋值对象的对象名
//先使用byName自动注入,如果byName失败了则使用byType注入
@Resource //byName去寻找school名的对象
School school;
如果都失败了则抛出异常
1-5Aop(面向切面编程)
1.代理学习
动态代理实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHandler创建代理对象.
jdk动态代理要求目标类必须实现接口
cglib动态代理:第三方工具库,创建代理对象,原理就是继承.通过继承目标类,创建子类.
子类就是代理对象.要求目标类不能是final的,方法也不能是final的.
动态代理的作用:
1)在目标类源代码不改变的情况下增加功能.
2)减少代码的重复.
3)专注业务逻辑代码.
4)解耦合,将业务代码和非业务代码分离.
2.Aop是什么
Aop:面向切面编程,基于动态代理,可以使用jdk或者cglib两种代理方式
Aop就是动态代理的规范化,吧动态代理的实现步骤,方式都定义好了,让开发人员用同样的方式使用动态代理.
Aop是用来在增加业务功能或者增加功能冗余的情况下可以使用.
传统代码
aop的应用
3.Aop概念和使用语法
- 目标对象:需要被增强的类就是目标对象.
- Aspect:切面,表示给目标类增强的功能,一般都是非业务方法独立使用.
- joinpoint:连接点,连接业务方法和切面方法的位置,就某个类的业务方法
- pointcut:切入点,多个连接点的集合,多个方法.
- Advice:通知,执行切面功能的时间(增强事务位置)
切面三要素:
1)切面的功能代码,切面干什么.
2)切面的执行位置,使用pointcut表示切面执行的位置.
3)切面的执行时间,使用Advice表示时间,是在目标方法之前,还是目标方法之后.
//目标对象
public class A{
//切入点,单个又称连接点
public void B(){
//Advice(切入时间)表示是在代码前切入还是在代码后切入
//Advice在目标代码前切入
new before();
//正常业务逻辑代码
new Work();
//Advice(切入时间)在目标代码后切入
new after();
}
}
aop的实现:spring自带aop实现但是一般不用,而是用集成的aspectJ框架来实现.
4.aspectJ的注解使用
切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中用注解表示的,也可以用xml配置文件来实现.
Advice的注解实现:
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After
这五个分别表示不同的切入时间.
AspectJ专门定义表达式用于指定切入点.表达式原型:
execution([修饰符]? 返回值类型 包名?.类名?.方法名(参数列表) 抛出异常类型?)
带?的为可选项,可以不填,其他为必填.每一项中间都有空格隔开
* | 0至多个任意字符 |
.. | 用在方法中表示多个参数 用在包名后表示当前包及其子包的路径 |
+ | 用在类名后,表示当前类及其子类 用在接口后,表示当前接口及其子接口 |
例子:
//这里execution表示的意思是
//切入 所有访问修饰 所有返回类型 并且在com包下的所有类的dosome方法 这个方法的传入的参数可以为多个参数的方法的前面
@Before("execution(* * com.*.dosome(..))")
//如果参数不匹配则会切入失败
//这里传入的参数只需要知道参数的类型就行了,不用形参名
//如果目标类方法只有一个String参数或者参数与这里设定的类型不匹配则不切入
//必须满足execution里面的所有条件才会切入,否则切入失败
@Before("execution(* * com.*.dosome(String,Integer))")
定义一个切面:
在aspectj中,可以使用@Aspect来定义一个切面类
可以定义一个普通的类,该类中实现增强的方法,然后在该类上使用@Aspect将该类作为切面类使用
@Before(前置通知)
在业务代码前执行的通知.
@Component("doOther")
//@Aspect(切面)表示这个类是切面类
@Aspect
public class DoOther {
//用来表面这个切入的时间和切入哪个目标,当前使用的是@Before这个注解,表示在目标方法前执行这个被注解的方法.
@Before(value = "execution(public void study.ssm.sp.*.dosome(..))")
public void do1() {
System.out.println("增强方法,非业务方法,打印当前时间" + new Date());
}
public void do2(){
}
}
@AfterReturning(后置通知)
@AfterReturning是在执行目标类的方法后执行的一个切入,除了value参数外还有一个returning参数,并且可以拿到目标类方法的返回值,在业务逻辑结束后做一些事,.
@Aspect
public class A{
//returning的参数是增强方法里的形参名
//这个增强方法可以接收到目标方法的返回值,用Object接收
@AfterReturning(value="execution(* com.*..doSomething(..))",retruning="res")
public void B(Object res){
//目标方法的返回值,可以拿来做一些事情
//如果是值拷贝,改变无效,如果是引用则会改变
res.xxxx();
//执行的代码
}
}
@Around(环绕通知)
环绕通知可以在目标方法的前面和后面增强功能,可以控制目标方法是否被调用执行,还能修改目标方法的执行结果,影响最后的调用结果.
@Aspect
public class A{
@Around(value="execution(* com.*.doSomething(..))")
//这里使用了Proceedingjoinpoint一个对象,可以拿到目标方法
public Object B(ProceedingJoinPoint pjp){
//方法执行返回结果
Object result = null;
//可以从执行的目标方法中拿到所有实参
Object[] args = pjp.getArgs();
//在目标方法执行前增强功能
System.out.println("环绕通知:在目标方法前执行"+new Date());
//执行目标方法
result = pjp.proceed();
//在目标方法后加入功能
//加入方法.....
//返回执行结果
return result;
}
}
同时也可以在该环绕通知方法里加入判断,决定是否执行业务方法,同样返回值也可以这样决定.
JoinPoint参数:
@AftherRrturning(value="execution(xxx xxx.(..))",result="xxx")
//这个参数可以获得目标方法在执行的时候的信息,例如方法名,方法实参.
public void B(JoinPoint jp){
//获取方法完整定义
System.out.println("方法的签名(定义)="+jp.getSignature());
//获取方法名称jp.getSignature().getName();
//获取方法实参jp.getSignature().getArgs();
}
@AfterThrowing(异常通知)
用来在目标方法执行发生异常时执行的,可以做异常监控程序,如果有异常则进行处理.
//throwing的参数名必须和形参名匹配
@AfterThrowing(value="xxx xxx.*.doSomething(..)",throwing="ex")
//目标方法发生异常是执行该方法
public void A(Exception ex){
System.out.println("异常通知:方法执行发生异常"+ex.getMessage());
}
@After(最终通知)
在目标方法执行后总是会执行的,同finally一样.
@After(value="execution(* com.*.doSomething(..))")
public void A(){
//一般做项目清除的
System.out.println("执行的最终通知");
}
@Pointcut
这个不是通知的注解,而是辅助类型的注解,当有多个通知的切入点表达式(execution)中的切入点相同时可以使用,用来去重复可以复用的,可以声明一个方法加上该注解定义切入点,然后在其他通知注解的切入点表达式中使用该方法就可以了.
@Pointcut(value="execution(* com.study.ssm.doSomething(..))")
private void poinCutt(){
//这个方法只是用来表示其他的通知注解的切入点表达式的,所以不用写任何代码
//也不用被其他对象调用所以为private
}
@Before(value="poinCutt()")
public void A(){
//切入代码...
}
@After(value="poinCutt()")
public void C(){
//切入代码...
}
1-6 Spring集成Mybatis的使用
1.整合Mybatis的思路
Spring将Mybatis整合到一起,当作一个框架使用,这样减轻了开发人员的负担
实现的方法就是ioc,ioc可以将Mybatis框架创建对象的工作交给Spring统一创建,开发人员只需要从spring中获取对象就可以使用了,不用同时面向两个框架或多个框架了,只需要面对spring.
Mybatis使用步骤:
- 定义dao接口.StudentDao
- 定义mapper文件 StudentDao.xml
- 定义mybatis主配置文件.mybatis.xml
- 创建dao代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);
- 使用dao代理去访问数据库,List<Student> student = dao.selectStudent();
dao对象创建大致流程:
dao ==> SqlSession.getMapper() ==> SqlSession ==>SqlSessionFactory ==>mybatis主配置文件
要使用dao对象,就必须要使用getMapper()方法才能得到dao对象,所以必须要拿到SqlSession才能使用getMapper()方法:
1.获取SqlSession对象,需要使用到SqlSessionFactory的openSession方法.
2.创建SqlSessionFactory对象. 通过mybatis主配置文件可以创建SqlSessionFactory对象.
所以我们只要拿到SqlSessionFactory对象就能创建dao对象了.
Mybatis主文件的配置:
1.数据库信息比如driver,url,user,password.
2.Mapper文件的位置
所以,如果要在spring框架中使用mybatis,必须创建以下对象:
- 独立的连接池对象
- SqlSessionFactory对象
- 创建dao对象
2.创建dao对象和SqlSessionFactory对象
大致步骤:
1.新建mevan项目
2.加入pom依赖
- spring依赖
- mybatis依赖
- mysql驱动
- spring事务依赖
- mybatis和spring集成依赖:mybatis官方用的,用来在spring项目中创建mybatis的SqlSessionFactory,dao对象
3.创建实体类(用来读/取数据库的数据存放类)
public class Student{
//需要查询或添加的名字
public String name;
//需要查询或添加的数字
public Integer number;
public String job;
}
4.创建dao接口和mapper文件
Dao接口:
//创建的Dao接口
public interface StudentDao{
//定义一个方法
int insertStudent();
List<Student> selectStudent();
}
mapper配置文件:
创建StudentDao.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.ssm.StudentDao">
<insert id="insertStudent">
insert into student value(#{id},#{name},#{job})
</insert>
<select id="selectStudent" resulType="com.study.ssm.Student">
select id,name,job from student
</select>
</mapper>
5.创建mybatis配置文件
创建一个mybatis.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>
<mappers>
<mapper resource="com/xyh/dao/StudentDao.xml"/>
</mappers>
</configuration>
6.创建Spring配置文件:声明mybatis对象交给spring创建
创建一个applicationContext.xml文件
数据源:
<!--数据源(dataSource)对象,连接池使用的德鲁伊连接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/newsDB?characterEncoding=UTF-8"></property>
<property name="username" value="root"></property>
<property name="password" value="12345"></property>
</bean>
SqlSessionFactory:
<!--创建sqlSessionFactory对象
class里用org.mybatis.spring.SqlSessionFactoryBean来创建SqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--给SqlSessionFactoryBean对象使用set赋值-->
<!--读取mybatis主文件配置路径-->
<property name="configLocation" value="classpath:MyBatis-Configuration.xml"></property>
<!--读取创建的数据源-->
<property name="dataSource" ref="mydataSource" />
</bean>
创建Dao对象:
<!--单个Dao对象创建-->
<bean id="studentDao" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--dao对象-->
<property name="mapperInterface" value="com.xyh.dao.StudentDao"/>
<!--SqlSessionFactory对象-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
===========================
<!--MapperScannerConfigurer是调用内部getMapper()方法生成每个dao接口的代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定sqlSessionFactory的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--创建的Dao对象所在的包名,多个包的Dao对象可以用逗号分隔去创建-->
<property name="basePackage" value="study.ssm.namespace.StudentDao"/>
</bean>
声明自定义的Service:
//创建servic实体类来使用dao接口
public class ServicStudentimp implement ServiceStudent{
//dao接口类
private StudentDao studentdao;
//servic方法来使用dao访问数据库
public int addStudent(Student student){
return studentdao.insert(student);
}
}
7.获取Service对象,通过service调用dao进行访问数据库
public class T{
public static void main(String[] args){
String config = "classpath:/applicationcontext.xml";
ApplicationContext ap = ClasspathXmlApplicationContext(config);
ServiceStudent st = (ServiceStudent)ap.getBean("serviceStudent");
st.insert(new Student);
}
}
2.Spring统一处理事务
1)什么是事务
事务是一组sql语句的集合,集合中有很多sql语句,可能是insert,update,select,delete,我们希望这些多个sql语句都能成功,或者都失败,这些sql语句是一致的,作为一个整体执行,就是事务.
2)什么时候使用事务
当操作数据库,涉及到多个表或者多个sql语句的insert,update,delete.需要确保这些语句都是成功才能完成功能,或者都失败,保证操作是符合要求的.
最简单的例子:当你vx发给朋友100元,从你的账户中-100,朋友+100.必须保证这个操作都执行了,或者都失败,不然就会产生问题.
在Java代码中,控制事务一般放在service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句.
3) 处理事务的统一方式
在开发中,访问数据库的框架可能不同,有mybatis,jdbc,hibernate等一些用来访问,而这些访问方法不同,处理事务也不相同.
jdbc访问数据库,处理事务 Connect con; con.commit(); con.rollback();
mybatis访问数据库,处理事务 SqlSession.commit(); SqlSession.rollback();
hibernate访问数据库,处理事务 Session.commit(); Session.rollback();
不同的访问数据库的技术方法不同,并且处理的方式也不同,而且需要掌握多种数据库中事务的处理逻辑,什么时候提交事务,什么时候回滚事务.
4)Spring统一模型
Spring提供一种处理事务的模型,使用统一步骤的方式,完成多种不同数据库访问技术的事务处理.
使用spring的事务处理机制,可以完成mybatis,jdbc,hibernate的事务处理.
1)事务内部提交,回滚事务,使用事务管理器对象,代替你完成commit,rollback,事务管理器是一个接口和其他众多的实现类.
接口:PlatformTransactionManager,定义了事务重要方法 commit , rollback.
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了.
如果你是使用mybatis访问数据库----spring创建的是 DataSourceTransactionManager
如果是hibernate访问数据库----spring创建的是 HibernateTransactionManager
这些都是spring自带的,而我们只需要告诉spring去使用哪种访问技术的实现类就行了,声明需要实现的bean对象.
声明一个需要使用的访问技术的类就ok了,spring就知道你要使用哪种技术去管理事务
<bean id = "xxxx" class = "..DataSourceTransactionManager ">
5)事务定义接口
事务定义接口TransactionDefinition定义了事务描述的三类常量:事务隔离级别,事务传播行为,事务默认超时时限,以及对他们的操作.
- 事务隔离级别
作用:方法在高并发情况下对数据安全性的控制
DEFAULT:使用DB默认的事务隔离级别,Mysql默认是REPEATABLE_READ; Oracle默认的是READ_COMMITTED;
READ_UNCOMMITTED:读未提交) 不解决任何并发问题.
READ_COMMITTED:读已提交) 解决脏读,存在不可重复读和幻读.
REPEATABLE_READ:可重复读) 解决脏读,不可重复读,存在幻读.
SERIALIZABLE:串行化) 不存在并发问题
- 事务传播行为
PROPAGATION_REQUIRED:
指定的方法必须在事务内执行,若当前存在事务,就加入到当前事务中,若当前没有事务,则创建一个新事务.这是spring的默认的事务传播方式.
PROPAGATION_REQUIRED_NEW:
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行.
PROPAGATION_SUPPORTS:
指定的方法总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务完毕.
- 事务默认超时时限
表示一个最长的执行时间,如果执行时间超过则回滚事务,单位是秒,整数值,默认是-1.
6)提交事务,回滚事务的时机
- 当你的业务方法执行成功,没有抛出异常,当方法执行完毕,spring会自动提交事务.
- 当你的业务方法抛出运行异常(包括它的子类)或ERROR,spring执行回滚,调用事务管理器的rollback.
- 当业务方法抛出非运行时异常(受查异常),提交事务.例如 IOExecption,SQLException.
3.使用@Transactional注解增加事务
通过@Transactional注解方式,可以将事务织入到相应的public方法中,实现事务管理.
propagation:事务传播属性,为Propagation的枚举,默认为Propagation.REQUIRED.
isolation:用于设置事务隔离级别,默认值为Ioslation.DEFAULT.
readOnly:设置该方法对数据库的操作是否是只读,默认为false;
timeout:用于设置本操作与数据库连接的超时时限.单位为秒,默认值-1;
rollbackFor:指定需要回滚的异常类,类型为Class[],默认值为空数组.
rollbackForClassName:指定需要回滚的异常类类名,类型为String[],默认为空数组.
noRollbackFor:指定不需要回滚的异常类,类型为Class[],默认为空数组.
noRollbackForClassName:指定不需要回滚的异常类类名,类型为String[].
使用@Transactional步骤:
1.需要声明事务管理器对象
<!--创建一个事务管理器对象,放入到ioc中
这里创建的是mybatis的管理对象-->
<bean id="dataSourceTransaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库,指定数据源-->
<property name="dataSource" ref="myDataSource"/>
</bean>
2.开启事务注解驱动,告诉spring框架,要使用注解的方式管理事务.
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务功能.
<!--开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
transaction-manager:事务管理器对象id-->
<tx:annotation-driven transaction-manager="dataSourceTransaction">
3.在方法上面加入@Transactional.
@Transactional(
//传播属性
propagation=Propagation.REQUIRED,
//隔离级别
isolation = Isolation.DEFAULT,
//该方法对数据库的操作是否是只读的
readOnly = false,
//发生指定的异常就回滚事务
rollbackFor = {
//指定的异常类型
NullPointerException.class
}
)
public void bu(){
}
==================================
//也可以使用全部默认值
//默认抛出运行异常回滚事务
@Transactional
public void A(){}
@Transactional必需加在public方法上!!!!!!
上面使用的是注解的方式进行事务管理,如果有多个方法,或者类甚至是几个包中的方法,一个个写注解不仅冗余,而且还浪费时间,所以我们可以采用xml文件来配置事务管理器.
编程式事务管理
编程式事务管理我们可以通过PlatformTransactionManager实现来进行事务管理,同样的Spring也为我们提供了模板类TransactionTemplate进行事务管理,下面主要介绍模板类,我们需要在xml配置文件中配置.
配置事务管理器:
<!--配置事务管理的模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!--事务管理器id-->
<property name="transactionManager" ref="transactionManager"></property>
<!--定义事务隔离级别,-1表示使用数据库默认级别-->
<property name="isolationLevelName" value="ISOLATION_DEFAULT"></property>
<!--事务传播方式-->
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>
</bean>
基于tx和aop命名空间xml配置:
<!--定义需要被任务管理器管理的方法-->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<!--需要被管理的方法,还有其他的事务属性-->
<tx:method name="insert" propagation="REQUIRED" read-only="false" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--需要被管理类的包名-->
<aop:pointcut id="pointCut" expression="execution (* com.gray.service.*.*(..))"/>
<!--需要切入的任务管理器-->
<aop:advisor advice-ref="advice" pointcut-ref="pointCut"/>
</aop:config>
这样配置完后,所有com.gray.service下的所有类中的insert方法就会被事务管理器管理起来,事务提交都会进行验证,符合要求就会提交事务,不符合则抛出运行异常回滚.