• Spring (2) AOP


    目录

    1 代理(Proxy)模式

    1.1 静态代理

    1.2 动态代理

    1.2.1 基于接口的动态代理

    1.2.2 基于子类的动态代理

    2 AOP

    2.1 注解开发

    3 声明式事务

    3.1 事务(transaction)

    3.2 spring声明式事务


    1 代理(Proxy)模式

            一种设计模式,它的作用是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接调用,而是通过代理类间接调用

    1.1 静态代理

            代理类持有目标对象,并且实现相同的接口,代理类在目标方法调用前后进行额外的操作,代理类和目标对象的关系是在编译期写死的

            静态代理工作原理:

                    (1) 定义一个接口(或抽象类),目标对象实现这个接口

                    (2) 创建一个代理类,实现目标接口,并持有目标对象

                    (3) 代理类重写目标接口方法,在重写方法中调用目标对象同名方法

                    (4) 代理类的重写方法在目标对象同名方法前后做额外操作

            示例代码:

    1. // 接口
    2. public interface Travel {
    3. void vacation();
    4. }
    5. // 目标对象
    6. public class TravelImpl implements Travel{
    7. @Override
    8. public void vacation() {
    9. System.out.println("度假...");
    10. }
    11. }
    12. // 代理类
    13. public class TravelProxy implements Travel {
    14. private TravelImpl travel; //引入目标对象
    15. public void setTravel(TravelImpl travel) {
    16. this.travel = travel;
    17. }
    18. @Override
    19. public void vacation() {
    20. System.out.println("去的车票购买");
    21. travel.vacation();
    22. System.out.println("回的车票购买");
    23. }
    24. }
    25. // 测试
    26. @Test
    27. public void travelTest() throws Exception {
    28. TravelProxy travel = new TravelProxy();
    29. travel.setTravel(new TravelImpl());
    30. travel.vacation();
    31. // 去的车票购买
    32. // 度假...
    33. // 回的车票购买
    34. }

    1.2 动态代理

            JAVA动态代理是基于反射实现的,代理类的创建和方法都是在运行期完成的

    1.2.1 基于接口的动态代理

            原理: 生成一个方法被增强接口的实现类的代理对象

            涉及的类: java.lang.reflect.Proxy

            涉及的接口: java.lang.reflect.InvocationHandler

            要求: 被代理的类至少实现一个接口

            示例代码:

    1. @Test
    2. public void travelTest() throws Exception {
    3. // 目标对象
    4. TravelImpl travelImpl = new TravelImpl();
    5. /**
    6. * newProxyInstance参数说明:
    7. * ClassLoader(类加载器):将被代理类的字节码文件加载到JVM
    8. * Class[](字节码数组):被代理类实现的所有接口的字节码数组
    9. * InvocationHandler(拦截器,被代理类实现的任何"接口方法"都会被拦截)
    10. * proxy:代理对象的引用(一般不用)
    11. * method:被代理对象的方法对象(反射原理)
    12. * args:当前方法所需参数
    13. */
    14. // 生成代理对象
    15. Travel travelProxy = (Travel) Proxy.newProxyInstance(TravelImpl.class.getClassLoader(), TravelImpl.class.getInterfaces(), new InvocationHandler() {
    16. @Override
    17. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    18. // 获取方法参数
    19. Object myName = args[0];
    20. // 获取方法名
    21. String methodName = method.getName();
    22. // 执行原始方法(有返回值)
    23. //参数1:目标对象
    24. //参数2,方法参数
    25. System.out.println("去的车票购买");
    26. Object methodReturn = method.invoke(travelImpl, args);
    27. System.out.println("回的车票购买");
    28. // 返回参数
    29. return methodReturn;
    30. }
    31. });
    32. // 执行代理对象
    33. travelProxy.vacation();
    34. //去的车票购买
    35. //度假...
    36. //回的车票购买
    37. travelProxy.study();
    38. //去的车票购买
    39. //去求学...
    40. //回的车票购买
    41. }

    1.2.2 基于子类的动态代理

            原理: 生成一个方法被增强该类的子类的代理对象

            引入jar包: cglib

            涉及的类: net.sf.cglib.proxy.Enhancer

            要求: 被代理的类不能是最终类(final,不能被继承)

            示例代码:

    1. @Test
    2. public void staticProxyTest() {
    3. // 目标对象
    4. TravelImpl travelImpl = new TravelImpl();
    5. // 代理对象
    6. TravelImpl travelProxy = (TravelImpl) Enhancer.create(TravelImpl.class, new MethodInterceptor() {
    7. @Override
    8. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    9. System.out.println("去的车票购买");
    10. Object methodReturn = method.invoke(travelImpl, objects);
    11. System.out.println("回的车票购买");
    12. return methodReturn;
    13. }
    14. });
    15. // 执行代理对象
    16. travelProxy.vacation();
    17. }

    2 AOP

            底层核心原理动态代理

            AOP(Aspect Oriented Programming)面向切面编程(使用动态代理技术,在不修改源码的基础上,给程序添加额外的功能)

    2.1 注解开发

    注解说明
    @Aspect标识类为"切面类"
    @Pointcut("execution(表达式)")

    切入点表达式

    例: public * xyz.aboluo.service.impl.*.*(..)

    @Before前置通知
    @AfterReturning后置通知
    @AfterThrowing异常通知
    @After最终通知(总会执行)
    @Around环绕通知

            (1) 引入依赖

            (2) 切面类

    1. @Component
    2. @Aspect //标识该类为一个"切面类"
    3. @Order(1) //针对"多切面",数字越小"越先执行,越后结束"
    4. public class LogAdvice {
    5. @Pointcut("execution(public * xyz.aboluo.controller.*.*(..))")
    6. private void pc1() {
    7. }
    8. @Pointcut("execution(public * xyz.aboluo.service.impl.*.*(..))")
    9. private void pc2() {
    10. }
    11. @Pointcut("pc1() || pc2()")
    12. private void pc3() {
    13. }
    14. //前置通知
    15. @Before("pc2()")
    16. public void beforeAdvice() {
    17. System.out.println("前置通知,方法前执行");
    18. }
    19. //后置通知
    20. @AfterReturning("pc2()")
    21. public void afterReturningAdvice() {
    22. System.out.println("后置通知,方法后执行");
    23. }
    24. //异常通知
    25. @AfterThrowing("pc2()")
    26. public void afterThrowingAdvice() {
    27. System.out.println("异常通知,异常时执行");
    28. }
    29. //最终通知
    30. @After("pc2()")
    31. public void afterAdvice() {
    32. System.out.println("最终通知,始终执行");
    33. }
    34. //环绕通知
    35. @Around("pc2()")
    36. public Object aroundAdvice(ProceedingJoinPoint pjp) {
    37. Object methodReturn = null;
    38. try {
    39. System.out.println("前置通知");
    40. Object target = pjp.getTarget(); //获取被代理对象
    41. String methodName = pjp.getSignature().getName(); //获取当前方法名
    42. Object[] args = pjp.getArgs(); //获取当前方法执行所需的参数
    43. methodReturn = pjp.proceed(args); //原方法执行
    44. System.out.println("后置通知");
    45. } catch (Throwable throwable) {
    46. System.out.println("异常通知");
    47. } finally {
    48. System.out.println("最终通知");
    49. }
    50. return methodReturn;
    51. }
    52. }

            (3) 切面执行顺序

                    单切面: @Around --> @Before --> 原始方法 -->@After --> @AfterReturning --> @Throwing

                    多切面: 使用@Order(1)注解,数字越"越先执行,越后结束"

    3 声明式事务

    3.1 事务(transaction)

            事务是数据库的,并不是java的,一个事务中的所有操作,要么全部执行,要么全部不执行

            事务的4大特性:

                    (A) 原子性: 十五中的操作要么都生效,要么都不生效

                    (C) 一致性: 事务执行前后,数据库中定义的约束,业务规则等保持不变

                    (I)  隔离性: 并发访问数据库时,多个事物之间互不影响

                    (D) 持久性: 如果事务被成功提交,数据会持久保存在数据库

            原子性、一致性、持久性都是单事务; 隔离性是多事务并发

            脏读: 读取到另一个事务未提交的数据

            不可重复读: 受其它事务update的干扰(期望是操作修改之前的数据)

            虚读/幻读: 读取不到其它事务的insert/delete,但操作数据时提示"数据异常"等问题

    隔离级别脏读不可重复读虚读/幻读
    read uncommitted
    read committed (oracle默认)
    repeatable read (mysql默认)
    searializable

    3.2 spring声明式事务

            底层原始是利用了AOP

            (1) 添加依赖

            (2) 配置事务管理器(注入数据源),开启spring对声明式事务注解支持

            (3) 在类/方法(方法优先级更高)上添加@Transactional注解

    1. /**
    2. * readOnly=true
    3. * 只读:只能查询,不能增删改
    4. * timeout=10 //默认-1(永不超时)
    5. * 超时(秒):响应超时,回滚
    6. * 回滚策略:设置哪些异常回滚,哪些异常不回滚
    7. * 设置回滚:rollbackFor={Exception.class,RuntimeException.class}
    8. * rollbackForClassName={"全限定类名"}
    9. * 设置不回滚:noRollbackFor={......}
    10. * noRollbackForClassName={.....}
    11. * isolation 事务隔离级别:解决"读的问题"
    12. * =Isolation.DEFAULT使用数据库的默认隔离级别
    13. * =Isolation.READ_UNCOMMITTED
    14. * =Isolation.READ_COMMITTED (oracle默认,有"不可重复读")
    15. * =Isolation.REPEATABLE_READ (mysql默认,有"虚读/幻读")
    16. * =Isolation.SERIALIZABLE
    17. * propagation 事务传播行为:事务之间的调用(例如:A调用B,那么要使用哪个的事务)
    18. * Propagation.REQUIRED(默认):当前线程有事务就用当前事务,没有就新建事务
    19. * Propagation.REQUIRES_NEW:不管当前线程有没有事务都新建一个事务,新事务与之前事务没有嵌套关系,之前的事务挂起
    20. */
    21. @Transactional(readOnly = false, timeout = 10, rollbackFor = RuntimeException.class, isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED)
    22. public void aaa() {
    23. userDao.aaa();
    24. }

    事务传播行为示例:

            有A,B两个事务,在A事务中循环调用B事务2次(第一次成功,第二次失败)

    (1) A事务传播行为:REQUIRED,B事务传播行为:REQUIRED

    1. @Transactional(propagation = Propagation.REQUIRED)
    2. public void transactionA() {
    3. for (int i = 0; i < 2; i++) {
    4. transactionB();
    5. }
    6. }
    7. @Transactional(propagation = Propagation.REQUIRED)
    8. public void transactionB() {
    9. // 第一次执行失败,第二次执行成功
    10. }

            在一个线程中,B事务中执行的2次事务都回滚,因为B事务使用的是Propagation.REQUIRED事务传播特性,因为AB在同一个线程中,而且已经存在了A事务,所以B就沿用了A事务

    (2) A事务传播行为:REQUIRED,B事务传播行为:REQUIRES_NEW

    1. @Transactional(propagation = Propagation.REQUIRED)
    2. public void transactionA() {
    3. for (int i = 0; i < 2; i++) {
    4. transactionB();
    5. }
    6. }
    7. @Transactional(propagation = Propagation.REQUIRES_NEW)
    8. public void transactionB() {
    9. // 第一次执行失败,第二次执行成功
    10. }

            在一个线程中,B事务中执行的第1次事务正常提交,第二次事务回滚,因为B事务使用的是Propagation.REQUIRES_NEW事务传播特性,因此每次transactionB方法执行都会新建一个新的事务(即B事务),新事务(B事务)与原事务(A事务)没有嵌套关系

  • 相关阅读:
    基础算法练习200题09、水池注水
    第一个servlet的程序
    swift语言学习总结
    信安软考 第二十五章 移动应用安全需要分析与安全保护工程
    2023中国物流系统集成商百强榜研究报告(附下载)
    子2023
    PointRend: 将图像分割视为渲染——PointRend:Image Segmentation as Rendering
    百度一高级经理因违反竞业协议被判赔107万;​苹果、谷歌和微软扩大对无密码登录的支持;Firefox 100 发布|极客头条
    国产化之银河麒麟.netcore3.1访问https服务的两个问题
    计算机毕业设计ssm好又多百货商业广场有限公司自助收银操作系统20287系统+程序+源码+lw+远程部署
  • 原文地址:https://blog.csdn.net/SunnerChen/article/details/132860091