第一部分:SpringAOP 面向切面编程。
SpringAOP 面向切面编程:
面向切面编程:将函数拦截做其他的事而不影响正常的业务流程。
出发点:核心代码与普通代码分离,减少重复代码,降低耦合度。
一,如何让spring项目支持面向切面编程:
1,引入面向切面编程的 jar 包。可以 maven导入或手动导入。
maven 导入: File,projectStructure,libraries,+,from maven,输入拷贝的jar 包名,搜索,出现 Found1 Show1,下三角号,选中,download to lib,ok,ok,apply,ok。
2,配置文件中加入aop的 xsd 文件
3,配置文件中添加 aspect 注解支持。
二,开发代码。
一个 IPayService 接口,一个抽象函数 pay,3个参数 user name money没有返回值。
4个实现类,实现函数 pay,在实现类上加上@Component注解,加载到容器,打印日志的内容( User用支付方式购买了 name 花费了money 钱。)。
测试类中,从容器中获取一个实现类的实例,调用 pay 函数传入参数,右键运行,后台可以看到打印的日志。
在业务线到实现类之后去拦截,在拦截的切面里去做日志。
1,创建一个切面类: new,Aspect,选择注解的方式创建。其实就是一个普通类加上 @Aspect 注解,加上@Component注解,把切面类加载到 Spring 容器中。
2,如何让切面与函数产生联系?
使用注解 @After
@After("execution(* com.qicong.ann.s29.IPayService.pay(..))")
@After:在业务线哪
execution:执行拦截
com.qicong.S29.IPayService.pay():拦截哪个函数
*星号:是否有返回值,有没有都用星号表示
(…):是否有参数,两个点代表有一个或多个参数。(这里自动有三个点)
与函数建立联系,拦截以后执行此函数,记录日志。
jp. getArgs()[0]:代表被拦截的 pay 函数的参数,参数的下标从0开始。
jp. getThis():被拦截函数的实现类。
总结:一个切面编程包含:
@Aspect注解:说明该类是切面类。
Pointcut 切点:说明该在业务线哪拦截函数。
function:拦截以后做什么普通代码。
jointPoint:作为普通代码的参数,是连接点,辅助实现普通代码。
三,切面编程的其他知识。
1,Pointcut 的类型:如果切多个面,按切的顺序执行。
@Before:在核心代码执行前执行。
@After:在核心代码执行后执行。
@AfterReturning:在 After 执行完之后执行。
@Around:环绕执行,把核心代码放到这个切面中执行 自定义执行顺序,函数要加try,catch。
@ AfterThrowing:抛出异常了切它
@ AfterTransaction:整个事务结束了切它。
例如:
@Before:支付前先切出来验证一下。
pay():支付。(核心代码)
@After:记录日志。
@AfterReturning:登出。
一般不会对一个函数用上所有的切点。
@After("execution(* com.qicong.ann.s29.IPayService.pay(..))")
public void recode(JoinPoint jp){
//logger
System.out.println(jp.getArgs()[0]
+ ",用 " + jp.getThis()
+ "购买了 " + jp.getArgs()[1]
+ ",共花费了 " + jp.getArgs()[2] + " 钱 。"
);
}
@Before("execution(* com.qicong.ann.s29.IPayService.pay(..))")
public void beforePay(JoinPoint jp){
System.out.println("pay verify。。。。");
}
@AfterReturning("execution(* com.qicong.ann.s29.IPayService.pay(..))")
public void afterReturn(JoinPoint jp){
System.out.println("logout 。。。。");
}
@Around("execution(* com.qicong.ann.s29.IPayService.pay(..))")
public void around(ProceedingJoinPoint jp){
System.out.println("around start ------------------");
try {
jp.proceed();//就是执行pay函数
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("around end ------------------");
}
2,简化代码:
声明一个切点 Pointcut 简化 execution里的内容,其他具体的切点去引用这个函数。
@AfterReturning("pointcut()")
public void afterReturn(JoinPoint jp){
System.out.println("logout 。。。。");
}
@Pointcut("execution(* com.qicong.ann.s29.IPayService.pay(..))")
public void pointcut(){ //没有具体的内容
}
3,切面顺序:在不同类对同一个核心代码做 @Before 类型的拦截,先执行哪一个切面,默认这两个切面类谁先加载到容器就先执行谁。
可以在类上使用 @Order 注解来改变默认的实现的顺序。注解里面的数字越小表示优先级越高,就先切出来实现。
第二部分:JDBC: JavaDatabaseConnectivity. Java数据库连接
1,也可以通过命令行或客户端 Navicat 连接数据库。
用IP地址,端口号,用户名密码连接数据库。
可以从客户端上看远程数据库和本地数据库连接信息不同。
2,访问数据库操作的简化:自己写jdbc,使用 SpringJDBCTemplate,使用 Mybatis 或 Hibernate 框架访问数据库。
自己写JDBC
1,把连接数据库的驱动加到项目中:引入 jar 包。
手动引入jar包,把老师项目的 jar 包复制一份,放到自己项目的 lib 目录中,拷贝以后 file, projectStructure,+,选中,ok,apply,ok。
2,创建JDBCText Java类:
Java 反射机制加载驱动程序。不同数据库驱动不同。
链接数据库的 url。
用Java sql 下Connection连接, 用 mysql 驱动程序,用连接数据库的 url 连接数据库,用户名和密码访问数据库。
查询操作:写sql 语句。传 sql 到的 prepare statement去执行sql 语句。返回的是一个集合,去遍历这个集合,打印查询出来的第1个字段的内容。
修改操作:写修改的sql 语句。传 sql 到的 prepare statement 去执行sql 语句。将头像数据修改成 myHeader.png,修改第2条数据的头像。
关闭链接。
使用 SpringJDBCTemplate 连接池去访问数据库:
1,概述:使用 statement 去执行 sql 的代码。将链接和数据源放到连接池中去维护避免频繁的创建,关闭链接。直接用连接池的API去访问数据库就好了。
2,使用的是C3P0连接池,引入 C3P0 的 jar 包。
3,将数据源和连接池配置到配置文件中。
数据源中:注入基本的:数据库的驱动,连接数据库的 url,访问数据库的用户名和密码。
连接池中:输入数据源。
4,代码实战:
从容器中获取连接池的实例,查询操作:查询的sql,连接池去执行 sql ,返回的String类型的 list 用 iterator 遍历出来,输出出来。修改操作。
JDBC和JDBCTemplate,不常用,常用myBatis ORM框架访问数据库。
第三部分:Spring 事务
一,Spring 事务介绍
1,@Transactional注解:保证事务修饰的范围内,所有操作要么全部成功,要么全部失败。一旦事务内抛出异常数据回滚。对数据库的单次操作就是最小的事务。
比如:
@Transactional
function operate_sql{
sql: A -= 10000;
//Error,回滚
sql: B += 10000;
}
2,事务的特点(ACID),笔试面试常问。
原子性(atomicity):所有的操作就是一个原子,要么都成功,要么都失败。
一致性(consistency):所有的操作完成,事务被提交,开发要与业务需求一致。
隔离性( Isolation):并发事物间隔离,并发事务指几个事务同时访问一个数据集。
持久性(durability):事务完成以后,结果会被永久的储存。
3,让 Spring支持事务的配置(SpringBoot 中可以省略,直接使用@Transactional 注解):
配置文件中:添加 xsd 支持;配置事务管理器;启动事务注解驱动。
事务指对数据库的操作,应同时成功或同时失败,所以要把数据源注入到事务管理器。
4,代码实战:
注入数据库连接池,事务中通过连接池,执行两条更新数据库的 sql 语句,模拟实现转账。main 函数中获取当前类的实例,调用当前事务函数,打开数据库客户端查看运行的结果。
两条 sql 中插入一条 int i = 1/0;
结论是:不加事务的注解,不能保证实现转账的需求同时成功或同时失败。
二,Spring 事务的传播行为
1,加 @Transactional 注解就是事务。
2,一个事务中包含另一个事务,叫事务的嵌套关系。
3,事务中的事务,里面的事务,叫内部事务,外面的事务,叫外部事务。
4,调用哪个函数,哪个函数为当前事务。
5,事务的传播行为指的是内部事务和外部事务的关系,主要有两种。
@Transactional(propogation=Propogation. REQUIRED)
REQUIRED:内部事务加入当前事务,当前事务没有就运行内部事务,内部事务与外部事务一起成功或一起失败。
REQUIRED_NEW:内部事务是独立事务,外部事务成功或失败,内部事务都不会受到影响。
当内部事务运行成功,外部事务到哪个节点运行失败,内部事务不受影响,外部事务挂起。
当内部事务成功,外部事务也成功,那么当前事务运行成功。
当内部事务运行失败,外部事务要么不执行,要么一起失败。
三,Spring 事务的隔离级别
1,常见的并发访问数据的问题:
脏读:数据没有提交完成时,读取数据导致脏读。
(数据没有提交完成时,读取了提交成功后的数据,如果数据提交失败,那么读取的数据与实际数据不同。)
不可重复读:重复读发现数据不一致,叫不可重复读。
(a读完,b更新, a不知道b更新了数据, a 再读时两次读的结果不一样。)
幻读:更新数据库时出现了幻觉。
( a 准备更新2条数据,并不知道 b 插入了3条数据,原本打算只更新 2 条数据的a,变成更新5条数据 ,a 出现了幻觉。)
2,事务隔离级别有4种类型,根据业务需求选择:
READ_UNCOMMITTED:允许读取未提交完成的数据,是最低隔离级别。
READ_COMMITTED:读取已提交的数据。
可阻止脏读,其他两个仍可能发生。
a 更新时,b 来读,让 b 挂起,等 a 更新完,再让 b 来读。
REPEATABLE_READ:可重复读,对同一字段多次读取一致。阻止不可重复读,幻读仍有可能发生。
SERIALIZABLE:可串行化。最高隔离级别,并发事务串行化,完全遵循ACID事务的特性,可防止常见的问题,但系统会变慢。
第四部分:Spring 的测试用例
1,概述:项目发布之前可以通过测试用例,对部分功能自动化测试验证,保证项目质量。通过测试用例来断言/验证,将程序运行的结果和想要的结果做匹配,预期不对,就单独针对那个功能做修改。
2,代码实战:
添加项目依赖的测试用例的 jar 包。创建 java 类。
@RunWith(Spring…)说明这个类是测试用例的类。
@ContextConfiguration(“classpath:applicationContext”)
用了Spring的东西,就要加载Spring的配置文件。
@Autowired 注入 JDBCTemplate,访问数据库,操作数据。
JDBCTemplate 去执行查询数据库的 sql ,返回一个list,iterator 把 list 遍历出来,输出到后台。
@Text :代替main,自动运行函数。