• Spring AOP 底层实现原理


    前言

    AOP 也是 Spring 中一个较为重要的内容,相对于传统的 OOP 模式,AOP 有很多让人难以理解的地方,本篇文章将向大家介绍 AOP 的实现方法及其底层实现,内容包括:

    • 初始 AOP
    • AOP 的基本概念
    • AOP(concepts)术语
    • 动态代理
    • 通知介绍(前置、后置、返回、异常、环绕)
    • 基于注解的方式配置通知
    • AOP的应用场景

    一、什么是AOP

    AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)

    二、AOP的基本概念

    AOP框架具有如下两个特征:

    1. 多个步骤之间的良好隔离性。
    2. 源代码无关性。

    三、AOP concepts(AOP术语)

    Aspect/Advisors(切面)

    一个关注点的模块化,这个关注点可能会横切多个对象。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。

    Join point(连接点)

    在程序执行期间的一点。在Spring AOP中,连接点总是表示方法执行。

    Advice(通知)

    在切面的某个特定的连接点上执行的动作。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

    Pointcut(切入点)

    查找连接点的条件。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行。

    Introduction(引入)

    给一个类型声明额外的方法或属性。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。

    Target object(目标对象)

    被一个或者多个切面所通知的对象。也被称做被通知(advised)对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。

    AOP proxy

    AOP框架创建的对象,用来实现切面契约(例如通知方法执行等等)。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

    Weaving(织入)

    织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期、类装载期、运行期进行。

    Spring AOP

    1、Spring AOP使用纯Java实现,它不需要专门的编译过程。Spring AOP不需要控制类加载器层次结构,因此适用于Servlet容器或应用程序服务器。

    2、Spring AOP目前仅支持方法执行连接点。

    3、Spring实现AOP的方法跟其他的框架不同。Spring并不是要提供最完整的AOP实现(尽管Spring AOP有这个能力),相反的,它其实侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决在企业级开发中的常见问题。

    4、Spring AOP从来没有打算通过提供一种全面的AOP解决方案来与AspectJ竞争。我们相信无论是基于代理(proxy-based)的框架如Spring AOP或者是成熟的框架如AspectJ都是很有价值的,他们之间应该是互补而不是竞争的关系。

    四、Spring框架的AOP的底层实现

     Srping框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种

    1、基于JDK的动态代理

    必须是面向接口的,只有实现了具体接口的类才能生成代理对象

    2、基于CGLIB动态代理

    对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式

    Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式

    • 如果实现类接口,使用JDK动态代理完成AOP
    • 如果没有实现接口,采用CGLIB动态代理完成AOP

    基于Spring的AOP简单实现

    注意一下,在讲解之前,说明一点:使用Spring AOP,要成功运行起代码,只用Spring提供给开发者的jar包是不够的,请额外上网下载两个jar包:

    1. aopalliance.jar
    2. aspectjweaver.jar

    五、通过配置的方式实现aop通知效果

    xml配置

    1. <bean id="logger" class="com.ahpome.company.utils.Logger" />
    2. <aop:config>
    3. // 定义切面 aspect
    4. <aop:aspect id="loggerAspect" ref="logger">
    5. // 定义 切入点
    6. <aop:pointcut id="loggerRef" expression="execution(* com.ahpome.company..*.*(..)) and !bean(logger)" />
    7. // 通知 环绕通知 :around;前置通知 :before;后置通知:after;最终通知:after-returning;异常通知:after-throwing
    8. <aop:around method="record" pointcut-ref="loggerRef" />
    9. aop:aspect>
    10. aop:config>
    1. public Object record(ProceedingJoinPoint pjp){
    2. Log log = new Log();
    3. try {
    4. log.setOperator("admin");
    5. String mname = pjp.getSignature().getName();
    6. log.setOperName(mname);
    7. Object[] args = pjp.getArgs();
    8. log.setOperParams(Arrays.toString(args));
    9. // 执行目标方法,返回的是目标方法的返回值,本例中 void
    10. Object obj = pjp.proceed();
    11. if(obj != null){
    12. log.setResultMsg(obj.toString());
    13. }else{
    14. log.setResultMsg(null);
    15. }
    16. log.setOperResult("success");
    17. System.out.println("1111");
    18. return obj;
    19. } catch (Throwable e) {
    20. log.setOperResult("failure");
    21. log.setResultMsg(e.getMessage());
    22. } finally{
    23. // logService.saveLog(log);
    24. }
    25. // 注意:这里如果是返回null即使你调用的时候返回是null,即使你的方法中含有返回值
    26. return null;
    27. }

    JDK动态代理

    1. /*
    2. * 接口
    3. */
    4. public interface UserService {
    5. void save();
    6. int select();
    7. }
    1. /*
    2. * 接口实现类
    3. */
    4. public class UserServiceImpl implements UserService {
    5. @Override
    6. public void save() {
    7. System.out.println("保存用户信息成功");
    8. }
    9. @Override
    10. public int select() {
    11. System.out.println("查询用户信息成功");
    12. return 10;
    13. }
    14. }
    1. /*
    2. * JDK动态代理工厂类
    3. */
    4. public class JdkProxyFactory implements InvocationHandler {
    5. private Object target;
    6. public JdkProxyFactory(Object target) {
    7. this.target = target;
    8. }
    9. /**
    10. * 获取代理对象,当前类继承InvocationHandler
    11. */
    12. public Object getProxyObject() {
    13. return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    14. }
    15. @Override
    16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    17. //添加功能
    18. System.out.println("增强代码,添加日志功能");
    19. //执行原有方法
    20. return method.invoke(target, args);
    21. }
    22. }

    测试JDK动态代理​

    1. @Test
    2. public void JdkProxyTest() {
    3. // 创建目标对象
    4. UserService userService = new UserServiceImpl();
    5. //创建工厂对象
    6. JdkProxyFactory jdkProxyFactory = new JdkProxyFactory(userService);
    7. UserService proxy = (UserService) jdkProxyFactory.getProxyObject();
    8. proxy.save();
    9. System.out.println("=========================");
    10. proxy.select();
    11. }

    六、AOP的应用场景

    前面我们提到AOP主要应用一些非核心业务逻辑中,我们看看AOP常见的应用场景

    • 监控
    • 日志
    • 缓存
    • 鉴权
    • 事务
    • 异常处理
    • 持久化

    总结

    以上就是我对Java开发大型互联网架构Spring AOP实现原理之Spring AOP底层实现问题的总结。

  • 相关阅读:
    当try_files遇上gateway是如何产生火花的
    [Codeforces] games (R1200) Part.2
    ctfshow-web入门-sqli-labs
    算法与数据结构之链表
    LeeCode《可以读通讯稿的组数》ac题解
    Eclipse配置Maven详细教程
    金属标记/荧光标记/功能化改性/官能团表面包覆聚苯乙烯微球
    云计算 - 以阿里云为例,企业上云策略全览与最佳实践
    Android开发基础——ListView
    excel内容通过apache poi读取后读出来为数字,转为日期类型
  • 原文地址:https://blog.csdn.net/gongzi_9/article/details/126297378