个人网站的订单,网络推广哪个公司好,网站升级对外解决方案,wordpress注册界面SpringBoot 整合 AOP
动态代理技术 JDK 动态代理 JDK 动态代理是 Java 自带的一种代理方式。它要求目标类必须有接口#xff0c;基于这个接口#xff0c;JDK 在运行时会动态生成一个代理对象。这个代理对象和目标对象就像 “拜把子” 的兄弟#xff0c;因为它们都实现了相同…SpringBoot 整合 AOP
动态代理技术 JDK 动态代理 JDK 动态代理是 Java 自带的一种代理方式。它要求目标类必须有接口基于这个接口JDK 在运行时会动态生成一个代理对象。这个代理对象和目标对象就像 “拜把子” 的兄弟因为它们都实现了相同的接口能以相同的方式被调用只是代理对象在调用真正的方法前后可以添加额外的逻辑。 . CGLIB 动态代理 CGLIB 动态代理是另一种代理技术。它的厉害之处在于不需要目标类有接口通过让代理类继承目标类来实现代理就好比代理类 “认” 目标类为 “干爹”。这样代理类就能在目标类方法调用的前后插入自己的逻辑从而对目标类的行为进行增强。 Spring AoP
AoP 概述 AOP 核心概念 切入点实际被 AOP 控制的方法需要被增强的方法通知封装共享功能的方法就是通知 AoP 通知类型 环绕就是一部分在目标方法之前一部分在之后 它的返回值代表的是原始方法执行完毕的返回值 try {前置通知 Before目标方法执行返回后通知 AfterReturning
} catch() {异常后通知 AfterThrowing
} finally {后置通知 After
}AoP 通知顺序 AoP 切点表达式 execution AoP 切点表达式重用
第一种创建存储切点的类维护 创建一个存储切点的类 单独维护切点表达式 execution 使用类全限定符.方法名() 切点维护类
Component
public class MyPointCut {Pointcut(execution(* com.atguigu.service.impl.*.*(..)))public void pc(){}重用类演示类 Before(com.atguigu.pointcut.MyPointCut.pc())public void start() {System.out.println(方法开始了);}After(com.atguigu.pointcut.MyPointCut.pc())public void after() {System.out.println(方法结束了);}AfterThrowing(com.atguigu.pointcut.MyPointCut.pc())public void error() {System.out.println(方法报错了);}第二种当前类中提取表达式 定义一个空方法 注解 Pointcut() 增强注解中引用切点 直接调用方法名 Aspect
Component
public class UserServiceAspect {// 定义一个空方法使用Pointcut注解来定义切点表达式这里表示匹配UserService接口下的所有方法Pointcut(execution(* com.example.demo.service.UserService.*(..)))public void userServicePointcut() {}// 在前置通知中复用上面定义的切点表达式直接写切点方法名即可Before(userServicePointcut())public void beforeAddUser() {System.out.println(在执行UserService的方法前执行的逻辑);}AoP 基本使用
底层技术组成 动态代理InvocationHandler JDK原生 的实现方式需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口兄弟两个拜把子模式。cglib通过继承被代理的目标类认干爹模式实现代理所以不需要目标类实现接口。AspectJSpringAOP借用了AspectJ中的AOP注解。也就是说 Before 等注解都来自 AspectJ 默认代理方式演示JDK原生 第一步导入依赖 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency第二步准备接口和实现类 public interface Calculator {int add(int i, int j);
}Component
public class CalculatorPureImpl implements Calculator {Overridepublic int add(int i, int j) {int result i j;return result;}
}第三步声明切面类 Aspect 切面类要加入 IoC 容器 并且要使用 Aspect 声明为切面类 Aspect
Component
public class LogAdvice {Pointcut(execution(* com.mangfu.Calculator.*(..)))public void Mypointcut(){}Before(Mypointcut())public void testBefore() {System.out.println(before);}AfterReturning(Mypointcut())public void testAfterReturning() {System.out.println(afterReturning);}AfterThrowing(Mypointcut())public void testAfterThrowing() {System.out.println(afterThrowing);}After(Mypointcut())public void testAfter() {System.out.println(after);}
}
环绕通知 就相当于直接进入切面类执行 接口和实现类
public interface Calculator {int add(int i, int j);
}Component
public class CalculatorPureImpl implements Calculator {Overridepublic int add(int i, int j) {int result i j;return result;}
}配置类开启 AspectJ
Configuration
ComponentScan(com.mangfu)
EnableAspectJAutoProxy
public class MyConfig {
}切面类 ProceedingJoinPoint joinPoint目标方法对象jointPoint.getArgs(): 获取目标方法运行的参数joinPoint.proceed(args): 执行目标方法 Aspect
Component
public class LogAdvice {Around(execution(* com.mangfu.Calculator.*(..)))public Object around(ProceedingJoinPoint joinPoint) {Object[] args joinPoint.getArgs(); //获取目标方法运行的参数Object result null; //用于接收目标参数的返回值try {System.out.println(Before);result joinPoint.proceed(args); //调用目标方法System.out.println(AfterReturning);} catch (Throwable e) {System.out.println(Afterthrowing);throw new RuntimeException(e);} finally {System.out.println(After);}return result;}
}测试类
SpringJUnitConfig(MyConfig.class)
public class MyTest {Autowiredprivate Calculator calculator;Testpublic void test() {System.out.println(calculator.add(1, 2));}
}JoinPoint 详解 CGLib 动态代理生效情况 在目标类没有实现任何接口的情况下Spring会自动使用cglib技术实现代理。 如果目标类有接口, 选择使用 jdk 动态代理实现目标接口如果目标类没有接口, 选择 cglib 动态代理继承目标对象如果有接口, 就用接口接值如果没有接口, 就用类进行接值 没实现接口的实体类和切面类
Component
public class Calculator {public int add(int i, int j) {int result i j;return result;}
}Aspect
Component
public class testBefore {Before(execution(* com.mangfu2.Calculator.add(..)) )public void before(){System.out.println(test sucessful);}
}测试类
SpringJUnitConfig(MyConfig.class)
public class MyTest {//建议用接口取。防止后期取不到代理类对象//正常aop - aop底层选择代理 - 选择jdk代理 - 根据接口生成代理类 - 代理对象和目标对象 是拜把子 兄弟关系。不是同一个//这里实现类没有实现接口所以用 CGLib 动态代理//aop - ioc 容器中真正存储的是代理对象不是目标对象Autowiredprivate Calculator calculator;Testpublic void test(){calculator.add(1,2);}}Spring AoP 对获取 Bean 的影响
JDK 原生代理 声明一个接口其仅有一个实现类同时创建切面类对该接口的实现类应用通知 按接口类型获取 bean 可正常获取。按类获取 bean 则无法获取原因在于应用切面后实际存放在 IOC 容器中的是代理类对象目标类本身并未放入 IOC 容器所以依据目标类类型无法从 IOC 容器中找到相应对象 CGLib 代理 声明一个类创建一个切面类对上面的类应用通知 根据类获取 bean能获取到 Spring TX 声明式事务
声明式事务概念 定义通过注解或 XML 配置的方式控制事务的提交和回滚具体事务实现由第三方框架负责开发者只需添加相应配置无需直接进行事务操作。优点可将事务控制和业务逻辑分离提升代码的可读性与可维护性。 spring-tx: 包含声明式事务实现的基本规范事务管理器规范接口和事务增强等等spring-jdbc: 包含DataSource方式事务管理器实现类DataSourceTransactionManagerspring-orm: 包含其他持久层框架的事务管理器实现类例如Hibernate/Jpa等 . spring-tx 就是 Spring 官方提供的事务接口各大持久层框架需要自己实现这个接口而 spring-jdbc 就是 jdbc, jdbcTemplate, mybatis 的实现类 DataSourceTranscationManager。spring-orm 就是 Hibernate/Jpa 等持久层框架的实现类 声明式事务基本使用 SpringBoot 项目会自动配置一个 DataSourceTransactionManager所以我们只需在方法或者类加上 Transactional 注解就自动纳入 Spring 的事务管理了 第一步加入依赖 jdbc, jdbcTemplate, mybatis 这三个持久层框架情况
!-- 第三方实现 spring 事务接口的类的依赖--
!-- spring-jdbc --dependencygroupIdorg.springframework/groupIdartifactIdspring-jdbc/artifactIdversion6.0.6/version/dependency如果是 Hibernate 等其他持久层框架就用 spring-jdbc 换成 spring-orm 依赖
第二步给需要事务的地方加上 Transactional 注解 Treanscational 加在类上就是类里所有方法开启事务。加在方法上就单独那个方法有事务 Dao层
Repository
public class StudentDao {Autowiredprivate JdbcTemplate jdbcTemplate;public void updateNameById(String name,Integer id){String sql update students set name ? where id ? ;;int rows jdbcTemplate.update(sql, name, id);}public void updateAgeById(Integer age,Integer id){String sql update students set age ? where id ? ;;jdbcTemplate.update(sql,age,id);}
}Service 层
Service
public class StudentService {Autowiredprivate StudentDao studentDao;/*** 添加事务:* Transctional* 位置: 方法 | 类上* 方法: 当前方法有事务* 类上: 泪下所有方法都有事务*///这里有自动事务了。所以可以报错可以自动回滚Transactionalpublic void changeInfo(){studentDao.updateAgeById(88,1);int i 1/0;System.out.println(-----------);studentDao.updateNameById(test1,1);}
}测试类
SpringJUnitConfig(JavaConfig.class)
public class TxTest {Autowired private StudentService studentService;Testpublic void testTx(){studentService.changeInfo();}
}事务的属性
事务属性只读 只读模式可以提升查询事务的效率一般情况都是通过类添加注解添加事务类下的所有方法都有事务而查询方法一般不用添加事务。这个时候可以再次添加事务注解设置为只读提高效率查询效率 Transactional(readOnly ...)
Transactional
Service
public class StudentService {Autowiredprivate StudentDao studentDao;//查询 没有必要添加事务。这里设置为只读提高效率Transactional(readOnly true)public void getStudentInfo() {//获取学生信息 查询数据库 不修改}}事务属性超时 事务在执行过程中有可能因为遇到某些问题导致程序卡住从而长时间占用数据库资源。而长时间占用资源大概率是因为程序运行出现了问题可能是Java程序或MySQL数据库或网络连接等等。此时这个很可能出问题的程序应该被回滚撤销它已做的操作事务结束把资源让出来让其他正常程序可以执行。概括来说就是一句话超时回滚释放资源。 Transactional(timeout ...)
Transactional(timeout 3)
Service
public class StudentService {Autowiredprivate StudentDao studentDao;public void changeInfo() {studentDao.updateAgeById(88,1);System.out.println(-----------);try {Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}studentDao.updateNameById(test2,1);}//因为这个注解会覆盖掉类上注解。所以要再设置一遍Transactional(readOnly true, timeout 3)public void getStudentInfo() {}}事务属性回滚 默认情况下发生运行时 (RuntimeException) 异常事务才回滚所以我们可以指定 Exception 异常来控制所有异常都回滚 roollbackFor 回滚的异常范围设置的异常都回滚noRollbackFor 不回滚的异常范围控制某个异常不回滚 //所有异常都回滚除了 FileNotFoundException
Transactional(rollbackFor Exception.class, noRollbackFor FileNotFoundException.class)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(99,1);new FileInputStream(xxxx);studentDao.updateNameById(test2,1);}
事务属性事务隔离级别 事务并发可能引发的问题 脏读一个事务读取另一个事务未提交的数据不可重复读一个事务就是读取了另一个事务提交的修改数据幻读: 一个事务读取了另一个事务提交的插入数据 事务隔离级别 读未提交Read Uncommitted事务可以读取未被提交的数据容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全一般不用。读已提交Read Committed事务只能读取已经提交的数据可以避免脏读问题但可能引发不可重复读和幻读。可重复读Repeatable Read在一个事务中相同的查询将返回相同的结果集不管其他事务对数据做了什么修改。可以避免脏读和不可重复读但仍有幻读的问题。串行化Serializable最高的隔离级别完全禁止了并发只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题但效率较低不适用于高并发场景。 . 不同的隔离级别适用于不同的场景需要根据实际业务需求进行选择和调整。 建议设置第二个隔离级别 isolation Isolation.事务的隔离级别 READ_UNCOMMITTED 读未提交READ_COMMITTED 读已提交REPEATABLE_READ 可重复读SERIALIZABLE 串行化 //设置事务隔离级别为可串行化
Transactional(isolation Isolation.SERIALIZABLE)public void changeInfo() throws FileNotFoundException {studentDao.updateAgeById(99,1);new FileInputStream(xxxx);studentDao.updateNameById(test2,1);}
事务属性事务传播行为 propagation 传播规则【默认是 Propagation.REQUIRED】 我们一般使用默认就行
名称【传播规则】含义REQUIRED如果父方法有事务就加入如果没有就新建自己独立父方法有事务报错子方法会回滚REQUIRES_NEW不管父方法是否有事务我都新建事务都是独立的即使父方法有有事务报错。两个子方法也不会回滚 就是用父方法 topService() 调用 子方法changeAge() 和 changeName()。这两个子方法是否会进行回滚