实现AOP机制 + Spring总结

文章目录

    • 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);@Overridepublic float getSum(float a, float b) {log.info("SmartDog...getSum...res=" + (a + b));return a + b;}@Overridepublic 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;// 初始化方法之前调用后置处理器 postProcessBeforeInitializationfor (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {Object bean1 = beanPostProcessor.postProcessBeforeInitialization(bean, name);// 如果beanPostProcessor返回的对象为空,则使用原来的对象if (bean1 != null) {current = bean1;}}// 初始化beaninit(current);// 初始化方法之后调用后置处理器 postProcessAfterInitializationfor (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.判断是否需要注入容器,查看有没有自定义的注解ComponentClass<?> aClass = null;try {aClass = classLoader.loadClass(fullPath);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}// 如果该类使用了注解Component则说明是一个spring beanif (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的idbeanName = 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.rhkb.cn/news/406869.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity抖音直播玩法开发流程

前言 近两年直播玩法逐渐新兴起来了&#xff0c;也出现不少质量还不错的作品&#xff0c;比如下列《红蓝对决》《三国全战》等。近期我们也做了一款直播玩法&#xff0c;就此记录下开发流程。 1&#xff0c;申请应用 进入抖音开发者平台&#xff0c;在首页入驻平台。 如果是…

Vue3+Vite 解决“找不到模块“@/components/xxx.vue”或其相应的类型声明 ts(2307)”

1. 安装插件 pnpm i types/node -D2. 修改vite.config.ts文件 import path from path;resolve: {alias: {"": path.resolve(__dirname,"./src"),},},3. 修改tsconfig.app.json文件 别人教的都是修改tsconfig.json文件&#xff0c;但是我发现可能是因为版…

写论文找不到灵感?ChatGPT能提供的一些帮助

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 在学术写作过程中&#xff0c;许多读者常常会面临一个问题——找不到灵感。面对庞大的文献和复杂的研究方向&#xff0c;往往感到无从下手。随着人工智能技术的发展&#xff0c;像ChatG…

redis面试(十九)读写锁ReadLock

读写锁ReadLock 简单来说就是互斥锁和非互斥锁。多个客户端可以同事加的锁叫读锁&#xff0c;只能有一个客户端加的锁叫写锁。这个理论应该是从数据库中来的&#xff0c;放在这里也是同样的解释。 多个客户端同时加读锁&#xff0c;是不会互斥的&#xff0c;多个客户端可以同…

“肯将玉钳作双戟,一舞天下定乾坤。”记唐铎《墨龙图》之中的笔墨画意

唐铎&#xff0c;1957 年生于北京&#xff0c;国家一级美术师&#xff0c;曾先后师从于刘文西、黄申发老师&#xff0c;原名唐京鸣&#xff0c;京城人士&#xff0c;取其名&#xff0c;不鸣则已&#xff0c;一鸣惊人之意&#xff0c;学画三十余年&#xff0c;专注于齐派虾蟹&am…

【技巧】-DNSlog外带文件

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

深入探索分布式任务调度框架:MySQL实现高效锁机制

本文主要介绍项目中怎么使用 MySQL 实现分布式锁的 背景 假如我们现在要做一个高性能、可扩展的分布式任务调度框架&#xff0c;要怎么设计呢&#xff1f;下面是我之前自己设计的一个架构图。 为了方便后续的分布式锁的设计&#xff0c;我们大致描述下各个角色都做了哪些事情…

鹏哥C语言自定义笔记重点(29-)

29.函数指针数组 30.void指针是不能直接解引用&#xff0c;也不能-整数。 void*是无具体类型的指针&#xff0c;可以接受任何类型的地址。 31.qsort:使用快速排序的思想实现一个排序函数(升序) 32. 33.地址的字节是4/8 34.char arr[]{a,b} sizeof(arr[0]1)答案是4&#xff0…

Godot《躲避小兵》实战之游戏开始界面制作

我们的游戏还需要用户可操作的界面&#xff0c;比如开始游戏&#xff0c;退出以及显示分数等UI界面。 创建新场景&#xff0c;点击“其他节点”按钮&#xff0c;然后添加一个 CanvasLayer 节点并命名为 HUD。“HUD”是“heads-up display”&#xff08;游戏信息显示&#xff0…

windows所有功能都可使用就是电脑黑屏了

运行新任务然后输入explorer.exe勾选上创建任务确定就好了 下次尽量不手欠&#xff01;&#xff01;&#xff01;&#xff01;

Golang | Leetcode Golang题解之第367题有效的完全平方数

题目&#xff1a; 题解&#xff1a; func isPerfectSquare(num int) bool {x0 : float64(num)for {x1 : (x0 float64(num)/x0) / 2if x0-x1 < 1e-6 {x : int(x0)return x*x num}x0 x1} }

改编版猜数字小游戏,猜错了就黑屏(整蛊版本)

1. 前情提要 在前一篇博客中&#xff0c;我们了解到了如何获得随机数&#xff0c;并且通过运算可以规定所获得的这个随机数的范围在多少数值之间 那么接下来我们就需要去具体去实现猜数字游戏的各种布置 2. 布置主菜单 玩一个游戏&#xff0c;最开始的界面都会是一个主菜单…

hutool excel 导出,格式错误,打开乱码

现象 最近在使用hutool excel 实现网站数据导出excel功能。在swagger 中调试接口时候遇到下载文件不能打开或者乱码的问题。 1. xlsx 文件不能打开 2. xls 文件打开乱码 解决方案 直接使用postman 或者 get 请求使用浏览器地址栏 总之&#xff0c;excel 文件不能导出&#xf…

leetcode 49 字母异位分词

正文 基础解法 首先&#xff0c;我们创建一个字典对象&#xff0c;然后遍历整个字符串列表&#xff0c;并且使用 sorted() 函数对字符串列表进行排序&#xff0c;所有的异位分词经过排序后它们的组成和顺序会趋于一致。但是需要注意的是 sorted 对字符串进行排序后会变成一个由…

day06_算法训练

一. Stream流 1.1 Stream流概述 概念: jdk1.8以后提供的新的API, 主要用于批量操作数据(集合的另外一种操作方式),代码非常简洁 流式处理思想: 2.2 Stream对象获取 1.单列集合的Stream流对象获取 2.双列集合的Stream流对象获取 3.数组的Stream流对象获取 4.散装数据的St…

上线eleme项目

&#xff08;一&#xff09;搭建主从从数据库 主服务器master 首先下载mysql57安装包&#xff0c;然后解压 复制改目录到/usr/local底下并且改个名字 cp -r mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql 删掉/etc/my.cnf 这个会影响mysql57的启动 rm -rf /etc…

科研绘图系列:Python语言时间趋势图

介绍 不同指标在时间上的变化,可以用时间序列线图表示趋势。 加载Python包 import sys import pandas as pd import numpy as np import scipy as sp from scipy import stats import randomimport seaborn as sns import matplotlib.pyplot as plt from matplotl

Open3D mesh 隐藏点移除

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 3.1原始点云 3.2去除隐藏点后的点云 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&…

C++ | Leetcode C++题解之第363题矩形区域不超过K的最大数值和

题目&#xff1a; 题解&#xff1a; class Solution { public:int maxSumSubmatrix(vector<vector<int>> &matrix, int k) {int ans INT_MIN;int m matrix.size(), n matrix[0].size();for (int i 0; i < m; i) { // 枚举上边界vector<int> sum(…