第一章spring框架
spring框架就是把我们项目中的成千上万个管理起来,使得他们之间的关系变得松散,这样使得模块中一个类的变化对其他类的影响小,这样对项目的改动和变化就相对容易一些。也就是解耦合,让我们的程序升级容易,改变容易,增加功能容易。帮助我们创建对象,并且管理对象。
spring功能:
spring第一个功能:ioc 控制反转
IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,
给属性赋值。
正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。–正转。
容器:是一个服务器软件, 一个框架(spring)
使用ioc的目的:
为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
spring配置文件的标志
从类路径加载文件 需要将resources里面的配置文件 复制到classes类加载文件中 具体看我的Mybatis
spring初始的创建
依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
类
实现类
beans.xml配置文件
测试
/**
* spring默认创建对象的时间:在创建spring的容器时,会创建配置文件中的所有的对象。
* spring创建对象:默认调用的是无参数构造方法
*/
@Test
public void test02(){
//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config="beans.xml";
//2.创建表示spring容器的对象, ApplicationContext
// ApplicationContext就是表示Spring容器,通过容器获取对象了
// ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取某个对象, 你要调用对象的方法
//getBean("配置文件中的bean的id值")
SomeService service = (SomeService) ac.getBean("someService");
//使用spring创建好的对象
service.doSome();
}
执行结果:
获取容器对象的数量和名称
第二章利用di对java对象的2种赋值方法
set注入只是调用了student的set方法 所以有set方法就可以执行,没有就不能执行bean里面的propery
基于xml的di 配置文件中
赋值成功。
引用类型的xml的set注入
applicationContext配置文件
输出
构造注入(理解就好)
@Mapper注解的原因
@Mapper 是 Mybatis 的注解,和 Spring 没有关系,@Repository 是 Spring 的注解,用于声明一个 Bean。
使用 Mybatis 有 XML 文件或者注解的两种使用方式,如果是使用 XML 文件的方式,我们需要在配置文件中指定 XML 的位置,这里只研究注解开发的方式。
在 Spring 程序中,Mybatis 需要找到对应的 mapper,在编译的时候动态生成代理类,实现数据库查询功能,所以我们需要在接口上添加 @Mapper 注解。
@Reposity注解的原因
首先是使用配置文件 有一个dao类 dao类中有userdao和它的实现类userdaoimpl类和service类 service类中有service接口类和它的实现类serviceimpl类;
在serviceimpl类中会出现下图:
private UserDao dao = new UserDaoImpl();
这是以往的做法,这样会把程序的值写死,不好添加或者更改程序代码,
所以我们会在applicationContext配置文件中注入Userdaoimpl的对象
也就是
<bean id="userdao" class="com.bjpowernode.dao.impl.UserDaoimpl/>
上面的这一行代替我们上面的那一行代码。此时我们就可以把上面的代码写成
private UserDao dao;
在我们学习了注解之后,我们会用@Reposity代替我们的配置文件的哪一行代码
@Autowired和@Service的来由
上图的声明service对象的配置方法 就相当于是在serviceimpl类
里面写上@Service创建serviceimpl对象,
下面的<property name ="dao" ref="mysqlDao"/>就相当于在
private UserDao dao;代码上添加@Autowired注解实现自动注入
上面的图确确实实实现了自动注入byName的方式,其中下面的与属性名一致的意思是 在student实体类中存在school属性 所以我们在实现自动注入的同时,需要把配置文件中bean的school类对象的id与student类的school类型保持一致。
多配置文件
在school里面就是声明school对象 代码如下
<?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">
<!--School模块所有bean的声明, School模块的配置文件-->
<!--声明School对象-->
<bean id="mySchool" class="com.bjpowernode.ba06.School">
<property name="name" value="航空大学"/>
<property name="address" value="北京的海淀区" />
</bean>
</beans>
在student里面就是声明student对象 代码如下
<?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">
<!--
student模块所有bean的声明
-->
<!--byType-->
<bean id="myStudent" class="com.bjpowernode.ba06.Student" autowire="byType">
<property name="name" value="张飒" />
<property name="age" value="30" />
<!--引用类型-->
注释:<!--<property name="school" ref="mySchool" />-->
</bean>
</beans>
基于注解的di
使用注解不仅要加入刚开始的spring-context依赖 还需要添加spring-aop依赖,当然随着spring-context依赖的加入,我们需要的spring-aop依赖也被间接的自动加入了进来。
注解的使用步骤
1.添加依赖 也就是上面的spring-context依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2.创建类,在指定的类中添加我们所需要的注解
3.在spring的配置文件中加入一个组件扫描器的标签,说明注解在你项目之中的位置。
4.使用注解创建对象,创建容器applicationContext
三种包的指定方式:
@Component是在实体类中的注解 目的是在容器中创建实体类的对象
@value是与其对应的注解方式
@Autowired是在用引用类型的时候添加的注解,同时被引用的实体类也需要添加@Component注解。
几个注解
mapper是mybatis的注解 repository是spring的注解
一般这样直接在项目中引用@Resource注解是报错的
解决办法:Spring项目中缺少javax.annotation包的依赖。在maven配置文件pom.xml中加入依赖。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.2</version>
</dependency>
加入依赖后 运行成功。和@Autowired一样的功能 只不过@Autowired默认是byType注入 而@Resource默认是byName注入
总结
第三章AOP
动态代理是什么?
1.动态代理
实现方式:jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口
cglib动态代理:第三方的工具库,创建代理对象,原理是继承。 通过继承目标类,创建子类。
子类就是代理对象。 要求目标类不能是final的, 方法也不能是final的
动态代理的作用
1)在目标类源代码不改变的情况下,增加功能。
2)减少代码的重复
3)专注业务逻辑代码
4)解耦合,让你的业务功能和日志,事务非业务功能分离。
动态代理的理解
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)就是5个注解
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能 -就是execution
AOP面向切面编程
3.Aop:面向切面编程, 基于动态代理的,可以使用jdk,cglib两种代理方式。
Aop就是动态代理的规范化, 把动态代理的实现步骤,方式都定义好了,
让开发人员用一种统一的方式,使用动态代理。
- AOP(Aspect Orient Programming)面向切面编程
Aspect: 切面,给你的目标类增加的功能,就是切面。 像上面用的日志,事务都是切面。
切面的特点: 一般都是非业务方法,独立使用的。
Orient:面向, 对着。
Programming:编程
oop: 面向对象编程
术语:
1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。
非业务功能, 常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。
2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。
***说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。***
aop的实现
aop是一个规范,是动态的一个规范化,一个标准
aop的技术实现框架:
1.spring:spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop。
我们项目开发中很少使用spring的aop实现。 因为spring的aop比较笨重。
2.aspectJ: 一个开源的专门做aop的框架。spring框架中集成了aspectj框架,通过spring就能使用aspectj的功能。
aspectJ框架实现aop有两种方式:
1.使用xml的配置文件 : 配置全局事务
2.使用注解,我们在项目中要做aop功能,一般都使用注解, aspectj有5个注解。
aspectj框架的使用
1)切面的执行时间, 这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中使用注解表示的。也可以使用xml配置文件中的标签
1)@Before
2)@AfterReturning
3)@Around
4)@AfterThrowing
5)@After
2)表示切面执行的位置,使用的是切入点表达式。
com.service.impl
com.bjpowrnode.service.impl
cn.crm.bjpowernode.service
execution(* *..service.*.*(..))
表示任意返回类型 包或者子包下的service包下的任意包下的任意方法(任意方法参数)
使用aspectj框架实现aop
使用aop:目的是给已经存在的一些类和方法,增加额外的功能。 前提是不改变原来的类的代码。
使用aspectj实现aop的基本步骤:
1.新建maven项目
2.加入依赖
1)spring依赖
2)aspectj依赖
3)junit单元测试
3.创建目标类:接口和他的实现类。
要做的是给类中的方法增加功能
4.创建切面类:普通类
1)在类的上面加入 @Aspect
2)在类中定义方法, 方法就是切面要执行的功能代码
在方法的上面加入aspectj中的通知注解,例如@Before
有需要指定切入点表达式execution()
@Before注解
5.创建spring的配置文件:声明对象,把对象交给容器统一管理
声明对象你可以使用注解或者xml配置文件
1)声明目标对象
2)声明切面类对象
3)声明aspectj框架中的自动代理生成器标签。
自动代理生成器:用来完成代理对象的自动创建功能的。
//@service将目标类放入容器中
//① 在类上使用 @Component 注解 把切面类加入到IOC容器中
//② 在类上使用 @Aspect 注解 使之成为切面类
上图中execution是可以给单个类中的方法增加功能的 而且还可以对多个类增加相同的功能(只通过改变execution里面的参数范围就行)
6.创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
通过代理执行方法,实现aop的功能增强。
注意
当读取配置文件到这个代码的时候
aop:aspectj-autoproxy/
此时就会一个一个找对应可用的切面类的注解,先找@service,哦这个注解不是,继续找,到@Aspect 哎呀找到了就是你,然后就会生成代理对象,执行@Before注解下的方法 @Before注解后面的execution里面是原有的功能方法,所以此时 @Before注解下的方法就会在原有的功能方法之前执行。
JoinPoint
@Afterunning注解
returning 自定义的变量,表示目标方法的返回值的。
自定义的变量名必须和通知方法的形参名一样。也就是下图的样子:
目标类添加doOther方法
上图的return返回值,可以被后面的切面类中的AfterRunning注解下的方法获取 并且改变它的返回值:也即是下图所示:
上图是执行的结果。
@Around
设置前置和后置通知
执行结果:
控制目标方法是否执行(故而经常做事务)
@around的ProceedingJoinPoint 的作用就是用来执行目标方法的,可以通过
ProceedingJoinPoint 调用目标方法的参数 通过获取的参数和自己在around设置的条件信息进行比较 如果相等就执行目标方法,如果不相等就不执行目标方法,从而达到控制目标方法执行的目的。
显然这里是自己设定的zy 肯定等于获取的地有个参数值zy 所以会执行目标方法。
执行结果:
修改目标方法的返回值
原有的返回值
修改
执行
实际上,环绕通知 就是使得接口实现类对象直接调用了around注解下的方法。如下图所示:
1是等于2的 这就是环绕通知的特殊之处。
所以目标方法经常做事务,在目标方法之前开启事务,执行目标方法,然后关闭事务。(用到控制目标方法执行的作用)
@Pointcut辅助execution复用功能
只是一个辅助功能 没有其他重要的作用了。
第四章spring集成Mybatis
:把mybatis框架和spring集成在一起,向一个框架一样使用。也就是说,以后就不用单独的使用mybatis框架了,而是spring集成mybatis,当然通过spring的ioc容器,mybatis就不用自己创建对象了。
回顾mybatis
mybatis使用步骤,对象
1.定义dao接口 ,StudentDao
2.定义mapper文件 StudentDao.xml
3.定义mybatis的主配置文件 mybatis.xml
4.创建dao的代理对象, StudentDao dao = SqlSession.getMapper(StudentDao.class);
List students = dao.selectStudents();
要使用dao对象,需要使用getMapper()方法,
怎么能使用getMapper()方法,需要哪些条件
1.获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
2.创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象
需要SqlSessionFactory对象, 使用Factory能获取SqlSession ,有了SqlSession就能有dao , 目的就是获取dao对象
Factory创建需要读取主配置文件
mybatis自己带的连接池比较弱,我们用功能强大的阿里连接池来代替,我们会使用独立的连接池类替换mybatis默认自己带的, 把连接池类也交给spring创建。所以在spring整合mybatis中,下面的environment就会被pass掉!!!!!!
主配置文件:
1.数据库信息
<environment id="mydev">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--数据库的驱动类名-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--连接数据库的url字符串-->
<property name="url" value="jdbc:mysql://localhost:3306/数据库名"/>
<!--访问数据库的用户名-->
<property name="username" value="root"/>
<!--密码-->
<property name="password" value="123456"/>
</dataSource>
- mapper文件的位置
<mappers>
<mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
<!--<mapper resource="com/bjpowernode/dao/SchoolDao.xml" />-->
</mappers>
spring和mybatis的集成步骤:
1.新建maven项目
2.加入maven的依赖
1)spring依赖
2)mybatis依赖
3)mysql驱动
4)spring的事务的依赖
5)mybatis和spring集成的依赖: mybatis官方体用的,用来在spring项目中创建mybatis
的SqlSesissonFactory,dao对象的
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--spring核心ioc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--做spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis和spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
<!--阿里公司的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</bulid>
3.创建实体类(这里我直接把注解spring注解写上了)
4.创建dao接口和mapper文件(4-5部一样 添加了@Mapper)
5.(添加@Mapper注解)创建mybatis主配置文件
6.创建Service接口和实现类,属性是dao。(这里我还添加了student属性 让student注入进来 这也是为什么 在student加注解 就不用在spring配置文件中写了 只用在配置文件中添加组件扫描器就行)
7.创建spring的配置文件:声明mybatis的对象交给spring创建
1)数据源DataSource
2)SqlSessionFactory
3) Dao对象
4)声明自定义的service
8.创建测试类,获取Service对象,通过service调用dao完成数据库的访问
(1) 数据源的配置(掌握)
使用 JDBC 模板,首先需要配置好数据源,数据源直接以 Bean 的形式配置在 Spring 配
置文件中。根据数据源的不同,其配置方式不同:
Druid 数据源 DruidDataSource
Druid 是阿里的开源数据库连接池。是 Java 语言中最好的数据库连接池。Druid 能够提供强大的监控和扩展功能。
官网:https://github.com/alibaba/druid
使用地址:https://github.com/alibaba/druid/wiki/常见问题
数据源:数据的源头,需要设置数据库url,用户名和密码,此时就相当于一个代理数据库;它包含连接池和连接池管理两个部分;
Java中的数据源就是javax.sql.DataSource。DataSource是Java定义的接口,可以有不同的实现。
连接池:一个连接相当于数据源和dao层的管道,那连接池就相当于存放了很多管道的池子;我们获取连接不从数据源取而是从连接池取。
上面(3)的指定文件mybatis已经由@Mapper所代替,写的时候可以在StudentDao接口类上加上注解即可 就不用在配置文件中写了
Spring 全部配置文件版本(没有写mapper注解的spring整合mybatis配置文件):
最后一行的@Service已经代替 而且studentDao值也通过@Autowired所注入,所以在配置文件中不写。
spring配置文件@Mapper版本
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--下面就是spring配置mybatis的配置文件了 数据源 sqlsessionFactory-->
<!-- 声明数据源,作用是连接数据库的-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close"
>
<!-- set注入给DruidDataSource 提供数据库信息-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
-->
<!-- <property name="configLocation" value="classpath:Mybatis.xml"/>===@Mapper代替
@Mapper只是为我们找到dao的对应的mapper映射文件
而创建dao接口的实现类并创建dao对象 是由下面的MapperScannerConfigurer执行的
所以说 省略上面注释才是与@Mapper的功能是一样的
而下面的不能省略 下面的相当于是Mybatis的动态代理Sqlssion.getMapper创建接口的实现类
-->
</bean>
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名, 包名是dao接口所在的包名。
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="com.bjpowernode.dao"/>
<!-- 如果有多个包那就依次写出来-->
</bean>
<!-- 组件扫描器 扫描指定包中的注解 然后创建他们的对象-->
<context:component-scan base-package="com.bjpowernode.dao"/>
<context:component-scan base-package="com.bjpowernode.Service"/>
<context:component-scan base-package="com.bjpowernode.domain"/>
</beans>
下面的组件扫描器 是为了扫描指定包中的注解。最后一个是实体类,我在测试类中测试插入的方法的时候需要调用ServiceStudent中的int insertStudent(Student student)方法,但是此时的我已经在ServiceStudent类中利用注解自动注入了student对象和它的注入值,所以在测试类的时候需要向insertStudent(Student student)方法添加方法所需要的参数值,但是已经注入到ServiceStudent,已经变成死的了,只能自己在测试类中重新手动创建student对象,这也是注解的坏处,虽然方便但是面对,经常需要改动的程序来说,还是选择配置文件吧。下面的两幅图就是我上面的文字描述:
所以将上图改为
可是这样改就摒弃了实体类的自动注入,在测试类中也就可以自己创建对象添加到数据库中了,现在才想起来前人们说的,实体类不要轻易的加注解,会很固定(但是如果想要更改,那么只需要在实体类中的Value注解赋其他的值就行),如果用户只有一个人 那么可以用,因为你加不进去啊,哈哈,注解限制了我们添加其他的用户。就像下图你再怎么new对象,你程序用的是上上图,那么始终调用的是上上图的student1自动注入(这样就只能通过改实体类中的注解Value来添加其他的用户了)。永远添加不了在下图new的对象。
测试类调用:
小插曲如何在idea中创建模板文件
第四章spring中的事务
代码不多,但是需要掌握和理解。
A、常用的两个实现类
PlatformTransactionManager 接口有两个常用的实现类:
➢ DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
➢ HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。
B、 Spring 的回滚方式(理解)
Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时
提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。
C、 回顾错误与异常(理解)
Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java 的 throw 语句抛出。
Error 是程序在运行过程中出现的无法处理的错误,比如 OutOfMemoryError、
ThreadDeath、NoSuchMethodError 等。当这些错误发生时,程序是无法处理(捕获或抛出)的,JVM 一般会终止线程。
程序在编译和运行时出现的另一类错误称之为异常,它是 JVM 通知程序员的一种方式。通过这种方式,让程序员知道已经或可能出现错误,要求程序员对其进行处理。
异常分为运行时异常与受查异常。
运行时异常,是 RuntimeException 类或其子类,即只有在运行时才出现的异常。如,NullPointerException、ArrayIndexOutOfBoundsException、IllegalArgumentException 等均属于运行时异常。这些异常由 JVM 抛出,在编译时不要求必须处理(捕获或抛出)。但,只要代码编写足够仔细,程序足够健壮,运行时异常是可以避免的。
受查异常,也叫编译时异常,即在代码编写时要求必须捕获或抛出的异常,若不处理,则无法通过编译。如 SQLException,ClassNotFoundException,IOException 等都属于受查异常。
RuntimeException 及其子类以外的异常,均属于受查异常。
当然,用户自定义的 Exception的子类,即用户自定义的异常也属受查异常。程序员在定义异常时,只要未明确声明定义的为 RuntimeException 的子类,那么定义的就是受查异常。
所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传播行为。事务传播行为是加在方法上的。
事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。
四个事务隔离级别在下面有详细介绍
回答问题
1.什么是事务
讲mysql的时候,提出了事务。 事务是指一组sql语句的集合, 集合中有多条sql语句 可能是insert , update ,select ,delete, 我们希望这些多个sql语句都能成功,或者都失败, 这些sql语句的执行是一致的,作为一个整体执行。
2.在什么时候想到使用事务
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。(比如转账,这边有记录记录收到,但是钱却没有打过来,为了保证数据库更新的一致性 所以我们此时就需要保证每一个数据库执行语句都能成功 此时在业务方法上增加事务)
在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句
3.通常使用JDBC访问数据库, 还是mybatis访问数据库怎么处理事务
1.jdbc访问数据库,处理事务 Connection conn ; conn.commit(); conn.rollback();
2.mybatis访问数据库,处理事务, SqlSession.commit(); SqlSession.rollback();
3.hibernate访问数据库,处理事务, Session.commit(); Session.rollback();
4.在3的问题中事务的处理方式,有什么不足
1.不同的数据库访问技术,处理事务的对象,方法不同,
需要了解不同数据库访问技术使用事务的原理
2.掌握多种数据库中事务的处理逻辑。什么时候提交事务,什么时候回顾事务
3.处理事务的多种方法。
总结: 就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法。
5.怎么解决不足
spring提供一种处理事务的统一模型, 能使用统一步骤,方式完成多种不同数据库访问技术的事务处理。
使用spring的事务处理机制,可以完成mybatis访问数据库的事务处理
使用spring的事务处理机制,可以完成hibernate访问数据库的事务处理。
6.处理事务,需要怎么做,做什么
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了
1)事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback
事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的DataSourceTransactionManager
hibernate访问数据库----spring创建的是HibernateTransactionManager
怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用声明就可以了
例如,你要使用mybatis访问数据库,你应该在xml配置文件中
<bean id=“xxx" class="…DataSourceTransactionManager">
问题:上面的一段话??????怎么用注解代替呢
2)你的业务方法需要什么样的事务,说明需要事务的类型。
说明方法需要的事务:
1)事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。
MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。
2) 事务的超时时间(一般少用): 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
单位是秒, 整数值, 默认是 -1.
3)事务的传播行为(这是重点): 控制业务方法是不是有事务的, 是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如何使用的。
PROPAGATION_REQUIRED
PROPAGATION_REQUIRES_NEW
PROPAGATION_SUPPORTS
以上三个需要掌握的
PROPAGATION_MANDATORY
PROPAGATION_NESTED
PROPAGATION_NEVER
PROPAGATION_NOT_SUPPORTED
a、 PROPAGATION_REQUIRED:(最常用的掌握这个就行)
指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。
如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也加入到该事务内执行。若 doSome()方法在调用doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。如下图所示:
b、PROPAGATION_SUPPORTS(查询用的)
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
c、 PROPAGATION_REQUIRES_NEW
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
3)事务提交事务,回滚事务的时机
1)当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit
2)当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException
3) 当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException
总结spring的事务
1.管理事务的是 事务管理和他的实现类
2.spring的事务是一个统一模型
1)指定要使用的事务管理器实现类,使用<bean>
2)指定哪些类,哪些方法需要加入事务的功能
3)指定方法需要的隔离级别,传播行为,超时
你需要告诉spring,你的项目中类信息,方法的名称,方法的事务传播行为。
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,或者都失败,保证操作是符合要求的。(比如转账,这边有记录记录收到,但是钱却没有打过来,为了保证数据库更新的一致性 所以我们此时就需要保证每一个数据库执行语句都能成功 此时在业务方法上增加事务)
解决方法:1.加事务 使用 Spring 的事务注解管理事务(掌握)
spring框架中提供的事务处理方案
1.适合中小项目使用的, 注解方案。
spring框架自己用aop实现给业务方法增加事务的功能, 使用@Transactional注解增加事务。
@Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息等等
使用@Transactional的步骤:
1.需要声明事务管理器对象
因为是mybatis操作数据库 所以class是DataSourceTransactionManager
2.开启事务注解驱动,告诉spring框架,我要使用注解的方式管理事务。
spring使用aop机制,创建@Transactional所在的类代理对象,给方法加入事务的功能。
spring给业务方法加入事务:
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
@Around("你要增加的事务功能的业务方法名称")
Object myAround(){
开启事务,spring给你开启
try{
buy(1001,10);
spring的事务管理器.commit();
}catch(Exception e){
spring的事务管理器.rollback();
}
}
3.在你的方法的上面加入@Trancational
@Transactional 的所有可选属性如下所示:
➢ propagation:用于设置事务传播属性。该属性类型为 Propagation 枚举,默认值为Propagation.REQUIRED。
➢ isolation:用于设置事务的隔离级别(默认值即可)。该属性类型为 Isolation 枚举,默认值为Isolation.DEFAULT。
➢ readOnly(一般不设置 太麻烦 网络等外界因素):用于设置该方法对数据库的操作是否是只读的(查询)。该属性为 boolean,默认值为 false。
➢ timeout:用于设置本操作与数据库连接的超时时限。单位为秒,类型为 int,默认值为
-1,即没有时限。
➢ rollbackFor:指定需要回滚的异常类。意思是发生指定的异常的时候需要回滚。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
➢ rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。
当然,若只有一个异常类时,可以不使用数组。
➢ noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若
只有一个异常类时,可以不使用数组。
➢ noRollbackForClassName:指定不需要回滚的异常类类名。类型为 String[],默认值为空
数组。当然,若只有一个异常类时,可以不使用数组。
需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public
方法,如果加上了注解@Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该
方法中。因为 Spring 会忽略掉所有非 public 方法上的@Transaction 注解。
若@Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。
1.在spring配置文件需要声明事务管理器对象并添加注解驱动代码
<!--使用spring的事务处理-->
<!--1. 声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--连接的数据库, 指定数据源-->
<property name="dataSource" ref="myDataSource" />
</bean>
<!--2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象
transaction-manager:事务管理器对象的id
-->
<tx:annotation-driven transaction-manager="transactionManager" />
注意添加注解驱动的时候是选择以tx结尾的驱动,tx结尾的才是事务
2.在你的方法的上面加入@Trancational并附上该有的属性
图1000:
rollbackFor里面的异常也可以是自己自定义的异常信息,表示执行方法时遇到异常,事务借助aop机制自动回滚,解决多条sql语句执行时出现结果不一致的情况。此时,发生回滚时,那么sql语句便不会向下继续执行,数据库里面的信息也不会发生更新的错误情况,也就是说注解的方法成功。
数据库为什么没有6和7呢,原因是在8是没有异常,成功执行所以数据库发生更新,而6,7都在执行时发生异常,事务进行回滚,将记录撤销了,又因为id设置的是自动增长,所以没有6,7。而执行一次没有异常就直接到8。
上图的属性都是默认值 所以可以在业务方法上只加入@Transactional注解本身就可以啦。与图1000对比。
解决方法:2.使用 AspectJ 的 AOP 配置管理事务(掌握)
适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
实现步骤: 都是在xml配置文件中实现。
1)要使用的是aspectj框架,需要加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2)声明事务管理器对象(由于是mybatos连接数据库,所以用到的是DataSourceTransactionManager)
3) 声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
4) 配置aop:指定哪些哪类要创建代理。
<!--声明式事务处理:和源代码完全分离的-->
<!--1.声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
</bean>
<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
transaction-manager:事务管理器对象的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--tx:attributes:配置事务属性-->
<tx:attributes>
<!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
name:方法名称,1)完整的方法名称,不带有包和类。
2)方法可以使用通配符,* 表示任意字符
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,com.bjpowernode.excep.NotEnoughException"/>
<!--使用通配符,指定很多的方法-->
<tx:method name="add*" propagation="REQUIRES_NEW" />
<!--指定修改方法-->
<tx:method name="modify*" />
<!--删除方法-->
<tx:method name="remove*" />
<!--查询方法,query,search,find-->
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<!--配置切入点表达式:指定哪些包中类,要使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
com.bjpowernode.service
com.crm.service
com.service
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器:关联adivce和pointcut
advice-ref:通知,上面tx:advice哪里的配置
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>
第五章Spring 与 Web
web项目中怎么使用容器对象。
1.做的是javase项目有main方法的,执行代码是执行main方法的,
在main里面创建的容器对象
ApplicationContext ctx = new ClassPathXmlApplicationContext(“applicationContext.xml”);
2.web项目是在tomcat服务器上运行的。 tomcat一起动,项目一直运行的。
既然是web就需要加入jsp servlet依赖
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
当多次发送请求的时候,servlet就会创建多次对此applicationContext.xml里面的对象,每一次请求就创建一次对象,你想,垃圾回收器还没有来得及回收这些用掉的对象,请求又来了,又得再次创建这些对象,这样容易造成内存爆满的情况。
实际上呢,配置文件里面的对象是我们项目需要的所有对象,我们只需要创建一次就可以啦。
如下图所示:
需求:
web项目中容器对象只需要创建一次, 把容器对象放入到全局作用域ServletContext中。
怎么实现:
使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext
监听器作用:
1)创建容器对象,执行 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
2)把容器对象放入到ServletContext, ServletContext.setAttribute(key,ctx)
监听器可以自己创建,也可以使用框架中提供好的ContextLoaderListener
private WebApplicationContext context;
public interface WebApplicationContext extends ApplicationContext
ApplicationContext:javase项目中使用的容器对象
WebApplicationContext:web项目中的使用的容器对象
把创建的容器对象,放入到全局作用域
key: WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
value:this.context
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
weil使用上面的监听器我们需要再次添加一个依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency
创建监听器并使用监听器之后:监听器创建配置文件中的所有对象,然后将这些对象放在全局作用域中,现在我们使用框架给我们的方法来调用这些只需要创建一次的对象,在controllerservlet里面的代码:
//使用框架中的方法,获取容器对象
ServletContext sc = getServletContext();
ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
System.out.println("容器对象的信息========"+ctx);
ApplicationContext:javase项目中使用的容器对象
WebApplicationContext:web项目中的使用的容器对象
创建监听器:目的就是创建容器对象,创建了容器对象,就可以把spring配置文件中的所有的对象创建好了,继而用户发送请求时就可以直接使用对象了。
到此基本的spring框架知识整理完毕,这是我个人看视频的学习笔记,如有错误还请各位大佬多多指正-谢谢。
东西不多,但还是有一点点。