1.什么是AOP?
2.为什么使用AOP?
3.如何使用AOP?
4.什么是事务?
5.spring如何实现事务管理
1.什么是AOP?
在Java中,AOP(面向切面编程)是一种编程范式,用于在应用程序中实现横切关注点(Cross-cutting Concerns)的模块化和重用。横切关注点是指那些横跨应用程序多个模块的功能或行为,如日志记录、安全性、性能监测等,它们通常割裂了程序的模块性,使得代码难以维护和扩展。
AOP通过对应用程序进行解耦和模块化,使得我们可以将横切关注点从核心业务逻辑中剥离出来,从而实现更好的代码组织和可维护性。
AOP的核心概念是切面(Aspect),切面是横切关注点的模块化单元。切面通过定义一系列的切点(Pointcut)和通知(Advice),在应用程序执行过程中根据切点来插入通知,从而实现对横切关注点的处理。
2.为什么使用AOP?
AOP:面向切面编程,相对于OOP面向对象编程。Spring的AOP的存在目的是为了解耦。AOP可以让一组类共享相同的行为。在OOP中只能继承和实现接口,且类继承只能单继承,阻碍更多行为添加到一组类上,AOP弥补了OOP的不足。还有就是为了清晰的逻辑,让业务逻辑关注业务本身,不用去关心其它的事情,比如事务。
AOP应用场景:
-
统一管理日志:在系统中记录日志是很常见的需求,如果把日志操作放到每个方法中,那么会产生很多冗余代码。使用AOP,我们可以定义一个日志切面,然后通过AOP框架将该切面织入到所有方法中,这样可以避免冗余代码的产生,同时也方便统一管理日志。
-
统一异常处理:在系统中,处理异常也是很常见的需求。如果将异常处理代码放到每个方法中,那么不仅会产生冗余代码,而且也会增加代码的复杂度。使用AOP,我们可以定义一个异常处理切面,将其织入到所有方法中,这样可以方便统一处理异常。
-
安全检查:在系统中进行安全检查是很必要的事情。如果将安全检查代码放到每个方法中,那么会产生很多冗余代码,而且也会增加代码的复杂度。使用AOP,我们可以定义一个安全检查切面,将其织入到所有方法中,这样可以方便统一进行安全检查。
AOP的结构:
AOP要做的三件事:
1,在哪里切入?
也就是权限校验,等非业务操作在哪里执行,
2,什么时候切入?
是业务执行代码后还是执行前
3.切入后做什么是?
比如做权限校验,日志记录等。
Aspect:切面
PointCut:切点:---方式:(1)路径表达式(2)注解形式
Advice:处理的机制。
3.如何使用AOP?
3.1路径表达式
(1)提前写的接口:MathService.java,代码省略,参照以下该接口的实现类。
提前写的实现类:MathServiceImpl.java,代码如下所示。
@Service //<bean id="mathServiceImp" class=""> public class MathServiceImpl implements MathService { public double add(double a, double b) { double result=a+b; return result; } public double mul(double a, double b) { double result=a-b; return result; } public double cheng(double a, double b) { double result=a*b; return result; } public double div(double a, double b) { double result=a/b; return result; } }
(2)引入相关依赖
<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>
(3)创建一个切面类
@Aspect //标记该类为切面类 @Component //该类对象的创建教于spring容器来创建,---等同于@Service,@Controller public class MyAspect { //定义为切点 @Pointcut(value = "execution(public double com.aaa.date0816.d1.MathServiceImpl.add(double,double))") private void test01(){ } @After(value = "test01()") private void test001(){ System.out.println("我是after'''''''''''之后执行"); } }
(4)测试
public class Test01 { public static void main(String[] args) { //加载spring配置文件 ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("classpath:spring.xml"); MathService mathServiceImpl = (MathService)app.getBean("mathServiceImpl"); mathServiceImpl.add(2,9); } }
使用通配符来通配类路径
@Aspect //标记该类为切面类 @Component //该类对象的创建教于spring容器来创建,---等同于@Service,@Controller public class MyAspect { //定义为切点 /** * 第一个* 表示任意修饰符,任意返回值。 * 第二个* 表示该包下的所有类 * 第三个* 表示类下所有的方法 * ..:表示任意参数 * * 建议包就别使用通配符 */ @Pointcut(value = "execution(* com.aaa.date0816.d1.*.*(..))") private void test01(){ } @After(value = "test01()") private void test001(){ System.out.println("我是after'''''''''''之后执行"); } }
3.2注解模式
1.自定义注解
@Target(value = {ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default ""; }
修改切面类
//自定义注解 @Pointcut(value = "@annotation(com.aaa.date0816.d1.MyAnnotation)") private void test02(){ } //在使用MyAnnotation注解的方法之后执行内容 @After(value="test02()") private void test002(){ System.out.println("我是after02~~~~~之后执行我"); }
3.3AOP切面通知的类型
@Before 前置通知. 被代理的方法执行前--执行
@After: 后置通知: 被代理的方法执行完后--执行
@AfterReturning: 后置返回通知: 被代理的方法碰到return.--才会执行
@AfterThrowing: 后置异常通知: 当被代理的方法出现异常时--才会执行。
@Around: 环绕通知。
@Aspect //标记该类为切面类 @Component //该类对象的创建教于spring容器来创建,---等同于@Service,@Controller public class MyAspect { //定义为切点 /** * 第一个* 表示任意修饰符,任意返回值。 * 第二个* 表示该包下的所有类 * 第三个* 表示类下所有的方法 * ..:表示任意参数 * * 建议包就别使用通配符 */ // @Pointcut(value = "execution(* com.aaa.date0816.d1.*.*(..))") // private void test01(){ // // } // // @After(value = "test01()") // private void test001(){ // System.out.println("我是after'''''''''''之后执行"); // } //自定义注解 @Pointcut(value = "@annotation(com.aaa.date0816.d1.MyAnnotation)") private void test02(){ } // //在使用MyAnnotation注解的方法之后执行内容,无论如何都执行 // @After(value="test02()") // private void test002(){ // System.out.println("我是after02~~~~~最后执行我"); // } // // //前置通知 // @Before(value = "test02()") // public void test003(){ // System.out.println("方法执行前执行切面的内容 前置通知~~~~~~~~"); // } // // //后置返回通知, 碰到return ,如果方法出现异常,这种通知不会执行 // @AfterReturning(value = "test02()",returning="r") // public void test004(Object r){//参数名必须和returning的名称一致 // System.out.println("----------后置返回通知"+r); // } // // //异常通知 // @AfterThrowing(value = "test02()") // public void Test005(){ // System.out.println("异常通知---------异常之后会走我"); // } //环绕通知 @Around(value = "test02()") public Object Test006(ProceedingJoinPoint joinPoint){//连接点, 可以理解为被执行方法的对象 System.out.println("开始的我"); try { joinPoint.proceed();//执行你的连接点 System.out.println("执行内容。。。。。。"); } catch (Throwable e) { e.printStackTrace(); System.out.println("方法出现异常执行我"); }finally { System.out.println("无论如何都会执行我"); } return 0.0; } }
4.什么是事务
在Java编程中,事务是指一系列数据库操作的逻辑单元,要么全部成功执行,要么全部回滚。事务的目的是确保一组操作的一致性和完整性,例如在数据库中执行多个操作时,要么全部操作成功提交,要么全部操作失败回滚,以保证数据的正确性。
举例1, A -->B, A向B转钱。
JDBC--模式事务是自动提交
--设置手动提交
public class Test03 { public static void main(String[] args) throws ClassNotFoundException { Connection connection = null; Class<?> aClass = Class.forName("com.mysql.cj.jdbc.Driver"); try { connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql5_1?serverTimezone=Asia/Shanghai", "root", "123456"); connection.setAutoCommit(false);//把自动提交关闭,就是手动提交 PreparedStatement preparedStatement = connection.prepareStatement("update account set money=money-500 where id=8"); PreparedStatement preparedStatement1 = connection.prepareStatement("update account set money=money+500 where id=9"); preparedStatement1.executeUpdate(); preparedStatement.executeUpdate(); connection.commit();//提交 } catch (SQLException e) { e.printStackTrace(); //事务回滚 try { connection.rollback(); } catch (SQLException ex) { throw new RuntimeException(ex); } } } }
5.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的配置--> <!--包扫描 扫描com.ykq以及该包下的子包--> <context:component-scan base-package="com.aaa"/> <!--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/mysql5_1?serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="123456"/> </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.aaa.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 AccountDao { //修改余额 public void updateBalance(@Param("id") int id,@Param("money") int 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.aaa.dao.AccountDao"> <update id="updateBalance"> update account set money=money+#{money} where id=#{id} </update> </mapper>
(4)service
package com.ykq.service.impl; import com.ykq.dao.UserDao; import com.ykq.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @program: qy168-transation * @description: * @author: 闫克起2 * @create: 2023-08-16 17:17 **/ @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); } //---事务管理: (1)自己写事务管理 --自己手动定义事务切面类 // (2)使用spring提供的事务切面类。 // }
(5)测试
public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext con = new ClassPathXmlApplicationContext("spring.xml"); AccountService accountServiceImpl = (AccountService) con.getBean("accountServiceImpl"); accountServiceImpl.zhuanzhang(8,9,500); } }
完