手写模拟Spring的基本功能

news/2024/10/22 14:02:30/

文章目录

  • 1. Spring的基本功能
  • 2. 容器启动+ 容器启动,即创建容器对象并赋予配置对象
  • 3. BeanDefinition扫描
  • 4. Bean的生命周期
  • 5. 单例Bean与多例Bean
  • 6. 依赖注入
  • 7. AOP
  • 8. Aware 回调
  • 9. 初始化
  • 10. BeanPostProcessor
  • 附录:

1. Spring的基本功能

在这里插入图片描述

2. 容器启动+ 容器启动,即创建容器对象并赋予配置对象

java">// 自定义的 IOC 容器对象
GyhApplicationContext gyhApplicationContext =new GyhApplicationContext(AppConfig.class);

3. BeanDefinition扫描

  • 对于基于注解的方式,会在构造方法中扫描 @ConponentScan 的 value 值对应其下的所有类对应的 .class 文件(存在于 out 文件夹下)
  • 将 扫描到的bean信息(Class, scope)封装至 BeanDefinition 对象中并由 一个 Map (beanDefinitionMap) 保存 beanName ---> BeanDefinition

4. Bean的生命周期

在这里插入图片描述

createBean 中:

  1. 利用 Bean 的空构造器通过反射创建
  2. 设置对象属性(伴随着依赖注入)
  3. 执行前置处理器
  4. 执行初始化方法
  5. 执行后置处理器
  6. 调用使用
  7. 执行 destory 方法

5. 单例Bean与多例Bean

  • 对于 单例Bean 是在初始化容器时(扫描过程中)创建并由 一个 Map(singletonObjects)管理 beanName —> beanInstance
  • 对于 多例Bean 是在 getBean() 方法中实时创建的

6. 依赖注入

  • 在 createBean 方法中,扫描所有 bean 被 @Autowire 标注的属性为其注入容器中的 bean
  • 若当前容器中还没有所依赖bean,则会创建该bean,然后再注入

7. AOP

  • spring是基于 动态代理
  • 初始化后,通过动态代理生成实现了指定接口的代理类
  • 使用了AOP对应的 bean 会被代理类的对象所替换

8. Aware 回调

  • 对应自定义的bean的类实现了Spring中指定的 Aware接口。可以在创建 bean 的过程中被检索到,并在定制的顺序中执行 Aware接口中定义的方法(即调用bean中实现的Aware接口的方法)

9. 初始化

  • spring的Aware接口中的一种,spring会检索实现了 InitializationBean 接口的 Bean。在创建这些bean是会调用其实现的 afterPropertiesSet 方法

10. BeanPostProcessor

  • 后置处理器,也是Aware回调接口中的一种,其定义的前置与后置方法会被用在所有bean的创建过程中

附录:

GyhApplicationContext

java">package com.gyh.spring;import com.gyh.service.UserService;import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** @author Gao YongHao* @version 1.0* 模拟Spring的javaConfig的容器启动方式*/public class GyhApplicationContext {private Class<?> configClass;// 用于保存Bean对象的信息(class 对象, 作用域信息)private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap =new ConcurrentHashMap<>();// 用于保存单例的对象private ConcurrentHashMap<String, Object> singletonObjects =new ConcurrentHashMap<>();// 用于保存 BeanPostProcessor 的集合private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();public GyhApplicationContext(Class<?> configClass) {this.configClass = configClass;// 扫描,单例的对象被直接创建,多例的对象在使用时创建if (configClass.isAnnotationPresent(ComponentScan.class)) {ComponentScan annotation = configClass.getAnnotation(ComponentScan.class);String packagePath = annotation.value(); // 扫描路径 "com.gyh.service"packagePath = packagePath.replace(".", "/");// 在 out 文件夹中定位 .class 文件的路径ClassLoader classLoader = GyhApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(packagePath);File file = new File(resource.getFile());
//            System.out.println(file);// 如果是文件夹if (file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {String absolutePath = f.getAbsolutePath();// 筛选出 .class 结尾的字节码文件if (absolutePath.endsWith(".class")) {String loadClassPath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));loadClassPath = loadClassPath.replace("\\", ".");// loadClassPath == "com\gyh\service\UserService"
//                        System.out.println(loadClassPath);try {// 利用类加载器加载指定文件夹下的 .class 文件Class<?> aClass = classLoader.loadClass(loadClassPath);// 查看是否有标注 Bean 对应的的注解if (aClass.isAnnotationPresent(Component.class)) {// 扫描出实现了 BeanPostProcessor 的 bean,将其实例化对象放置于 集合 中if (BeanPostProcessor.class.isAssignableFrom(aClass)) {beanPostProcessors.add((BeanPostProcessor) aClass.newInstance());}// 设置的Bean名称(若为""或null)则设置为首字母小写的类名String beanName = aClass.getAnnotation(Component.class).value();if ("".equals(beanName)) {beanName = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);}// 创建保存Bean信息的对象BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(aClass); // 该对象类型if (aClass.isAnnotationPresent(Scope.class)) {Scope annotation1 = aClass.getAnnotation(Scope.class);String value = annotation1.value();beanDefinition.setScope(value);} else { // 默认是单例的beanDefinition.setScope("Singleton");}// 放置于 beanDefinitionMap 中管理beanDefinitionMap.put(beanName, beanDefinition);}// 填充单例对象for (Map.Entry<String, BeanDefinition> s : beanDefinitionMap.entrySet()) {BeanDefinition value = s.getValue();if (value.getScope().equals("Singleton")) {// 存有依赖注入的单例对象生成singletonObjects.put(s.getKey(), createBean(s.getKey(), value));}}} catch (Exception e) {e.printStackTrace();}}}}}}/*** 创建Bean对象,** @param beanName       bean 对象名称* @param beanDefinition bean 对象的信息* @return*/public Object createBean(String beanName, BeanDefinition beanDefinition) {Class<?> type = beanDefinition.getType();Object o = null;try {o = type.getConstructor().newInstance();// 依赖注入// 遍历属性对属性进行依赖注入for (Field f : type.getDeclaredFields()) {if (f.isAnnotationPresent(Autowired.class)) {f.setAccessible(true); // 关闭访问权限// 将容器中管理的bean注入到该属性中(可能先于当前类创建,也可能后于当前类创建)f.set(o, getBean(beanName, type));}}// 回调机制,查看当前的Bean是否实现了指定的接口// 如果实现则执行相关方法(BeanPostProcessor即是如此实现)if (o instanceof BeanNameAware) {((BeanNameAware) o).setBeanName(beanName);}for(BeanPostProcessor b:beanPostProcessors){b.beforePostProcess(beanName, o);}// 初始化也使用回调实现// 基于AOP会在后置处理器中创建 bean 的动态代理对象,并封装切面逻辑for(BeanPostProcessor b:beanPostProcessors){b.afterPostProcess(beanName, o);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return o;}/*** 用于返回 Bean 对象的方法** @param beanName Bean 的名称* @param clazz    Bean 对应的 Class 对象* @param <T>* @return*/public <T> T getBean(String beanName, Class<T> clazz) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition == null) {throw new NullPointerException();} else {String scope = beanDefinition.getScope();if (scope.equals("Singleton")) {// 从单例池中获取对象Object o = singletonObjects.get(beanName);if (o == null) { // 对应依赖注入时可能会用到o = createBean(beanName, beanDefinition);singletonObjects.put(beanName, o); // 放入单例池中}return (T) o;} else { // 多例则实时创建Object o = null;try {o = beanDefinition.getType().newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return (T) o;}}}
}

http://www.ppmy.cn/news/1541070.html

相关文章

Ajax:跨域、防抖和节流、HTTP协议

在善意的“双向奔赴”中&#xff0c;每个普通人都如星辰&#xff0c;微小但释放着自己的光芒&#xff0c;交织成灿烂的星河 文章目录 跨域防抖和节流HTTP协议HTP状态码以及代表意义错误代码的影响移动的小天使 跨域 同源策略 概念&#xff1a;协议&#xff0c;域名&#xff0c…

大数据-178 Elasticsearch Query - Java API 索引操作 文档操作

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

uniapp 获取签名证书 SHA1 自有证书签名打包

1.登录你的Dcloud 账户 2.找到我的应用菜单 3.点开某个应用 4.查看证书详情&#xff0c;里面有SHA1 和别名&#xff0c;密码&#xff0c;下载证书用于云打包&#xff0c;可以选择自有证书&#xff0c;输入别名&#xff0c;密码打包

Linux网络编程(六)-TCP协议服务端及代码实现

1.概述 在讲述了那么多以后我们终于来到了代码阶段的讲解了&#xff0c;先放一张流程图便于大家理解。接着会为大家讲述具体的实现过程。 通过上图我们可以看到一个完整的Socket网络通信&#xff0c;是有客户端和服务端两部分代码组成的&#xff0c;即两个程序&#xff08;你发…

Android TextView实现一串文字特定几个字改变颜色

遇到一个需求&#xff0c;让Android端实现给定一个字符串指定下标的几个字颜色与其他字颜色不一致。 主要是用ForegroundColorSpan这个API来传入颜色值&#xff0c;用SpannableString来设置指定索引下标的字的颜色值。 这里通过给定一个输入文字描述框&#xff0c;要求输入指定…

Mysql 和MongoDB用户访问权限问题

Mysql 刚给二线运维排查了一个问题&#xff0c;Mysql安装完可用&#xff0c;且可用navicat连接&#xff0c;项目中通过127.0.0.1去连数据库报错了。错误是access denied for user ‘root’localhost,排查思路 1. 密码是否正确 &#xff08;不需要重置。到Mysql的安装目录下找…

流批一体计算引擎-17-[Flink]中的Table API常用算子

文章目录 1 概述&示例1.1 data.csv1.2 代码示例2 操作算子2.1 扫描、投影和过滤2.1.1 from_path【流批】2.1.2 from_elements【流批】2.1.3 select【流批】2.1.4 alias【流批】2.1.5 where【流批】2.1.6 filter【流批】2.2 列操作2.2.1 add_columns【流批】2.2.2 add_or_re…

13 django管理系统 - 注册与登录 - 中间件控制访问

去管理员列表中&#xff0c;获取刚才登录的用户session&#xff1a; 用户发来请求&#xff0c;获取cookie随机字符串&#xff0c;拿着随机字符串&#xff0c;看看session中有没有 通过request.session.get("user_info")来获取 def admin_list(request):# 获取当前登…