spring之面向切面:AOP(1)

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: 【1月更文挑战第15天】一、场景模拟1、声明接口2、创建实现类3、创建带日志功能的实现类4、提出问题二、代理模式1、概念2、静态代理3、动态代理4、测试三、AOP概念及相关术语1、概述2、相关术语①横切关注点②通知(增强)③切面④目标⑤代理⑥连接点⑦切入点3、作用

文章目录

前言

一、场景模拟

1、声明接口

2、创建实现类

3、创建带日志功能的实现类

4、提出问题

二、代理模式

1、概念

2、静态代理

3、动态代理

4、测试

三、AOP概念及相关术语

1、概述

2、相关术语

①横切关注点

②通知(增强)

③切面

④目标

⑤代理

⑥连接点

⑦切入点

3、作用

总结


前言

一、场景模拟

1、声明接口

2、创建实现类

3、创建带日志功能的实现类

4、提出问题

二、代理模式

1、概念

2、静态代理

3、动态代理

4、测试

三、AOP概念及相关术语

1、概述

2、相关术语

①横切关注点

②通知(增强)

③切面

④目标

⑤代理

⑥连接点

⑦切入点

3、作用


一、场景模拟

搭建子模块:spring6-aop

1、声明接口

声明计算器接口Calculator,包含加减乘除的抽象方法

publicinterfaceCalculator {
intadd(inti, intj);
intsub(inti, intj);
intmul(inti, intj);
intdiv(inti, intj);
}

2、创建实现类

publicclassCalculatorImplimplementsCalculator {
@Overridepublicintadd(inti, intj) {
intresult=i+j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
@Overridepublicintsub(inti, intj) {
intresult=i-j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
@Overridepublicintmul(inti, intj) {
intresult=i*j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
@Overridepublicintdiv(inti, intj) {
intresult=i/j;
System.out.println("方法内部 result = "+result);
returnresult;
    }
}

3、创建带日志功能的实现类

publicclassCalculatorLogImplimplementsCalculator {
@Overridepublicintadd(inti, intj) {
System.out.println("[日志] add 方法开始了,参数是:"+i+","+j);
intresult=i+j;
System.out.println("方法内部 result = "+result);
System.out.println("[日志] add 方法结束了,结果是:"+result);
returnresult;
    }
@Overridepublicintsub(inti, intj) {
System.out.println("[日志] sub 方法开始了,参数是:"+i+","+j);
intresult=i-j;
System.out.println("方法内部 result = "+result);
System.out.println("[日志] sub 方法结束了,结果是:"+result);
returnresult;
    }
@Overridepublicintmul(inti, intj) {
System.out.println("[日志] mul 方法开始了,参数是:"+i+","+j);
intresult=i*j;
System.out.println("方法内部 result = "+result);
System.out.println("[日志] mul 方法结束了,结果是:"+result);
returnresult;
    }
@Overridepublicintdiv(inti, intj) {
System.out.println("[日志] div 方法开始了,参数是:"+i+","+j);
intresult=i/j;
System.out.println("方法内部 result = "+result);
System.out.println("[日志] div 方法结束了,结果是:"+result);
returnresult;
    }
}

4、提出问题

①现有代码缺陷

针对带日志功能的实现类,我们发现有如下缺陷:

  • 对核心业务功能有干扰,导致程序员在开发核心业务功能时分散了精力
  • 附加功能分散在各个业务功能方法中,不利于统一维护

②解决思路

解决这两个问题,核心就是:解耦。我们需要把附加功能从业务功能代码中抽取出来。

③困难

解决问题的困难:要抽取的代码在方法内部,靠以前把子类中的重复代码抽取到父类的方式没法解决。所以需要引入新的技术。

二、代理模式

1、概念

①介绍

二十三种设计模式中的一种,属于结构型模式。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。让不属于目标方法核心逻辑的代码从目标方法中剥离出来——解耦。调用目标方法时先调用代理对象的方法,减少对目标方法的调用和打扰,同时让附加功能能够集中在一起也有利于统一维护。

使用代理后:

②生活中的代理

  • 广告商找大明星拍广告需要经过经纪人
  • 合作伙伴找大老板谈合作要约见面时间需要经过秘书
  • 房产中介是买卖双方的代理

③相关术语

  • 代理:将非核心逻辑剥离出来以后,封装这些非核心逻辑的类、对象、方法。
  • 目标:被代理“套用”了非核心逻辑代码的类、对象、方法。

2、静态代理

创建静态代理类:

publicclassCalculatorStaticProxyimplementsCalculator {
// 将被代理的目标对象声明为成员变量privateCalculatortarget;
publicCalculatorStaticProxy(Calculatortarget) {
this.target=target;
    }
@Overridepublicintadd(inti, intj) {
// 附加功能由代理类中的代理方法来实现System.out.println("[日志] add 方法开始了,参数是:"+i+","+j);
// 通过目标对象来实现核心业务逻辑intaddResult=target.add(i, j);
System.out.println("[日志] add 方法结束了,结果是:"+addResult);
returnaddResult;
    }
}

静态代理确实实现了解耦,但是由于代码都写死了,完全不具备任何的灵活性。就拿日志功能来说,将来其他地方也需要附加日志,那还得再声明更多个静态代理类,那就产生了大量重复的代码,日志功能还是分散的,没有统一管理。

提出进一步的需求:将日志功能集中到一个代理类中,将来有任何日志需求,都通过这一个代理类来实现。这就需要使用动态代理技术了。

3、动态代理

生产代理对象的工厂类:

publicclassProxyFactory {
privateObjecttarget;
publicProxyFactory(Objecttarget) {
this.target=target;
    }
publicObjectgetProxy(){
/*** newProxyInstance():创建一个代理实例* 其中有三个参数:* 1、classLoader:加载动态生成的代理类的类加载器* 2、interfaces:目标对象实现的所有接口的class对象所组成的数组* 3、invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法*/ClassLoaderclassLoader=target.getClass().getClassLoader();
Class<?>[] interfaces=target.getClass().getInterfaces();
InvocationHandlerinvocationHandler=newInvocationHandler() {
@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable {
/*** proxy:代理对象* method:代理对象需要实现的方法,即其中需要重写的方法* args:method所对应方法的参数*/Objectresult=null;
try {
System.out.println("[动态代理][日志] "+method.getName()+",参数:"+Arrays.toString(args));
result=method.invoke(target, args);
System.out.println("[动态代理][日志] "+method.getName()+",结果:"+result);
                } catch (Exceptione) {
e.printStackTrace();
System.out.println("[动态代理][日志] "+method.getName()+",异常:"+e.getMessage());
                } finally {
System.out.println("[动态代理][日志] "+method.getName()+",方法执行完毕");
                }
returnresult;
            }
        };
returnProxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

4、测试

@TestpublicvoidtestDynamicProxy(){
ProxyFactoryfactory=newProxyFactory(newCalculatorLogImpl());
Calculatorproxy= (Calculator) factory.getProxy();
proxy.div(1,0);
//proxy.div(1,1);}

三、AOP概念及相关术语

1、概述

AOP(Aspect Oriented Programming)是一种设计思想,是软件设计领域中的面向切面编程,它是面向对象编程的一种补充和完善,它以通过预编译方式和运行期动态代理方式实现,在不修改源代码的情况下,给程序动态统一添加额外功能的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2、相关术语

①横切关注点

分散在每个各个模块中解决同一样的问题,如用户验证、日志管理、事务处理、数据缓存都属于横切关注点。

从每个方法中抽取出来的同一类非核心业务。在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。

这个概念不是语法层面的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点。

②通知(增强)

增强,通俗说,就是你想要增强的功能,比如 安全,事务,日志等。

每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。

  • 前置通知:在被代理的目标方法执行
  • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
  • 异常通知:在被代理的目标方法异常结束后执行(死于非命
  • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
  • 环绕通知:使用try…catch…finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

③切面

封装通知方法的类。

④目标

被代理的目标对象。

⑤代理

向目标对象应用通知之后创建的代理对象。

⑥连接点

这也是一个纯逻辑概念,不是语法定义的。

把方法排成一排,每一个横切位置看成x轴方向,把方法从上到下执行的顺序看成y轴,x轴和y轴的交叉点就是连接点。通俗说,就是spring允许你使用通知的地方

⑦切入点

定位连接点的方式。

每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物(从逻辑上来说)。

如果把连接点看作数据库中的记录,那么切入点就是查询记录的 SQL 语句。

Spring 的 AOP 技术可以通过切入点定位到特定的连接点。通俗说,要实际去增强的方法

切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

3、作用

  • 简化代码:把方法中固定位置的重复的代码抽取出来,让被抽取的方法更专注于自己的核心功能,提高内聚性。
  • 代码增强:把特定的功能封装到切面类中,看哪里有需要,就往上套,被套用了切面逻辑的方法就被切面给增强了。

总结

以上就是spring之面向切面:AOP(1)的相关知识点,希望对你有所帮助。

积跬步以至千里,积怠惰以至深渊。时代在这跟着你一起努力哦!

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
29天前
|
Java 关系型数据库 MySQL
利用Spring AOP技术实现一个读写分离
利用Spring AOP技术实现一个读写分离
32 0
|
2月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
46 1
|
2月前
|
Java Spring 容器
Spring的AOP失效场景详解
Spring的AOP失效场景详解
119 0
|
2月前
|
设计模式 Java Maven
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
36 1
|
3月前
|
XML Java 数据格式
5个点轻松搞定Spring AOP底层实现原理
AOP 也是 Spring 中一个较为重要的内容,相对于传统的 OOP 模式,AOP 有很多让人难以理解的地方,本篇文章将向大家介绍 AOP 的实现方法及其底层实现,内容包括:
47 1
|
1天前
|
安全 Java 开发者
在Spring框架中,IoC和AOP是如何实现的?
【4月更文挑战第30天】在Spring框架中,IoC和AOP是如何实现的?
7 0
|
1天前
|
Java 测试技术 开发者
【亮剑】如何通过自定义注解来实现 Spring AOP,以便更加灵活地控制方法的拦截和增强?
【4月更文挑战第30天】通过自定义注解实现Spring AOP,可以更灵活地控制方法拦截和增强。首先定义自定义注解,如`@MyCustomAnnotation`,然后创建切面类`MyCustomAspect`,使用`@Pointcut`和`@Before/@After`定义切点及通知。配置AOP代理,添加`@EnableAspectJAutoProxy`到配置类。最后,在需拦截的方法上应用自定义注解。遵循保持注解职责单一、选择合适保留策略等最佳实践,提高代码可重用性和可维护性。记得测试AOP逻辑。
|
18天前
|
Java Spring
代码优雅的转变:基于注解的AOP编程在Spring中的实践
代码优雅的转变:基于注解的AOP编程在Spring中的实践
17 0
|
18天前
|
XML 监控 Java
Spring AOP:解锁切面编程的威力与实践
Spring AOP:解锁切面编程的威力与实践
21 0
Spring AOP:解锁切面编程的威力与实践
|
21天前
|
XML 安全 Java
spring面向切面编程AOP
spring面向切面编程AOP