实现AOP机制 + Spring总结

news2025/1/11 1:18:27

文章目录

    • 1.目录
    • 2.SmartAnimal.java 接口(JDK代理必须有接口)
    • 3.SmartDog.java
    • 4.SmartAnimalAspect.java
    • 5.SunSpringApplicationContext.java
        • 1.在Bean的后置处理器之后使用动态代理
        • 2.完整代码
    • 6.测试
        • 1.AppMain.java 调用被代理的类的方法
        • 2.结果
    • 7.Spring底层机制总结
          • 1.扫描包,存储Bean定义信息,初始化单例池,依赖注入
          • 2.后置处理器的Before方法对Bean进行处理
          • 3.实现InitializingBean接口或者有@PostConstruct注解的初始化方法被调用
          • 4.后置处理器的After方法对Bean进行处理
          • 5.如果发现某个类被AOP了,则在后置处理器之后就会返回代理对象,当目标对象使用代理对象调用方法时,就可以得到目标对象和调用的方法的信息

1.目录

CleanShot 2024-08-06 at 14.19.09@2x

2.SmartAnimal.java 接口(JDK代理必须有接口)

package com.sunxiansheng.sunspring.compent;

/**
 * Description: SmartAnimal
 * @Author sun
 * @Create 2024/8/4 14:51
 * @Version 1.0
 */
public interface SmartAnimal {

    public float getSum(float a, float b);

    public float getSub(float a, float b);

}

3.SmartDog.java

package com.sunxiansheng.sunspring.compent;


import com.sunxiansheng.sunspring.annotation.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description: SmartDog
 * @Author sun
 * @Create 2024/8/4 14:51
 * @Version 1.0
 */
@Component // 交给Spring容器管理
public class SmartDog implements SmartAnimal {

    private static final Logger log = LoggerFactory.getLogger(SmartDog.class);

    @Override
    public float getSum(float a, float b) {
        log.info("SmartDog...getSum...res=" + (a + b));
        return a + b;
    }

    @Override
    public float getSub(float a, float b) {
        log.info("SmartDog...getSub...res=" + (a - b));
        return a - b;
    }

}

4.SmartAnimalAspect.java

package com.sunxiansheng.sunspring.compent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Description: 切面类
 * @Author sun
 * @Create 2024/8/6 13:50
 * @Version 1.0
 */
public class SmartAnimalAspect {

    private static final Logger log = LoggerFactory.getLogger(SmartAnimalAspect.class);

    public static void showBeginLog() {
        log.info("AOP:前置通知-方法执行开始");
    }

    public static void showEndLog() {
        log.info("AOP:后置通知-方法执行结束");
    }

}

5.SunSpringApplicationContext.java

1.在Bean的后置处理器之后使用动态代理

CleanShot 2024-08-06 at 14.21.38@2x

2.完整代码
package com.sunxiansheng.sunspring.ioc;

import com.sunxiansheng.sunspring.annotation.*;
import com.sunxiansheng.sunspring.annotation.myenum.MyScope;
import com.sunxiansheng.sunspring.compent.SmartAnimalAspect;
import com.sunxiansheng.sunspring.processor.BeanPostProcessor;
import com.sunxiansheng.sunspring.processor.InitializingBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Description: 自定义Spring容器
 * @Author sun
 * @Create 2024/8/4 16:35
 * @Version 1.0
 */
public class SunSpringApplicationContext {

    private static final Logger log = LoggerFactory.getLogger(SunSpringApplicationContext.class);

    /**
     * bean定义的map
     */
    private ConcurrentHashMap<String, BeanDefintion> beanDefintionMap = new ConcurrentHashMap<>();

    /**
     * 单例池
     */
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    /**
     * bean的后置处理器
     */
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    // 构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class<?> configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        // 完成bean的扫描,将bean的信息记录到beanDefintionMap中
        beanDefinitionByScan(configClass);
        // 初始化单例池
        initSingletonObjects();
    }

    /**
     * 给某个bean对象完成依赖注入
     */
    private void populateBeans(Object bean) {
        // 扫描beanDefintionMap中的bean信息,对bean对象中的属性进行依赖注入
        // 获取Class对象
        Class<?> clazz = bean.getClass();
        // 获取所有字段
        Field[] fields = clazz.getDeclaredFields();
        // 判断字段上是否有@Resource注解
        for (Field field : fields) {
            if (field.isAnnotationPresent(Resource.class)) {
                // 获取字段名
                String fieldName = field.getName();
                // 根据字段名获取bean对象
                Object beanObject = null;
                // 从beanDefintionMap中获取bean对象
                BeanDefintion beanDefintion = beanDefintionMap.get(fieldName);
                try {
                    // 根据bean的定义信息创建bean对象
                    beanObject = getBean(fieldName);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                // 设置字段可访问
                field.setAccessible(true);
                try {
                    // 依赖注入
                    field.set(bean, beanObject);
                    log.info("依赖注入成功:{} => {}.{}", beanObject.getClass(), clazz, fieldName);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 初始化单例池
     */
    private void initSingletonObjects() {
        // 将beanDefintionMap中的bean信息创建成bean对象放到单例池中
        beanDefintionMap.forEach((beanName, beanDefintion) -> {
            try {
                // 根据bean的定义信息创建bean对象
                Object bean = createBean(beanDefintion);
                if (bean != null) {
                    // 将bean对象放到单例池中
                    singletonObjects.put(beanName, bean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        // 打印单例池中的bean对象
        log.info("根据bean定义信息初始化单例池:{}", singletonObjects);
    }

    // 返回容器中的对象
    public Object getBean(String name) throws Exception {
        BeanDefintion beanDefintion = beanDefintionMap.get(name);
        if (beanDefintion == null) {
            throw new NullPointerException("在bean定义中没有找到bean对象");
        }
        // 根据单例和多例来获取bean对象
        MyScope scope = beanDefintion.getScope();
        Object bean = null;
        if (scope == MyScope.SINGLETON) {
            // 单例就直接从单例池中获取对象
            bean = singletonObjects.get(name);
        } else {
            // 多例就创建一个新的对象
            bean = createProtoTypeBean(beanDefintion);
        }
        // 给bean对象完成依赖注入
        populateBeans(bean);
        // 记录当前对象,因为后置处理器可能会返回一个新的对象
        Object current = bean;
        // 初始化方法之前调用后置处理器 postProcessBeforeInitialization
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            Object bean1 = beanPostProcessor.postProcessBeforeInitialization(bean, name);
            // 如果beanPostProcessor返回的对象为空,则使用原来的对象
            if (bean1 != null) {
                current = bean1;
            }
        }
        // 初始化bean
        init(current);
        // 初始化方法之后调用后置处理器 postProcessAfterInitialization
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            bean = beanPostProcessor.postProcessAfterInitialization(current, name);
            // 如果beanPostProcessor返回的对象为空,则使用原来的对象
            if (bean == null) {
                bean = current;
            }
        }
        // 这里直接写死,如果是SmartDog类,则使用动态代理
        if (name.equals("smartDog")) {
            final Object proxyObject = current;
            // 返回代理对象
            /**
             * 参数1:被代理对象的类加载器,参数2:被代理对象的接口,参数3:代理对象
             */
            bean = Proxy.newProxyInstance(current.getClass().getClassLoader(), current.getClass().getInterfaces(), (proxy, method, args) -> {
                // 前置通知
                SmartAnimalAspect.showBeginLog();
                // 调用目标方法
                Object invoke = method.invoke(proxyObject, args);
                // 后置通知
                SmartAnimalAspect.showEndLog();
                return invoke;
            });
            log.info("SmartDog返回代理对象:{}", bean.getClass());
            return bean;
        }

        log.info("getBean:{}", bean.getClass());
        return bean;
    }

    /**
     * 初始化bean
     * @param bean
     */
    public void init(Object bean) {
        if (bean instanceof InitializingBean) {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    /**
     * 根据bean的定义信息创建bean对象(单例bean)
     * @param beanDefintion
     * @return
     * @throws Exception
     */
    private Object createBean(BeanDefintion beanDefintion) throws Exception {
        // 得到bean的类型
        Class<?> clazz = beanDefintion.getClazz();
        // 根据bean的作用域创建bean对象,多例就不创建了,单例就创建
        if (beanDefintion.getScope() == MyScope.PROTOTYPE) {
            return null;
        }
        Object bean = clazz.getDeclaredConstructor().newInstance();
        return bean;
    }

    /**
     * 创建多例bean
     * @param beanDefintion
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    private static Object createProtoTypeBean(BeanDefintion beanDefintion) throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        // 多例就创建一个新的对象
        Class<?> clazz = beanDefintion.getClazz();
        Object bean = clazz.getDeclaredConstructor().newInstance();
        return bean;
    }

    /**
     * 完成bean的扫描,将bean的信息记录到beanDefintionMap中
     * @param configClass
     * @throws ClassNotFoundException
     */
    private void beanDefinitionByScan(Class<?> configClass) {
        // 传进来一个配置类的Class对象
        // 一、获取要扫描的包
        // 1.首先反射获取类的注解信息
        ComponentScan componentScan = configClass.getDeclaredAnnotation(ComponentScan.class);
        // 2.通过注解来获取要扫描的包的路径
        String path = componentScan.packagePath();
        log.info("扫描的包路径:{}", path);
        // 二、得到要扫描包的.class文件对象,从而得到全路径进行反射
        // 1.获取App类加载器
        ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();
        // 2.获取要扫描包的真实路径,默认刚开始在根目录下
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        // 3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型
        File file = new File(resource.getFile());
        // 4.遍历该文件夹下的所有.class文件对象
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                // 反射注入容器
                // 1.获取所有文件的绝对路径
                String absolutePath = f.getAbsolutePath();
                // 只处理class文件
                if (absolutePath.endsWith(".class")) {
                    // 2.分割出类名
                    String className = extractClassName(absolutePath);
                    // 3.得到全路径
                    String fullPath = path.replace("/", ".") + "." + className;
                    // 4.判断是否需要注入容器,查看有没有自定义的注解Component
                    Class<?> aClass = null;
                    try {
                        aClass = classLoader.loadClass(fullPath);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    // 如果该类使用了注解Component则说明是一个spring bean
                    if (aClass.isAnnotationPresent(Component.class)) {
                        log.info("扫描到Spring Bean:{}", aClass);

                        // 将Bean的后置处理器加入到beanPostProcessorList中
                        // 判断Class对象是否实现了BeanPostProcessor接口
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            Object o = null;
                            try {
                                o = aClass.getDeclaredConstructor().newInstance();
                            } catch (Exception e) {
                                log.info("BeanPostProcessor实例化失败:{}", e);
                            }
                            if (o instanceof BeanPostProcessor) {
                                beanPostProcessorList.add((BeanPostProcessor) o);
                            }
                            log.info("BeanPostProcessor实例化成功:{}", o);
                            // 直接跳过,不需要将BeanPostProcessor加入到beanDefintionMap中
                            continue;
                        }

                        // 将bean的信息记录到beanDefintionMap中
                        BeanDefintion beanDefintion = new BeanDefintion();
                        // 1.获取Scope注解的value值
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                            MyScope value = scope.value();
                            // 放到beanDefintion中
                            beanDefintion.setScope(value);
                        } else {
                            // 如果没有指定作用域,则默认为单例
                            beanDefintion.setScope(MyScope.SINGLETON);
                        }
                        beanDefintion.setClazz(aClass);

                        // 2.获取Component注解的value值
                        Component component = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = component.value();
                        if ("".equals(beanName)) {
                            // 如果没有指定value属性,则使用类名首字母小写作为bean的id
                            beanName = className.substring(0, 1).toLowerCase() + className.substring(1);
                        }

                        // 3.将bean的id和bean的信息放到beanDefintionMap中
                        beanDefintionMap.put(beanName, beanDefintion);
                    } else {
                        log.info("这不是一个Spring Bean={}", aClass);
                    }
                }
            }
        }
        // 打印beanDefintionMap中的bean信息
        log.info("将bean定义信息放到beanDefintionMap:{}", beanDefintionMap);
    }

    /**
     * 分割出类名
     * 类似于 com/sunxiansheng/sunspring/compent/MonsterService.class 的类名
     * @param filePath
     * @return
     */
    private String extractClassName(String filePath) {
        // 获取最后一个 '/' 的位置
        int lastSlashIndex = filePath.lastIndexOf('/');
        // 获取最后一个 '.' 的位置
        int lastDotIndex = filePath.lastIndexOf('.');
        // 提取两者之间的字符串作为类名
        return filePath.substring(lastSlashIndex + 1, lastDotIndex);
    }

}

6.测试

1.AppMain.java 调用被代理的类的方法

CleanShot 2024-08-06 at 14.22.58@2x

2.结果

CleanShot 2024-08-06 at 14.23.22@2x

7.Spring底层机制总结

1.扫描包,存储Bean定义信息,初始化单例池,依赖注入
2.后置处理器的Before方法对Bean进行处理
3.实现InitializingBean接口或者有@PostConstruct注解的初始化方法被调用
4.后置处理器的After方法对Bean进行处理
5.如果发现某个类被AOP了,则在后置处理器之后就会返回代理对象,当目标对象使用代理对象调用方法时,就可以得到目标对象和调用的方法的信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2065186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

维特——六九轴陀螺仪传感器代码的移植方法

使用的是如图所示的这款陀螺仪&#xff0c;相比MPU6050&#xff0c;它可以做到Z轴不漂的效果。 官方给的代码是使用两个串口&#xff0c;一个用来和上位机通信&#xff0c;一个用来于该模块通信。在实际使用中&#xff0c;我们只需要单片机和该模块通信即可&#xff0c;所以我…

Centos安装Jenkins教程详解版(JDK8+Jenkins2.346.1)

本教程基于 JDK8 和 Jenkins2.346.1 JDK安装 下载OpenJDK8文件 wget https://mirrors.tuna.tsinghua.edu.cn/Adoptium/8/jdk/x64/linux/OpenJDK8U-jdk_x64_linux_hotspot_8u422b05.tar.gz解压到指定目录 # 创建目录 mkdir -p /usr/local/software# 解压文件到指定目录&#…

uniapp点击预览图片,两种效果

背景&#xff1a; 在使用uniapp框架中&#xff0c;我们对图片的展示需要点击放大展示(单张)&#xff1b;如果是多张图片&#xff0c;要支持左右滑动查看多张图片(多张)。 官网链接&#xff1a;点击跳转官网 一、单张&#xff0c;点击放大 代码&#xff1a; <template> …

Spring模块详解Ⅲ(Spring Expression Language (SPEL)和Spring JDBC)

目录 Spring Expression Language&#xff08;SpEL&#xff09;Spring Context 模块详解1. 什么是 Spring Context?2. ApplicationContext 的主要实现3. 国际化支持国际化消息文件使用 MessageSourceXML 配置&#xff1a;Java 配置&#xff1a; 使用国际化消息&#xff1a; 4.…

SwapPrompt(论文解读): Test-Time Prompt Adaptation for Vision-Language Models

2023(Neural IPS) 摘要 测试自适应 &#xff08;TTA&#xff09; 是无监督域自适应&#xff08;UDA&#xff09;中一种特殊且实用的设置&#xff0c;它允许源域中的预训练模型去适应另一个目标域中的未标记测试数据。为了避免计算密集型的骨干网络微调过程&#xff0c;因此利…

【技巧】-DNSlog外带文件

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 1.什么是DNSlog 我们都知道DNS就是将域名解析为ip&#xff0c;用户在浏览器上输入一个域名A.com&#x…

实现Nginx四层负载均衡

Nginx在1.9.0版本开始支持tcp模式的负载均衡&#xff0c;在1.9.13版本开始支持udp协议的负载&#xff0c;udp主要用于 DNS的域名解析&#xff0c;其配置方式和指令和http 代理类似&#xff0c;其基于ngx_stream_proxy_module模块实现tcp 负载&#xff0c;另外基于模块ngx_strea…

vue2版本空目录下创建新项目的方法2024

vue2版本空目录下创建新项目的方法2024 node -v npm -v vue -V 安装vue-cli 2.9版本的命令 npm install vue-cli -g 卸载vue2.x方法&#xff1a; npm uninstall vue-cli -g 设置 NPM 镜像 npm config set registry https://registry.npmmirror.com vue -V 报错时需设置环…

多进程和多线程基础概念LINUX

进程和程序的区别 程序是静态的&#xff0c;它是保存在磁盘上的指令的有序集合&#xff0c;没有任何执行的概念进程是一个动态的概念&#xff0c;它是程序执行的过程&#xff0c;包括了动态创建、调度和销毁的整个过程 并行&#xff1a;在 cpu 多核的支持下&#xff0c;实现物…

Git 的基本使用

1.创建 Git 本地仓库 仓库是进⾏版本控制的⼀个⽂件⽬录。我们要想对⽂件进⾏版本控制&#xff0c;就必须先创建⼀个仓库出来&#xff0c;例如下面代码创建了gitcode_linux的文件夹&#xff0c;之后再对其进行初始化。创建⼀个 Git 本地仓库对应的命令为 git init &#xff0c…

视频项目开发,EasyCVR视频融合平台为何成为关键驱动力

智慧类视频项目是基于多个系统融合&#xff0c;旨在实现更广泛联动功能&#xff0c;以满足智能化应用需求为基石的信息化项目。当前&#xff0c;智慧社区、智慧园区、智慧工厂乃至智慧城市等应用场景的需求日益增长。这些智慧项目的整合进程中&#xff0c;视频融合能力扮演着不…

ASP.NET Core 入门教程一 创建最小 API

构建最小 API&#xff0c;以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 启动 Visual Studio 2022 并选择“创建新项目”。 在“创建新项目”…

RabbitMQ 最新版 安装,配置,java接入使用(详细教程)

一 RabbitMQ下载 RabbitMQ 官网最新版下载&#xff1a; RabbitMQ: One broker to queue them all | RabbitMQ RabbitMQ依赖erlang-26.2.5.2-1.el7.x86_64.rpm下载&#xff1a; https://github.com/rabbitmq/erlang-rpm/releases/download/v26.2.5.2/erlang-26.2.5.2-1.el7.…

Information Processing Technician

信息处理技术员试题 🔥SeptemberZone 1.信息是一种() A.资源 B.物质 C.能量 D.载体 2.以下关于信息的表达中,不正确的选项是() A.一切数据都能产生信息 B.信息的产生、处理和传递依靠于物质和能量 C.同一信息在不同的时间可能具有不同的价值 D.信息的屡次使用不会使信息…

MD编辑器学习笔记

MD编辑器学习笔记 目录标题文本样式列表图片链接代码片数学公式表格部分总结 目录 目录是使用“[TOC](目录&#xff09;”&#xff0c;记住别忘了加上&#xff08;&#xff09;标题 使用#来确定标题&#xff0c;几个#就是几级标题。记住#后面要加上空格文本样式 tips: 在写正…

什么是制造业项目管理软件?适合制造企业的项目管理软件具备哪些特征

当前&#xff0c;我国的制造业呈现出稳步增长与风险并存的现象。经济构建以国内大循环为主体&#xff0c;国产替代的浪潮正在席卷国内制造业&#xff0c;越来越多的制造领域企业开始启动数字化变革来支撑企业的迅猛发展&#xff0c;进一步优化项目管理流程&#xff0c;促进研发…

Docker的概述及如何启动docker的镜像、远程管理宿主机的docker进程

一、概述&#xff1a; 1、Docker 是什么&#xff1f; Docker 是⼀个开源的应⽤容器引擎&#xff0c;可以实现虚拟化&#xff0c;完全采用“沙盒”机制&#xff0c;容器之间不会存在任何接口。 2、Docker 和虚拟机的区别&#xff1a; 1&#xff09;启动速度&#xff1a;Dock…

使用 Visual Studio 编辑器作为 DailyNotes 的 markdown 编辑器

DailyNotes 是我使用过的最优秀的日常笔记管理工具&#xff0c;为它配置一个好的 markdown 编辑器&#xff0c;可以大幅提升效率。 除了使用 Typora 作为 markdown 编辑器&#xff0c;Visual Studio Code 也是一个非常不错的选择&#xff0c;令人惊喜的是&#xff0c;它也支持…

React学习笔记(二)——react基础

目录 1. 受控表单绑定 2. React中获取DOM 3. 案例&#xff1a;B站评论 — 发表评论 3.1 B站评论案例 —— 核心功能实现 3.2 B站评论案例 — id处理和时间处理 3.3 B站评论案例 — 清空内容并重新聚焦 4. 组件通信 4.1 理解组件通信 4.2 父传子-基础实现 4.3 父传子-…

基于Spark计算网络图中节点之间的Jaccard相似性

基于Spark计算网络图中节点之间的Jaccard相似性 Jaccard 相似度是一种较为常用的衡量两个集合相似性的指标&#xff0c;用于计算两个集合的交集与并集的比率。具体来说&#xff0c;它的计算公式为&#xff1a; 在网络图中同样经常使用Jaccard来计算节点之间的相似性&#xff…