java Sping aop 以及Spring aop 的应用事务管理
1. 回顾
线程死锁概念和如何避免死锁的发生:
线程的通信 wait notify() notify():---Object类
线程的状态: NEW--->start()--->就绪状态---CPU时间片---运行状态RUNNABLE]--->sleep()--->TIMED_WAITING--->wait()---->WAITING----sysn---Blocked---->终止状态[T]
线程池: 常见的线程池种类: 4种和原始
2. 正文 (3W+1H what why where How)
1. 什么是AOP?
2. 为什么使用AOP?
3. 如何使用AOP?
4. 什么是事务?
5. spring如何实现事务管理。
3. 什么是AOP?
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP:它是面向切面编程的语言,它可以让你的业务代码和非业务代码进行隔离。在不改变业务代码的前提下,可以增加新的非业务代码。
4. 为什么使用AOP
5. AOP应用场景
- 记录日志
- 权限校验
- spring事务管理。
6. AOP的结构
AOP要做的三件事在哪里切入,也就是权限校验,等非业务操作在哪些业务 代码中执行;什么时候切入,是业务代码执行前还是执行后;切入后做什 么事,比如做权限校验、日志记录等。
- Aspect: 切面
- PointCut:切点:---方式: 路径表达式 (2)注解形式
- Advice: 处理的时机。
7. 如何使用AOP
案例
public class MathServiceImpl implements MathService {
public double add(double a, double b) {
double result=a+b;
System.out.println("AAA--->The add method result="+result);
return result;
}
public double mul(double a, double b) {
double result=a-b;
System.out.println("AAA--->The mul method result="+result);
return result;
}
public double cheng(double a, double b) {
double result=a*b;
System.out.println("AAA--->The cheng method result="+result);
return result;
}
public double div(double a, double b) {
double result=a/b;
System.out.println("AAA--->The div method result="+result);
return result;
}
}
发现: 我们在每个操作后,都要记录日志,如果后期日志内容发生改变。需要在每个操作后都进行修改。 不利于代码的维护。
我们来使用AOP来解决。
(1)引入相关依赖
<dependencies>
<!--引入spring核心依赖库-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!--引入spring切面依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
</dependencies>
(2)创建一个切面类
@Aspect //标记该类为切面类
@Component //该类对象的创建交于spring容器来管理-----等价于@Service @Controller
public class MyAspect {
@Pointcut(value = "execution(public double com.ykq.aop.MathServiceImpl.add(double, double))") //定义为切点
private void mypointcut(){}
@After(value = "mypointcut()")
public void b(){
System.out.println("AAA--->The add method result");
}
}
(3) 创建一个spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--包扫描-->
<context:component-scan base-package="com.ykq.aop"/>
<!--开启aop切面注解驱动-->
<aop:aspectj-autoproxy/>
</beans>
(4)测试
public class Test {
public static void main(String[] args) {
//加载spring配置文件
ApplicationContext app=new ClassPathXmlApplicationContext("classpath:spring.xml");
MathService mathServiceImpl = (MathService) app.getBean("mathServiceImpl");
System.out.println(mathServiceImpl.add(20, 10));
}
}
使用通配符来统配类路径
@Aspect //标记该类为切面类
@Component //该类对象的创建交于spring容器来管理-----等价于@Service @Controller
public class MyAspect {
//通配符:
/**
* 第一个* : 表示任意修饰符 任意返回类型。
* 第二个* : 表示该包下所有的类。
* 第三个* : 类下所有的方法
* ..: 表示任意参数
*
* 建议包就别使用统配符
*/
@Pointcut(value = "execution(* com.ykq.aop.*.*(..))") //定义为切点
private void mypointcut(){}
@After(value = "mypointcut()")
public void b(){
System.out.println("AAA--->The add method result");
}
}
7.2 注解模式
(1)自定义注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
}
(2)修改切面类
@Pointcut(value = "@annotation(com.ykq.aop.MyAnnotation)") //定义为切点
private void mypointcut2(){}
//在使用MyAnntation注解的方法之后执行内容
@After(value = "mypointcut2()")
public void b(){
System.out.println("AAA--->The add method result");
}
7.3 aop切面通知的类型
package com.aaa.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //标记该类为切面类
@Component //该类对象的创建交于spring容器来管理-----等价于@Service @Controller
public class MyAspect {
//通配符:
/**
* 第一个* : 表示任意修饰符 任意返回类型。
* 第二个* : 表示该包下所有的类。
* 第三个* : 类下所有的方法
* ..: 表示任意参数
*
* 建议包就别使用统配符
*/
@Pointcut(value = "execution(* com.ykq.aop.*.*(..))") //定义为切点
private void mypointcut(){}
@Pointcut(value = "@annotation(com.ykq.aop.MyAnnotation)") //定义为切点
private void mypointcut2(){}
// //在使用MyAnntation注解的方法之后执行内容。无论如何都执行。
// @After(value = "mypointcut()")
// public void a(){
// System.out.println("AAA--->The add method result");
// }
//
// //前置通知:
// @Before(value = "mypointcut()")
// public void b(){
// System.out.println("========方法执行前执行切面的内容 前置通知===========");
// }
//
// //后置返回通知. 碰到return. 如果方法出现异常;这种通知不会被执行
// @AfterReturning(value = "mypointcut()",returning = "r") //returnning它会把方法执行的结果赋值给该变量
// public void afterReturning(Object r){ //参数名必须和returning的名称一致
// System.out.println("~~~~~~~~~~~~~~~~~~后置返回通知~~~~~~~~~~~~~~~~"+r);
// }
//
// //异常通知: 当被切入的方法出现异常时,才会执行
// @AfterThrowing(value = "mypointcut()")
// public void afterThrowable(){
// System.out.println("==============异常通知===========================");
// }
//环绕通知。
@Around(value = "mypointcut()")
public Object around(ProceedingJoinPoint joinPoint){//joinPoint:连接点 理解为被执行的方法对象
System.out.println("业务代码执行前执行的内容======================");
try {
Object result = joinPoint.proceed();//执行你的连接点
System.out.println("方法执行完毕后~~~~~~~~~~~~~~~~~");
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("方法出现异常时执行~~~~~~~~~~~~~");
}finally{
System.out.println("无论如何都会执行");
}
return 0.0;
}
}
@Before 前置通知. 被代理的方法执行前--执行
@After: 后置通知: 被代理的方法执行完后--执行
@AfterReturning: 后置返回通知: 被代理的方法碰到return.--才会执行
@AfterThrowing: 后置异常通知: 当被代理的方法出现异常时--才会执行。
@Around: 环绕通知。
8. spring如何操作事务
8.1 什么是事务?
事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用.
例子: 转账
扣钱和加钱----要么都执行要么都不执行。
JDBC----它模式事务自动提交的。
public class Test {
public static void main(String[] args) {
Connection conn=null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/aaa?serverTimezone=Asia/Shanghai","root","root");
conn.setAutoCommit(false);//设置事务手动提交。
PreparedStatement ps=conn.prepareStatement("update t_user set balance=balance-600 where id=7");
ps.executeUpdate();
//int i=10/0;
PreparedStatement ps2=conn.prepareStatement("update t_user set balance=balance+600 where id=6");
ps2.executeUpdate();
conn.commit();//事务提交
}catch (Exception e){
e.printStackTrace();
//事务回滚
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}finally {
}
}
}
8.2 spring如何实现事务
spring框架一定会提供一个事务切面类。【1】前置通知---开启手动事务 [2]后置返回通知[事务提交] [3]异常通知[事务回滚]
(1)依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<!--spring事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.12.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.15.RELEASE</version>
</dependency>
<!--mybatis的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!--mybatis和spring整合的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<!--druid的连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
(2)spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--springmvc的配置-->
<context:component-scan base-package="com.ddd"/>
<!--spring整合mybatis的配置-->
<!--数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<!--mysql驱动为8.0以后必须使用时区-->
<property name="url" value="jdbc:mysql://localhost:3306/aaa?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--spring封装了一个类SqlSessionFactoryBean类,可以把mybatis中的配置-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:/mapper/*.xml"/>
</bean>
<!--为指定dao包下的接口生产代理实现类-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
<!--它会为com.ykq.dao包下的所有接口生产代理实现类-->
<property name="basePackage" value="com.ddd.dao"/>
</bean>
<!----================以下内容是关于事务的配置===================------>
<!--事务切面管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务管理注解的驱动-->
<tx:annotation-driven/>
</beans>
(3) dao类和xml
public interface UserDao {
//1.修改账号余额
public void updateBalance(@Param("id") int id, @Param("money") double money);
}
<?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">
<!--namespace必须和dao接口的名称一模一样-->
<mapper namespace="com.ddd.dao.UserDao">
<update id="updateBalance">
update t_user set balance=balance+#{money} where id=#{id}
</update>
</mapper>
(4)service
package com.ddd.service.impl;
import com.ddd.dao.UserDao;
import com.ddd.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserServieImpl implements UserService {
@Autowired
private UserDao userDao;
@Transactional //该方法交于spring的事务来管理了---默认spring不识别该注解
public void zhuanzhang(int id, int uid, double money) {
//1.扣钱
userDao.updateBalance(id,-money);
//int c=10/0;
//2.收钱
userDao.updateBalance(uid,money);
}
}
(5)测试:
public class Test {
public static void main(String[] args) {
ApplicationContext app=new ClassPathXmlApplicationContext("classpath:spring.xml");
UserService userServieImpl = (UserService) app.getBean("userServieImpl");
userServieImpl.zhuanzhang(7,6,400);
}
}
本文来自博客园,作者:知行合二为一,转载请注明原文链接:https://www.cnblogs.com/226zjw/p/17636657.html
热门相关:峡谷正能量 异世修真邪君 大妆 异世修真邪君 买妻种田:山野夫君,强势宠!