手写Spring

简单实现Spring基于注解配置

 

ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {String value() default "";
}

相当于component-scan 

HspSpringConfig

@ComponentScan(value = "spring.write.component")
public class HspSpringConfig {
}

替代bean.xml文件,添加@ComponentScan,获得扫描的包

AppMain

public class AppMain {public static void main(String[] args) {HspApplicationContext ioc = new HspApplicationContext(HspSpringConfig.class);Object userDao = ioc.getBean("userDao");System.out.println(userDao);}
}

HspApplicationContext

public class HspApplicationContext {private Class configClass;private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();public HspApplicationContext(Class configClass) {//1、获得扫描包路径//得到config文件的.class类型this.configClass = configClass;//反射得到ComponentScan注解ComponentScan componentScan =(ComponentScan)configClass.getDeclaredAnnotation(ComponentScan.class);//获取注解的value就是扫描的包路径String path = componentScan.value();System.out.println(path);//spring.write.component//2、得到包下的所有.class//得到类的加载器,获取实际目录下的(out),ClassLoader classLoader = ClassLoader.getSystemClassLoader();//URL必须按照斜杠/来写//path = spring.write.componentpath = path.replace(".","/");URL resource = classLoader.getResource(path);System.out.println(resource);//file:/D:/Atest/spring/out/production/spring/spring/write/component//对路径下的文件进行遍历File file = new File(resource.getFile());if(file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {//只处理.class文件(java文件)String fileAbsolutePath = f.getAbsolutePath();if(fileAbsolutePath.endsWith(".class")) {System.out.println(fileAbsolutePath);//D:\Atest\spring\out\production\spring\spring\write\component\UserDao.class//前面已经有了path,还需要具体的类名进行反射,放入容器String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));String classFullName = path.replace("/", ".") + "." + className;//判断该类是不是需要注入到容器,有无注解try {Class<?> aClass = classLoader.loadClass(classFullName);//判断该类的实例是否有注解if(aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Controller.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class)) {//查看是否指定了idif(aClass.isAnnotationPresent(Component.class)) {//只演示了ComponentComponent component = aClass.getDeclaredAnnotation(Component.class);String id = component.value();if(! "".endsWith(id)) {className = id;}}//放入容器Class<?> clazz = Class.forName(classFullName);Object instance = clazz.newInstance();//类名首字母小写存入,springframework下的工具类ioc.put(StringUtils.uncapitalize(className),instance);}} catch (Exception e) {e.printStackTrace();}}}}}public Object getBean(String name) {return ioc.get(name);}
}

Spring整体架构分析

没有加@Scope(value="prototype")就是单例
ioc.getBean("name"),先到BeanDefinition Map获取,未找到异常处理,
如果是单例(single)就从单例Bean Map获取
如果是多例(prototype)就创建bean对象并返回(到BeanDefinition Map中,得到Bean的clazz对象,使用反射,创建bean返回)

1、实现扫描包,得到bean的class对象

Spring基于注解配置已经写过

HspApplicationContext

public class HspApplicationContext {private Class configClass;public HspApplicationContext(Class configClass) {this.configClass = configClass;ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);String path = componentScan.value();System.out.println(path);//com.spring.componentClassLoader classLoader =HspApplicationContext.class.getClassLoader();path = path.replace(".", "/");System.out.println(path);//com/spring/componentURL resource =classLoader.getResource(path);File file = new File(resource.getFile());if(file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String fileAbsolutePath = f.getAbsolutePath();if(fileAbsolutePath.endsWith(".class")) {String className = fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));String classFullName = path.replace("/", ".") + "." + className;try {Class<?> clazz = classLoader.loadClass(classFullName);if(clazz.isAnnotationPresent(Component.class)) {System.out.println("是Spring bean【class对象】= " + clazz + " 【类名】 = " + className);} else {System.out.println("不是Spring bean【class对象】= " + clazz + " 【类名】 = " + className);}} catch (Exception e) {e.printStackTrace();}}}}}
}

2、扫描bean信息,封装到BeanDefinition对象并放入Map

增加@Scope注解来判断单例(single)或者多例(prototype)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {String value() default "";
}

BeanDefinition用于封装/记录Bean信息
1、单例/多例
2、多例需要动态生成,存放Bean对应的Class对象,反射生成

public class BeanDefinition {private String scope;private Class clazz;public String getScope() {return scope;}public void setScope(String scope) {this.scope = scope;}public Class getClazz() {return clazz;}public void setClazz(Class clazz) {this.clazz = clazz;}@Overridepublic String toString() {return "BeanDefinition{" +"scope='" + scope + '\'' +", clazz=" + clazz +'}';}
}

HspApplicationContext

private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();
//单例对象类型不确定,用Object
private ConcurrentHashMap<String, Object> singletonObejcts =new ConcurrentHashMap<>();

如果有@Component,就放入beanDefinitionMap中

可以将1、2步写成一个方法,在构造器里调用

3、初始化bean单例池并完成getBean、createBean方法

public class HspApplicationContext {private Class configClass;private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();//单例对象类型不确定,用Objectprivate ConcurrentHashMap<String, Object> singletonObejcts =new ConcurrentHashMap<>();public HspApplicationContext(Class configClass) {//完成扫描指定包beanDefinitionScan(configClass);//遍历所有的beanDefinition对象Enumeration<String> keys = beanDefinitionMap.keys();while(keys.hasMoreElements()) {String beanName = keys.nextElement();BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if("singleton".equalsIgnoreCase(beanDefinition.getScope())) {Object bean = createBean(beanDefinition);singletonObejcts.put(beanName,bean);}}System.out.println("singletonObejcts单例池=" + singletonObejcts);System.out.println("beanDefinitionMap=" + beanDefinitionMap);}public void beanDefinitionScan(Class configClass) {}public Object createBean(BeanDefinition beanDefinition) {Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}return null;}public Object getBean(String name) {//判断beanName是否存在if(beanDefinitionMap.containsKey(name)) {BeanDefinition beanDefinition = beanDefinitionMap.get(name);if("singleton".equalsIgnoreCase(beanDefinition.getScope())) {return singletonObejcts.get(name);} else {return createBean(beanDefinition);}} else {throw new NullPointerException("没有该Bean");}}
}

4、依赖注入

增加@Autowired注解,这里只用名字来进行匹配

 

    public Object createBean(BeanDefinition beanDefinition) {Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();//遍历所有字段,for(Field field : clazz.getDeclaredFields()) {//是否有Autowiredif(field.isAnnotationPresent(Autowired.class)) {String name = field.getName();//通过getBean获取要组装的对象Object bean = getBean(name);//进行组装,private需要暴破field.setAccessible(true);field.set(instance,bean);//将 bean 赋值给 instance 的 field}}return instance;} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();}return null;}

5、bean后置处理器实现

原生Spring接口InitializingBean(初始化),自己实现这个接口,完成初始化方法
接口中有afterPropertiesSet()方法,在Bean的setter方法执行完毕后,被spring容器调用,就是初始化方法

public interface InitializingBean {void afterPropertiesSet() throws Exception;
}

应用:选择MonsterService来实现这个接口

@Component("monsterService")
@Scope(value = "property")
public class MonsterService implements InitializingBean {@Autowiredprivate MonsterDao monsterDao;public void m1() {monsterDao.hi();}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("monsterService初始化方法被调用");}
}

在创建好Bean实例后,判断是否需要初始化
容器中常用的一个方法是:根据该类是否实现了某个接口,来判断是否要执行某个业务(接口编程)

在HspApplicationContext的createBean返回instance之前加上判断是否是这个接口的子类型,是的话就执行初始化方法

判断如果返回为null,不变化
加上beanName

public Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getClazz();try {Object instance = clazz.getDeclaredConstructor().newInstance();//遍历所有字段,for(Field field : clazz.getDeclaredFields()) {//是否有Autowiredif(field.isAnnotationPresent(Autowired.class)) {String name = field.getName();//通过getBean获取要组装的对象Object bean = getBean(name);//进行组装,private需要暴破field.setAccessible(true);field.set(instance,bean);//将 bean 赋值给 instance 的 field}}System.out.println("======创建好实例======" + instance);//初始化方法前,调用before方法,可以对容器的bean实例进行处理,然后返回处理后的beanfor (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {Object current = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);if(current != null)instance = current;}if(instance instanceof InitializingBean) {try {((InitializingBean) instance).afterPropertiesSet();//将instance转成InitializingBean类型,调用方法} catch (Exception e) {e.printStackTrace();}}//初始化方法后,调用after方法,可以对容器的bean实例进行处理,然后返回处理后的beanfor (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {Object current = beanPostProcessor.postProcessAfterInitialization(instance, beanName);if(current != null)instance = current;}return instance;

后置处理器接口BeanPostProcessor

对容器中的所有bean生效

public interface BeanPostProcessor {//bean的初始化前调用default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}//bean的初始化后调用default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}

自己写一个类实现后置处理器,通过Component注入到容器中

实现类可以不止一个,放在Arraylist中

private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

@Component
public class HspBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("后置处理器before()");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("后置处理器after()");return bean;}
}

在HspApplicationContext的扫描方法中扫描Component,再判断是否实现了后置处理器接口

6、AOP机制实现 

原生Spring实现方法
A接口写方法,B类实现A接口,给B类加上@Component注入到容器
切面类加上@Component和@Aspect
B类执行后置处理器的After方法后,变成代理对象

public interface SmartAnimalable {float getSum(float i, float j);float getSub(float i, float j);
}
@Component(value = "smartDog")
public class SmartDog implements SmartAnimalable {public float getSum(float i, float j) {float res = i + j;System.out.println("SmartDog-getSum-res=" + res);return res;}public float getSub(float i, float j) {float res = i - j;System.out.println("SmartDog-getSub-res=" + res);return res;}
}
public class SmartAnimalAspect {public static void showBeginLog() {System.out.println("前置通知..");}public static void showSuccessLog() {System.out.println("返回通知..");}
}

在HspBeanPostProcessor的after方法中实现AOP(写死的方法)

public Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("后置处理器after()");if("smartDog".equals(beanName)) {Object proxyInstance = Proxy.newProxyInstance(HspBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;if("getSum".equals(method.getName())) {SmartAnimalAspect.showBeginLog();result = method.invoke(bean,args);SmartAnimalAspect.showSuccessLog();} else {result = method.invoke(bean,args);}return result;}});return proxyInstance;}return bean;}

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

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

相关文章

两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力

今天就遇到有点儿dt的问题&#xff0c;利用大模型顺利通了自定义的工具调用&#xff08;并没有用到tools功能&#xff0c;而是通过prompt强制输出&#xff09;&#xff0c;单个单个的没问题哈&#xff0c;但是多个一起就出现问题了 我说“关闭电脑PC1, 打开第2台电脑” 它看不懂…

安卓实现导入Excel文件

使用简化版的jar包 api files(libs/poi-3.12-android-a.jar) api files(libs/poi-ooxml-schemas-3.12-a.jar) 导入遇到了两个兼容问题 1.build.gradle文件里面 android { 要添加 packagingOptions {exclude META-INF/INDEX.LIST } 2.加载大文件要在清单文件里面加androi…

网络变压器HR911130C的使用注意点

HR911130C的使用&#xff0c;需要2个注意点&#xff1a; 1&#xff09;数据线data0、data2、data3是相邻的引脚&#xff0c;但是data1是 不相邻的两个引脚&#xff0c;注意看下面的电路图&#xff0c;所以绘图时需要注意 2&#xff09;LED灯的连接 11脚、12脚&#xff0c;连…

快手可灵AI全球升级1.5模型:引入“运动笔刷”功能 画质大幅提升

9月19日&#xff0c;快手公司宣布其可灵AI模型进行了全球范围内的重磅升级&#xff0c;推出了1.5版本。新版本在多个方面实现了显著提升&#xff0c;包括视频画质、动态效果、美学表现、运动合理性以及语义理解等。 新升级的1.5模型支持在高品质模式下直接输出1080p高清视频&am…

【CSS】一行三个盒子 每个盒子都是16:9

padding-top 属性接受百分比值时,其百分比是基于父元素的宽度来计算的,而不是自身元素的宽度 aspect-ratio 更方便&#xff0c;但存在兼容性问题 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name&quo…

字符设备驱动 — 4 异常与中断

异常与中断 中断属于异常的一种 异常会直接打断 CPU 的运行&#xff0c;而各种中断会被传到中断控制器&#xff0c;由中断控制器来选择优先级最高的中断并通知 CPU 处理流程 arm 对异常&#xff08;中断&#xff09;处理流程&#xff1a; 初始化&#xff1a; 设置中断源&…

水经微图PC版5.0.0即将内测

让GIS更简单高效&#xff01; 水经微图&#xff08;以下称“微图”&#xff09;PC版5.0.0即将内测&#xff0c;这是一个基于WeMapEngine开发的全新版本。 关于什么是WeMapEngine&#xff0c;请从《WeMapEngine可快速构建的GIS应用功能》一文中了解。 微图5.0.0功能界面 水经…

【分享】“可恶”的运算放大器电容负载

他们说如果使用放大器驱动电容负载(图 1、CLOAD)&#xff0c;一个不错的经验是采用一个 50 或 100 欧的电阻器 (RISO) 将放大器与电容器隔开。这个附加电阻器可能会阻止运算放大器振荡。 图 1.支持电容负载的放大器可能需要在放大器输出与负载电容器之间连接一个电阻器。 使用…

STM32—I2C通信外设

1.I2C外设简介 STM32内部集成了硬件I2C收发电路&#xff0c;可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能&#xff0c;减轻CPU的负担支持多主机模型&#xff08;可变多主机&#xff09;支持7位/10位地址模式&#xff08;11110......)支持不同的通…

JavaWeb JavaScript 11.XML —— 配置文件

生活想埋没我&#xff0c;没想到我是颗种子 —— 24.9.19 一、XML 1.什么是XML XML是EXtensible Markup Languge的缩写&#xff0c;翻译过来就是可扩展标记语言。所以很明显&#xff0c;XML和HTML一样都是标记语言&#xff0c;也就是说它们的基本语法都是标签 可扩展 三个字…

OpenCV基础入门30讲(Python)——第二讲 图像色彩转换

常见的几种颜色类型介绍 1、彩色图像&#xff08;Color Image&#xff0c;BGR&#xff09; 数据类型&#xff1a;uint8通道数&#xff1a;3&#xff08;BGR&#xff1a;蓝色、绿色、红色&#xff09;描述&#xff1a;彩色图像有三个通道&#xff0c;每个通道的值范围是 0 到 …

【图书推荐】《Autodesk Inventor 2024入门与案例实战(视频教学版)》

本书重点 配套示例文件、PPT课件、教学视频、电子教案、课程标准、骄婿大纲、模拟试题、作者微信群答疑服务。 内容简介 《Autodesk Inventor 2024入门与案例实战&#xff1a;视频教学版》以Autodesk Inventor 2024为平台&#xff0c;重点介绍Autodesk Inventor 2024中文版的…

洗衣机制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

洗衣机制造业作为传统制造业的重要组成部分&#xff0c;通过引入5G智能工厂物联数字孪生平台&#xff0c;加速推进自身的数字化转型进程。这一创新模式不仅极大地提升了生产效率&#xff0c;还深刻改变了产品的设计、生产、管理及运维流程&#xff0c;为行业带来了前所未有的竞…

[数据集][目标检测]手机识别检测数据集VOC+YOLO格式9997张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;9997 标注数量(xml文件个数)&#xff1a;9997 标注数量(txt文件个数)&#xff1a;9997 标注…

saltstack企业实战

saltstack官网最新文档 saltstack架构设计 saltstack 高可用方案&#xff1a;Salt官网是有 HARebalance minion配置里写多个master地址 failover&#xff08;syndic&#xff09; 架构 操作系统&#xff1a;CentOS7.6salt版本&#xff1a;3000.3 多master https://www.cn…

【贪心算法】贪心算法一

贪心算法一 1.柠檬水找零2.将数组和减半的最少操作次数3.最大数4.摆动序列 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1f496;&#x1f496; 你的支持是对我最大的鼓励&#xff0c;我们一起努力吧!&#x1f603;&#x1f603; 1.柠檬水找零 题目…

【2023工业异常检测文献】SimpleNet

SimpleNet:ASimpleNetworkforImageAnomalyDetectionandLocalization 1、Background 图像异常检测和定位主要任务是识别并定位图像中异常区域。 工业异常检测最大的难题在于异常样本少&#xff0c;一般采用无监督方法&#xff0c;在训练过程中只使用正常样本。 解决工业异常检…

无人机黑飞打击技术详解

随着无人机技术的普及&#xff0c;无人机“黑飞”&#xff08;未经授权或违反规定的飞行&#xff09;现象日益严重&#xff0c;对公共安全、隐私保护及重要设施安全构成了严重威胁。为有效应对这一挑战&#xff0c;各国政府和安全机构纷纷研发并部署了一系列无人机黑飞打击技术…

光控资本:沪指涨0.59%,酿酒板块大幅拉升,数字货币概念等活跃

19日早盘&#xff0c;两市首要指数全线拉升&#xff0c;深证成指、创业板指涨约1%&#xff1b;场内超4800股飘红。 截至午间收盘&#xff0c;沪指涨0.59%报2733.38点&#xff0c;深证成指涨1.25%&#xff0c;创业板指涨0.99%&#xff0c;两市估计成交4263亿元。 盘面上看&…

C++ 9.19

练习&#xff1a;要求在堆区申请5个double类型的空间&#xff0c;用于存储5名学生的成绩。请自行封装函数完成 1> 空间的申请 2> 学生成绩的录入 3> 学生成绩的输出 4> 学生成绩进行降序排序 5> 释放申请的空间 主程序中用于测试上述函数 #include<ios…