技术博客 技术博客
  • JAVA
  • 仓颉
  • 设计模式
  • 人工智能
  • Spring
  • Mybatis
  • Maven
  • Git
  • Kafka
  • RabbitMQ
  • RocketMQ
  • Redis
  • Zookeeper
  • Nginx
  • 数据库套件
  • MySQL
  • Elasticsearch
  • MongoDB
  • Hadoop
  • ClickHouse
  • Hbase
  • Hive
  • Flink
  • Flume
  • SQLite
  • linux
  • Docker
  • Jenkins
  • Kubernetes
  • 工具
  • 前端
  • AI
GitHub (opens new window)
  • JAVA
  • 仓颉
  • 设计模式
  • 人工智能
  • Spring
  • Mybatis
  • Maven
  • Git
  • Kafka
  • RabbitMQ
  • RocketMQ
  • Redis
  • Zookeeper
  • Nginx
  • 数据库套件
  • MySQL
  • Elasticsearch
  • MongoDB
  • Hadoop
  • ClickHouse
  • Hbase
  • Hive
  • Flink
  • Flume
  • SQLite
  • linux
  • Docker
  • Jenkins
  • Kubernetes
  • 工具
  • 前端
  • AI
GitHub (opens new window)
  • Spring

    • spring

      • 核心内容拆解 IOC
      • 核心内容拆解 AOP
        • AOP
          • 封装
          • 把封装的融入到 Spring 中
      • 核心内容拆解 事件通知
      • 核心内容拆解 三级缓存
      • 核心内容拆解 FactoryBean
      • 注解替代Spring生命周期实现类
    • spring mv

      • Spring MVC 之基本工作原理
    • spring boot

      • SpringBoot 之 Filter、Interceptor、Aspect
      • SpringBoot 之 Starter
      • SpringBoot 之 Stomp 使用和 vue 相配置
      • SpringBoot MyBatisPlus 实现多数据源
      • SpringBoot MyBatis 动态建表
      • Spring Boot 集成 Jasypt 3.0.3 配置文件加密
      • Spring Boot 集成 FastDFS
      • Spring Boot VUE前后端加解密
      • Spring Boot logback.xml 配置
      • Spring Boot MinIO
      • Spring Boot kafka
      • Spring Boot WebSocket
    • spring cloud

      • SpringCloud - Ribbon和Feign
      • SpringCloud alibaba - Nacos
      • SpringCloud alibaba - Sentinel哨兵
      • SpringCloud alibaba - Gateway
      • SpringCloud alibaba - 链路跟踪
      • SpringCloud - 分布式事务一(XA,2PC,3PC)
      • SpringCloud - 分布式事务二(Seata-AT,TCC,Saga)
      • SpringCloud - 分布式事务三(Seata搭建)
      • SpringCloud - 分布式事务四(多数据源事务)
      • SpringCloud - 分布式事务五(微服务间调用的事务处理)
  • Mybatis

    • 核心功能拆解 工作流程
    • 核心功能拆解 Plugin插件功能实现
    • 核心功能拆解 一二级缓存原理
    • MyBatis Plus+Spring Boot 实现一二级缓存以及自定义缓存
  • maven

    • pom 文件介绍及 parent、properties 标签详解
    • dependencies 标签详解
    • 使用 Nexus3.x 搭建私服
  • git

    • 私有 git 仓库搭建
目录

核心内容拆解 AOP

# AOP

AOP 的诞生可以追溯到上世纪 90 年代初期,它最早由 Gregor Kiczales 等人提出,并在 1997 年发表了经典的论文 Aspect-Oriented Programming。后来,AspectJ 成为了 Java 生态中使用最广泛的 AOP 框架之一。

AOP 的目的是为了解决在 OOP(面向对象编程)中难以处理的横切关注点问题,即将系统业务逻辑代码与其他非业务功能(如日志记录、性能统计、安全控制等)分离开来。AOP 通过把这些非业务功能独立出来,在需要时动态地植入到系统中,从而实现对业务逻辑的无侵入式增强。

AOP 的核心在于其能够将业务逻辑与非业务功能分离开来,从而降低了代码的耦合度,并且支持在运行时动态地植入和移除切面。这样一来,就可以实现更加灵活、可维护和可扩展的系统。

AOP 的具体表现包括切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)和引入(Introduction)等概念。其中,切面是指横跨多个对象的通用功能,连接点是程序执行过程中能够插入切面的点,通知则是定义了切面在连接点处所执行的操作,切点则是一个谓词表达式,用于匹配连接点,引入则是为某个对象添加新的接口实现。具体如下代码:

    public void test_proxy_method() {
        // 目标对象(可以替换成任何的目标对象)
        Object targetObj = new UserService();
        // AOP 代理
        IUserService proxy = (IUserService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), targetObj.getClass().getInterfaces(), new InvocationHandler() {
            // 方法匹配器
            MethodMatcher methodMatcher = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))");
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (methodMatcher.matches(method, targetObj.getClass())) {
                    // 方法拦截器
                    MethodInterceptor methodInterceptor = invocation -> {
                        long start = System.currentTimeMillis();
                        try {
                            return invocation.proceed();
                        } finally {
                            System.out.println("监控 - Begin By AOP");
                            System.out.println("方法名称:" + invocation.getMethod().getName());
                            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
                            System.out.println("监控 - End\r\n");
                        }
                    };
                    // 反射调用
                    return methodInterceptor.invoke(new ReflectiveMethodInvocation(targetObj, method, args));
                }
                return method.invoke(targetObj, args);
            }
        });
        String result = proxy.queryUserInfo();
        System.out.println("测试结果:" + result);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

这段代码使用了 JDK 动态代理实现 AOP 的功能,没有使用 Spring 提供的方法、类和注解等。

  • 连接点:连接点是在目标对象上匹配的特定点,这里的连接点是 IUserService 接口中的所有方法,由于使用了 targetObj.getClass ().getInterfaces () 获取目标对象所实现的接口,因此只拦截了 IUserService 接口中的方法。
  • 切面:切面是一个模块化的横切关注点,在这里我们可以视为没有显式定义的切面。而是直接在 InvocationHandler.invoke () 中实现了拦截和增强逻辑,包括方法匹配器、方法拦截器和反射调用等。
  • 切点:切点是一种谓词表达式,用于匹配连接点。这里使用了 AspectJ 表达式 "execution (* cn.bugstack.springframework.test.bean.IUserService.*(..))",它匹配了 IUserService 接口中的所有方法。
  • 通知:通知类型包括前置通知、后置通知、环绕通知、抛出通知和最终通知。在这里使用了环绕通知,即在方法执行之前和之后添加了监控逻辑。
  • 引入:引介通常是一个特殊的通知类型,它允许在运行时为类动态地添加新接口实现。这里没有使用引介。

# 封装

在 Spring 中,核心逻辑是离不开上面的代理例子的,只是相对应做了些封装,我们先用类图来简单说明下封装关系:

用测试例子来说明每步的核心

    /**
     * 切点表达式,来验证切点
     * @throws NoSuchMethodException
     */
    @Test
    public void test_aop() throws NoSuchMethodException {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.UserService.*(..))");
        Class<UserService> clazz = UserService.class;
        Method method = clazz.getDeclaredMethod("queryUserInfo");
        System.out.println("切点是否包含该类:" + pointcut.matches(clazz));
        System.out.println("切点是否包含该类该方法:" + pointcut.matches(method, clazz));
    }

    /**
     * 切面 和 动态代理
     */
    @Test
    public void test_dynamic() {
        // 目标对象
        IUserService userService = new UserService();
        // 组装代理信息,切面
        AdvisedSupport advisedSupport = new AdvisedSupport();
        // 设置代理目标对象
        advisedSupport.setTargetSource(new TargetSource(userService));
        // 设置拦截器
        advisedSupport.setMethodInterceptor(new UserServiceInterceptor());
        // 匹配代理对象
        advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"));
        // 代理对象(JdkDynamicAopProxy)
        IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        // 测试调用
        System.out.println("测试结果:" + proxy_jdk.queryUserInfo());
        // 代理对象(Cglib2AopProxy)
        IUserService proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy();
        // 测试调用
        System.out.println("测试结果:" + proxy_cglib.register("花花"));
    }

public class UserServiceInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return invocation.proceed();
        } finally {
            System.out.println("监控 - Begin By AOP");
            System.out.println("方法名称:" + invocation.getMethod());
            System.out.println("方法耗时:" + (System.currentTimeMillis() - start) + "ms");
            System.out.println("监控 - End\r\n");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# 把封装的融入到 Spring 中

右侧部分就是描述了整个融合到 Spring 中的类,会在 Bean 创建的过程中 初始化方法之后 这个生命周期内先找到是否提供了 DefaultAdvisorAutoProxyCreator 类的支持,因为他描述了具体代理类的过程。

为什么会在初始化方法之后才进行代理,是因为代理类也需要的属性也需要被填充,所以等填充完毕后在代理

核心方法,描述了整个类被代理的过程

    protected Object wrapIfNecessary(Object bean, String beanName) {
        // 判断Bean是否是Advice,Pointcut,Advisor的子类或者两类相同可以相互转(类层面),用户定义的类都是 false
        if (isInfrastructureClass(bean.getClass())) return bean;
        // 得到注册的AspectJExpressionPointcutAdvisor
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
        for (AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            // 用表达式 过滤匹配类
            if (!classFilter.matches(bean.getClass())) continue;
            // 封装
            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource targetSource = new TargetSource(bean);
            advisedSupport.setTargetSource(targetSource);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(true);
            // 返回代理对象
            return new ProxyFactory(advisedSupport).getProxy();
        }
        return bean;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<beans>
    <!-- 目标类 -->
    <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService"/>
    <!-- 代理类 -->
    <bean id="beforeAdvice" class="cn.bugstack.springframework.test.bean.UserServiceBeforeAdvice"/>
    <!-- 组件类,至关重要 -->
    <bean class="cn.bugstack.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
    <!-- 
        这里是  advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); 设置拦截器,
        可以是前置拦截,后置拦截,或者环绕拦截
     -->
    <bean id="methodInterceptor" class="cn.bugstack.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
        <property name="advice" ref="beforeAdvice"/>
    </bean>
    <!-- 切面表达式 -->
    <bean id="pointcutAdvisor" class="cn.bugstack.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
        <property name="expression" value="execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"/>
        <property name="advice" ref="methodInterceptor"/>
    </bean>
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
上次更新: 6/11/2025, 4:10:30 PM
核心内容拆解 IOC
核心内容拆解 事件通知

← 核心内容拆解 IOC 核心内容拆解 事件通知→

Theme by Vdoing | Copyright © 2023-2025
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式