当前位置: 首页 > news >正文

做网站 创业 流程品牌词优化

做网站 创业 流程,品牌词优化,珠海集团网站建设,网站建设公司前十名这里我们采用手撕源码的方式,开始探索spring boot源码中最有意思的部分-bean的生命周期,也可以通过其中的原理理解很多面试以及工作中偶发遇到的问题。 springboot基于约定大于配置的思想对spring进行优化,使得这个框架变得更加轻量化&#…

这里我们采用手撕源码的方式,开始探索spring boot源码中最有意思的部分-bean的生命周期,也可以通过其中的原理理解很多面试以及工作中偶发遇到的问题。

springboot基于约定大于配置的思想对spring进行优化,使得这个框架变得更加轻量化,集成各种starter组件时使其能够更加全面。

1、SpringApplication启动类的配置与软件包的反射加载

通常我们在建立一个新的spring boot项目时,利用idea脚手架生成模板内部会自带一个标注有SpringApplication注解的启动类,如下所示:

/*** @author : spring* {@code @description:}* {@code @date} : 2024/2/4* {@code @modified} By: spring* {@code @project:} spring-plus*/
@SpringApplication(scanBeanPackagePath = "com/hlc/springplus/test")
public class ApplicationStarter {public static void main(String[] args) {ApplicationContext applicationContext = new DefaultApplicationContext(ApplicationStarter.class);TestBean contextBean = (TestBean) applicationContext.getBean("test");contextBean.test();}
}

由于本文的代码全部都是手撕,所以会与spring boot的源码有所不同,个人主义完美凸显。

言回正传,用过spring boot的人都知道它主要的特色基础是它的“容器”的概念,我们可以通过配置文件、注解、导入以及反射实例化后调用通用应用上下文注入的方式将我们的bean交给spring容器管理,那么这里启动类启动后“容器”是怎么识别出我们标准或配置的bean信息同时将其实例化、配置属性、配置名称.....的呢?

那么下面就是一个过程

获取启动类上标注注解中的包路径值
SpringApplication annotation = appconfig.getAnnotation(SpringApplication.class);String beanPackagePath = annotation.scanBeanPackagePath();
String path = beanPackagePath.replace('.', '/');

这里的path就是我们实际的包路径,为什么需要将.替换城/呢?实际上我们配置的包路径是软件包中的相对路径,并不是Resource获取时规定的路径格式。

获取当前类的类加载器并根据路径加载URL获取文件
 ClassLoader classLoader = DefaultApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path);File file = new File(resource.getFile());

DefaultApplicationContext就是我当前类的名称,后续串完全部的流程会将全部的代码挂出的,这里的类加载器获取资源的方式是比较常用的。

需要注意的是这里拿到的file有可能是文件夹,也可能是文件。

通过文件夹或文件夹获取字节码
 for (File item : files) {int begin = item.getAbsolutePath().indexOf("com");int end = item.getAbsolutePath().indexOf('.');String className = item.getAbsolutePath().substring(begin, end).replace('\\', '.');try {Class<?> clazz = Class.forName(className);if (clazz.isAnnotationPresent(Component.class)) {beanClazzList.add(clazz);//收集后置处理器(意图是收集后置处理器而不是收集bean对象)if (BeanPostprocessor.class.isAssignableFrom(clazz)) {beanPostprocessorList.add((BeanPostprocessor) clazz.getDeclaredConstructor().newInstance());}}} catch (Exception e) {throw new RuntimeException(e.getMessage());}                            }

上面的类路径获取方式以及判断字节码是否实现了接口BeanPostprocessor的判断、字节码是否标注了注解Component的判断都是比较常用的方法。

那么通过此三步就将全部需要加载的字节码文件都获取到我们的成员变量beanClazzList列表中去了。

2、ApplicationContext接口的定义以及相关注解的配置

虽然我们解决了待加载bean的字节码列表的收集问题,但是spring boot的容器我们还没有加载出来,也没有实现相关注解的配置,注解标注了bean的身份、名称、类型、加载方式、加载条件、加载顺序、依赖关系等。

ApplicationContext接口的定义
public interface ApplicationContext extends BeanFactory {Object getBean(String beanName);<T> void registerBean(T bean, Class<T> clazz, String name);
}

ApplicationContext接口的释义是“应用上下文”,在计算机科学中,上下文表示进程在执行过程中系统内部的资源情况与中断向量表的记录情况,总之代表的是进程所处的逻辑环境。这里顾名思义ApplicationContext代表的也自然就是bean所处的环境。也就是我们口中的spring boot的容器。

对于基本的ApplicationContext能力而言,它应当具备获取bean对象与注册bean对象的能力。所以这里定义的两个基础能力接口。在考虑如何实现它之前,我们还需要配置以下bean相关的其他注解:

组件注解(标注类型为bean组件)

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

注入注解(标注为某类型注入某bean的属性值)

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

作用域注解(标注单例、原型等生存周期的bean类型)

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

懒加载注解(标注即用即加载还是立刻加载)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Lazy {
}

初始化方法注解(标注bean的初始化方法)

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InitBeanMethod {
}

3、实现ApplicationContext接口与bean加载原理

上述我们定义了相关接口与注解,接下来我们实现容器上下文接口以及描述bean是如何加载到容器内部管理的。

那么这里我们就先将实现ApplicationContext接口的DefaultApplicationContext.java代码放在下方:

public class DefaultApplicationContext implements ApplicationContext {private final Class<?> appconfig;private List<Class<?>> beanClazzList = new LinkedList<>();private Map<String, Object> singletonBeanMap = new ConcurrentHashMap<>();private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();private List<BeanPostprocessor> beanPostprocessorList = new LinkedList<>();public DefaultApplicationContext(Class<?> appconfig) {this.appconfig = appconfig;//1、扫描启动类注解的字节码列表scanBeansByPackage(beanClazzList);//2、注册bean的BeanDefinition初始配置信息initBeanDefinition(beanClazzList, beanDefinitionMap);//3、实例化单例bean并存入map中instanceSingletonBeans(beanDefinitionMap, singletonBeanMap);}@Overridepublic Object getBean(String beanName) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (null != beanDefinition && "prototype".equals(beanDefinition.getScope())) {try {return beanDefinition.getBeanClazz().getDeclaredConstructor().newInstance();} catch (InstantiationException | IllegalAccessException | InvocationTargetException |NoSuchMethodException e) {throw new RuntimeException(e.getMessage());}}return singletonBeanMap.get(beanName);}@Overridepublic <T> void registerBean(T bean, Class<T> clazz, String name) {singletonBeanMap.put(name, bean);}/*** 扫描bean的字节码列表** @param beanClazzList bean的字节码列表(待填充)*/protected void scanBeansByPackage(List<Class<?>> beanClazzList) {if (null != appconfig && appconfig.isAnnotationPresent(SpringApplication.class)) {SpringApplication annotation = appconfig.getAnnotation(SpringApplication.class);if (null != annotation) {String beanPackagePath = annotation.scanBeanPackagePath();String path = beanPackagePath.replace('.', '/');ClassLoader classLoader = DefaultApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path);if (resource != null) {File file = new File(resource.getFile());if (file.isDirectory()) {File[] files = file.listFiles();if (files != null) {for (File item : files) {loadAndFilterBeanClazzes(beanClazzList, item);}}} else {loadAndFilterBeanClazzes(beanClazzList, file);}}} else {throw new RuntimeException("Annotation SpringApplication is not exist");}} else {throw new RuntimeException("Annotation SpringApplication is not exist and appconfig is null");}}/*** 加载bean的字节码列表并过滤** @param beanClazzList bean的字节码列表(待填充)* @param item          文件或文件夹*/private void loadAndFilterBeanClazzes(List<Class<?>> beanClazzList, File item) {int begin = item.getAbsolutePath().indexOf("com");int end = item.getAbsolutePath().indexOf('.');String className = item.getAbsolutePath().substring(begin, end).replace('\\', '.');try {Class<?> clazz = Class.forName(className);if (clazz.isAnnotationPresent(Component.class)) {beanClazzList.add(clazz);//收集后置处理器(意图是收集后置处理器而不是收集bean对象)if (BeanPostprocessor.class.isAssignableFrom(clazz)) {beanPostprocessorList.add((BeanPostprocessor) clazz.getDeclaredConstructor().newInstance());}}} catch (Exception e) {throw new RuntimeException(e.getMessage());}}/*** 注册bean的BeanDefinition初始配置信息** @param beanClazzList     bean的字节类型列表* @param beanDefinitionMap bean的BeanDefinition初始配置信息池子*/private void initBeanDefinition(List<Class<?>> beanClazzList, Map<String, BeanDefinition> beanDefinitionMap) {if (null != beanClazzList && !beanClazzList.isEmpty()) {for (Class<?> clazz : beanClazzList) {BeanDefinition beanDefinition = new BeanDefinition();Component component = clazz.getAnnotation(Component.class);Scope scope = clazz.getAnnotation(Scope.class);Lazy lazy = clazz.getAnnotation(Lazy.class);beanDefinition.setBeanClazz(clazz);beanDefinition.setLazy(null != lazy);beanDefinition.setScope(null != scope ? scope.value() : "prototype");String beanName = component.name();if (beanName.isEmpty()) {beanName = clazz.getSimpleName();}beanDefinitionMap.put(beanName, beanDefinition);}}}/*** 实例化单例bean** @param beanDefinitionMap bean定义信息* @param singletonBeanMap  单例bean池子*/private void instanceSingletonBeans(Map<String, BeanDefinition> beanDefinitionMap, Map<String, Object> singletonBeanMap) {if (null != beanDefinitionMap && !beanDefinitionMap.isEmpty()) {for (Class<?> clazz : beanDefinitionMap.values().stream().map(BeanDefinition::getBeanClazz).toList()) {if (clazz.isAnnotationPresent(Scope.class) && "prototype".equals(clazz.getAnnotation(Scope.class).value())) {continue;}if (!clazz.isAnnotationPresent(Lazy.class)) {//实例化beantry {Component component = clazz.getAnnotation(Component.class);String beanName = component.name();if (null == beanName || beanName.isEmpty()) {beanName = clazz.getSimpleName();}//1、实例化beanObject newInstance = clazz.getDeclaredConstructor().newInstance();//2、属性填充attributeAutoWiredPadding(clazz, newInstance);//3、aware能力透传awareBeanInstancePadding(newInstance);//4、初始化//4.1、后置处理器 初始化前执行for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {newInstance = beanPostprocessor.beforeInitialization(newInstance, beanName);}//4.2、初始化bean执行//检查是否实现初始化Bean的接口initializeBeanInstancePadding(newInstance);//检查是否配置过init方法initBeanMethodInstancePadding(newInstance);//4.3、后置处理器能力 初始化后执行for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {newInstance = beanPostprocessor.afterInitialization(newInstance, beanName);}singletonBeanMap.put(beanName, newInstance);} catch (InvocationTargetException | NoSuchMethodException | InstantiationException |IllegalAccessException e) {throw new RuntimeException(e.getMessage());}}}}}/*** bean的属性填充** @param beanClazz   bean的字节类型* @param newInstance 实例化的bean*/private void attributeAutoWiredPadding(Class<?> beanClazz, Object newInstance) {if (null != beanClazz) {Field[] fields = beanClazz.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(AutoWired.class)) {field.setAccessible(true);Class<?> declaringClass = field.getType();AutoWired autoWired = field.getAnnotation(AutoWired.class);String name = autoWired.value();if (null == name || name.isEmpty()) {name = declaringClass.getSimpleName();}Object fieldBean = singletonBeanMap.get(name);if (null == fieldBean) {List<Class<?>> beanClazzList = new LinkedList<>();beanClazzList.add(declaringClass);initBeanDefinition(beanClazzList, beanDefinitionMap);Map<String, BeanDefinition> definitionMap = new HashMap<>();definitionMap.put(name, beanDefinitionMap.get(name));instanceSingletonBeans(definitionMap, singletonBeanMap);try {field.set(newInstance, singletonBeanMap.get(name));} catch (IllegalAccessException e) {throw new RuntimeException(e.getMessage());}} else {try {field.set(newInstance, fieldBean);} catch (IllegalAccessException e) {throw new RuntimeException(e.getMessage());}}}}}}/*** bean的Aware接口的实现类填充** @param bean bean实例对象*/private void awareBeanInstancePadding(Object bean) {if (null != bean) {if (bean instanceof Aware) {if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this);}if (bean instanceof BeanNameAware) {((BeanNameAware) bean).setBeanName();}}}}/*** bean的初始化方法填充** @param bean 实例化的bean*/private void initializeBeanInstancePadding(Object bean) {if (null != bean) {if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}}}/*** bean的初始化方法填充** @param newInstance 实例化的bean*/private void initBeanMethodInstancePadding(Object newInstance) {if (null != newInstance) {Method[] methods = newInstance.getClass().getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(InitBeanMethod.class)) {method.setAccessible(true);try {method.invoke(newInstance);} catch (IllegalAccessException | InvocationTargetException e) {throw new RuntimeException(e.getMessage());}}}}}private void sortBeanInstanceClazzList() {}
}

这里我们着重描述一下关于bean加载原理的这一块代码:

private void instanceSingletonBeans(Map<String, BeanDefinition> beanDefinitionMap, Map<String, Object> singletonBeanMap) {if (null != beanDefinitionMap && !beanDefinitionMap.isEmpty()) {for (Class<?> clazz : beanDefinitionMap.values().stream().map(BeanDefinition::getBeanClazz).toList()) {if (clazz.isAnnotationPresent(Scope.class) && "prototype".equals(clazz.getAnnotation(Scope.class).value())) {continue;}if (!clazz.isAnnotationPresent(Lazy.class)) {//实例化beantry {Component component = clazz.getAnnotation(Component.class);String beanName = component.name();if (null == beanName || beanName.isEmpty()) {beanName = clazz.getSimpleName();}//1、实例化beanObject newInstance = clazz.getDeclaredConstructor().newInstance();//2、属性填充attributeAutoWiredPadding(clazz, newInstance);//3、aware能力透传awareBeanInstancePadding(newInstance);//4、初始化//4.1、后置处理器 初始化前执行for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {newInstance = beanPostprocessor.beforeInitialization(newInstance, beanName);}//4.2、初始化bean执行//检查是否实现初始化Bean的接口initializeBeanInstancePadding(newInstance);//检查是否配置过init方法initBeanMethodInstancePadding(newInstance);//4.3、后置处理器能力 初始化后执行for (BeanPostprocessor beanPostprocessor : beanPostprocessorList) {newInstance = beanPostprocessor.afterInitialization(newInstance, beanName);}singletonBeanMap.put(beanName, newInstance);} catch (InvocationTargetException | NoSuchMethodException | InstantiationException |IllegalAccessException e) {throw new RuntimeException(e.getMessage());}}}}}

到这里我们已经了解在获取待加载bean的字节码列表之后,我们需要将bean的配置信息存储到我们的beanDefinitionMap中,再根据beanDefinitionMap将其中的单例bean信息加载成一个个bean放入单例bean map中,这里的存储key统一都是beanName。

看以上代码我们不难分析出通过bean配置信息加载bean的过程中,一个bean需要经过6步周期性工作才会被放入容器中给我们使用。以下是图示:

http://www.khdw.cn/news/7522.html

相关文章:

  • 校园兼职信息发布网站开发论文自动优化句子的软件
  • 网站开发要什么基础现在做网络推广好做吗
  • 内江住房和城乡建设厅网站发布软文的平台有哪些
  • 厦门模板建站平台2021年网络十大关键词
  • 天津做网站好的公司有哪些网站编辑怎么做
  • 官方网站下载微信最新版跨境电商
  • 行业门户型网站制作小璇seo优化网站
  • 辽宁建筑信息网查询上海优化公司有哪些
  • 网站服务器要多少钱网络推广接单平台
  • 深圳网站建设定制游戏挂机赚钱一小时20
  • 企业官网网站建设报价微信广告朋友圈投放
  • 长沙找人做企业网站文案怎么把产品快速宣传并推广
  • 做网站的时候表格怎么去掉苏州优化收费
  • 中山营销网站建设联系方式长春网络优化哪个公司在做
  • 手机门户网站网站seo怎么做
  • 益保网做推广网站吗?关键词全网搜索工具
  • 为什么要用模板建站网址查询地址查询
  • 全网营销网站建设特点内蒙古seo优化
  • wordpress相册分类名称厦门seo网站排名优化
  • 做加工都在哪个网站推广关注公众号推广2元一个
  • 建设部科研申报网站用着不好网页设计模板图片
  • 网站开发的公司排名百度知道首页登录入口
  • 成都 网站建设短链接生成器
  • 寄生虫网站怎么做青岛seo青岛黑八网络最强
  • 游戏网站制作竞价推广课程
  • 软文营销范文上海专业排名优化公司
  • 贵州省建设厅三类人员报名网站百度文库登录入口
  • wordpress站点地图样式百度知道下载安装
  • 微信的微网站模板网络营销模式有哪些
  • 自建虚拟主机网站源码网站优化哪个公司好