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

房产类网站制作商/腾讯新闻潍坊疫情

房产类网站制作商,腾讯新闻潍坊疫情,东莞做棋牌网站建设,电子商城平台网站建设前置知识 《【Spring专题】Spring底层核心原理解析》 思路整理 我们在上一节《【Spring专题】Spring底层核心原理解析》课里面有简单分析过一个Spring容器的一般流程,所以,本节课我们这里尝试写一下简易的Spring容器。 手写源码示例 一、手写前的准…

前置知识

《【Spring专题】Spring底层核心原理解析》

思路整理

我们在上一节《【Spring专题】Spring底层核心原理解析》课里面有简单分析过一个Spring容器的一般流程,所以,本节课我们这里尝试写一下简易的Spring容器。

手写源码示例

一、手写前的准备

1.1 注解

既然是需要手写Spring容器,那我们肯定需要自定义一个MyApplicationContext类,以及自定义注解@ComponentScan@Component@Autowired@Scope代码如下:

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

其中@Scope是为了验证多例Bean跟单例Bean的。当然,需要准备的接口不止于此,后面我会随着方法的完善,逐渐引出其余需要的接口或者注解。

1.2 测试Bean

另外就是测试用的Bean,代码如下:

/*** @author zhangshen* @tag 【手写】* @Date 2023/8/7 19:57* @slogan 编码即学习**/
@Component("userService")
public class MyUserService implements InitializingBean, BeanPostProcessor {private int afterPropertiesSet = 0;private int postProcessBeforeInitialization = 0;private int postProcessAfterInitialization = 0;private static int count = 0;public void showYourFace() {System.out.println("手写的spring。。。。。 test一下");System.out.println("初始化是第几步,答案是:" + afterPropertiesSet);System.out.println("初始化前是第几步,答案是:" + postProcessBeforeInitialization);System.out.println("初始化后是第几步,答案是:" + postProcessAfterInitialization);}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("这里是初始化,是在aware回调之后");count++;afterPropertiesSet = count;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {System.out.println("这里是初始化前");count++;postProcessBeforeInitialization = count;return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {System.out.println("这里是初始化后");count++;postProcessAfterInitialization = count;return bean;}
}

1.3 调用实例

调用实例:

/*** 手写Spring容器测试** @author zhangshen* @tag 【手写】* @Date 2023/8/7 19:55* @slogan 编码即学习**/
public class MyApplicationContextTest {public static void main(String[] args) {MyApplicationContext context = new MyApplicationContext(MyAppConfig.class);MyUserService userService = (MyUserService) context.getBean("userService");userService.showYourFace();}
}

二、构造方法(构建基本流程)

在上节课中,我们说过,在容器启动的构造方法里面,大致的流程可以分为三步:
在这里插入图片描述
所以,我们代码起手式,是新建一个MyApplicationContext类,然后一个默认的无参构造方法,代码如下:

public class MyApplicationContext {public MyApplicationContext(Class clazz) {// 步骤一:扫描scan(clazz);// 步骤二:IOCioc();// 步骤三:AOPaop();}
}

但事实上,我们前面说过,虽然看似大过程是3步,不过AOP是需要发生在IOC内部的(Bean放入单例池之前),所以,后面,我们会把这个AOP的过程写在IOC里面,如下:

public class MyApplicationContext {public MyApplicationContext(Class clazz) {// 步骤一:扫描scan(clazz);// 步骤二:IOCioc();}private void doIOC() {// 步骤三:AOPaop();}
}

三、实现scan()方法

先上扫描的简易流程图:
在这里插入图片描述
接下来,我们只需要在doScan()方法里面实现这些步骤就好了。

    /*** 扫描方法** @param clazz 配置类*/private void scan(Class clazz) {// 步骤一:获取扫描路径String basePackage = doGetScanPackage(clazz);if (StrUtil.isEmpty(basePackage)) {return;}// 步骤二、三:从电脑磁盘中加载文件,并且扫描doLoadClassFromDiskAndScan(basePackage, clazz);}

3.1 doGetScanPackage():获取扫描路径

    /*** 获取包扫描路径** @param clazz 配置类* @return 扫描路径*/private String doGetScanPackage(Class clazz) {if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation = (ComponentScan) clazz.getAnnotation(ComponentScan.class);return componentScanAnnotation.value();}return null;}

3.2 doLoadClassFromDiskAndScan():从电脑磁盘中加载文件,并且扫描

    /*** 从磁盘中加载class,并且扫描** @param basePackage 扫描路径*/private void doLoadClassFromDiskAndScan(String basePackage) {// 将Java包名转换为电脑路径名basePackage = basePackage.replace(".", "/");// 加载ClassLoader classLoader = MyApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(basePackage);File file = new File(resource.getFile());if (file.isDirectory()) {for (File listFile : file.listFiles()) {// 获取全限定名String fullyQualifiedClassName = transferToFullyQualifiedClassName(listFile);try {Class<?> beanClass = classLoader.loadClass(fullyQualifiedClassName);if (!beanClass.isAnnotationPresent(Component.class)) {continue;}// 记录Bean信息doRecordBeanInfo(beanClass);} catch (ClassNotFoundException e) {e.printStackTrace();}}}}

方法解读:

  1. 在上一步,我们已经获取到了扫描路径,但是扫描路径是我们Java包名,现在需要从磁盘重读取的话,定然是需要转换成电脑磁盘重的路径的。
  2. 大家可能对classLoader这一块比较难理解,这是属于JVM那一块的。不过这么说,我们自己写的业务代码,除非特别指定,不然都是使用的同一个类加载器
  3. 这里还有2个关键方法调用,一个是transferToFullyQualifiedClassName(),是将文件名转换为类加载器能识别的类全限定名;另一个是doRecordBeanInfo()方法,记录Bean定义信息的。代码会在后面

3.3 transferToFullyQualifiedClassName() :获取全限定名

    /*** 将电脑磁盘上的文件,转换为AppClassLoader能识别的类全限定名(包 + 类名)** <p>* 由于JVM的类加载器有三种,默认加载用户自定义class文件的,其实是ApplicationClassLoader* 该类加载器所能识别的,其实是包名* </p>** @param listFile 电脑磁盘上的文件*/private String transferToFullyQualifiedClassName(File listFile) {String absolutePath = listFile.getAbsolutePath();absolutePath = absolutePath.substring(absolutePath.indexOf("org"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");return absolutePath;}

*3.4 doRecordBeanInfo() :记录Bean信息

    /*** 过滤,并且记录Bean信息*/private void doRecordBeanInfo(Class<?> beanClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 获取bean的名字String beanName = doGetBeanName(beanClass);// 记录BeanPostProcessorif (BeanPostProcessor.class.isAssignableFrom(beanClass)) {BeanPostProcessor instance = (BeanPostProcessor) beanClass.getConstructor().newInstance();beanPostProcessorList.add(instance);}BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(beanClass);if (beanClass.isAnnotationPresent(Scope.class)) {Scope scopeAnnotation = beanClass.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);}// 记录BeanDefinitionbeanDefinitionMap.put(beanName, beanDefinition);}

这里,引入了一个新的类BeanDefinition类,源码如下:

/*** 手写Bean定义** @author zhanghuitong* @tag 【手写】 【Bean定义】 【图纸】* @Date 2023/8/8 10:54* @slogan 编码即学习**/
@Getter
@Setter
public class BeanDefinition {private static final String SINGLETON = "singleton";/*** Bean类型*/private Class type;/*** 作用域* 是原型,还是单例*/private String scope = SINGLETON;
}

也许大家会很奇怪,为什么需要引入这么一个东西呢?我都已经扫描好了,我直接根据类信息生成不就行了吗?如果你也有这个想法,那么我反问你一个问题:如果我后面想修改怎么办?还别说,真的有可能修改!Spring提供了那么多拓展机制,其中就有对类信息修改的拓展点(至于具体应用场景,我还没彻底学清楚,我后面学到了我会回来更新的)。但是为了方便大家理解这个玩意的存在,我举个通俗的例子,如下:

BeanDefinition的存在更像是一份家具定制的图纸,Bean是具体的某个家具。而Spring里面,在后面我们会学到的ApplicationContext,则是生产家具的厂家。这样类比的话,你应该能想明白,为什么需要BeanDefinition了吧。
总结一句话:ApplicationContext厂家根据BeanDefinition图纸生成具体的某个家具Bean。(PS:ApplicationContext 包含 BeanFactory,都是Bean工厂)

四、实现ioc()方法

先看看ioc简易流程图:
在这里插入图片描述
(PS:下面的手写源码,没有实现【推断构造方法】的逻辑)

    /*** 进行IOC过程*/private void ioc() {// 循环遍历beanDefinitionMapSet<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMap.entrySet();for (Map.Entry<String, BeanDefinition> entry : entries) {String beanName = entry.getKey();BeanDefinition beanDefinition = entry.getValue();if (beanDefinition.getScope().equals(BeanDefinition.SINGLETON)) {// 创建BeanObject bean = createBean(beanName, beanDefinition);// AOPaop();singletonPool.put(beanName, bean);}}}

方法解读:

  1. 在这里,就是遍历我们上一步获得的beanDefinitionMap图纸Map。
  2. 接着就开始根据图纸生成Bean了(createBean()方法)
  3. 接着看看是否需要AOP了(在放入单例池之前)
  4. 最后就是放入单例池里面了

*4.1 createBean() :创建Bean

    /*** 创建bean** @param beanName       bean名称* @param beanDefinition 对应的bean定义*/private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();Object instance = null;try {// 实例化。本应该实现推断构造方法,但是这里从简使用默认的instance = clazz.getConstructor().newInstance();// 依赖注入doDependecyInjection(clazz, instance);// 各种Aware回调doAwareCallBack(beanName, instance);// 初始化前instance = doInitBefore(beanName, instance);// 初始化doInit(instance);// 初始化后instance = doInitAfter(beanName, instance);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}// 返回return instance;}

方法解读:
虽然上面的代码是简单的实现,但也稍微能窥见一点IOC的生命周期了,大家需要好好记住。

实例化 -> 依赖注入 -> 各种Aware回调 -> 初始化前 -> 初始化 -> 初始化后

因为是简单实现,所以上面这个流程不全,应该是缺了点其他拓展点的实现的。但是局部上的顺序是没啥问题,而且这不是我吹的,而是在Spirng接口上注释的。

4.2 doDependecyInjection() :依赖注入

    /*** 依赖注入** @param clazz    需要被注入的对象的类信息* @param instance 需要被注入的对象*/private void doDependecyInjection(Class clazz, Object instance) throws IllegalAccessException {Field[] declaredFields = clazz.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.isAnnotationPresent(Autowired.class)) {declaredField.setAccessible(true);declaredField.set(instance, getBean(declaredField.getName()));}}}

4.3 doAwareCallBack() :各种Aware回调

    /*** 实现各种Aware的回调处理** @param beanName bean名称* @param instance bean对象*/private void doAwareCallBack(String beanName, Object instance) {if (instance instanceof BeanNameAware) {((BeanNameAware) instance).setBeanName(beanName);}}

方法解读:
在这里,引入了一个新的接口BeanNameAware,它实现自Aware接口。其实除了这个我们用来展示的BeanNameAware还有很多其他的XxxAware,主要的目的是在实例化之后,让我们获得感知一些Spring组件等等能力。下面是BeanNameAware接口的代码示例(其中注释源于Spring源码):

/*** 手写Spring的BeanNameAware接口** @author zhangshen* @tag 【手写】* @Date 2023/8/8 19:04* @slogan 编码即学习**/
public interface BeanNameAware {/*** 在创建该bean的bean工厂中设置该bean的名称。* 在填充普通bean属性之后,但在初始化回调(如InitializingBean.afterPropertiesSet())或自定义初始化方法之前调用。* 参数:* 名称-工厂中bean的名称。请注意,这个名称是工厂中使用的实际bean名称,它可能与最初指定的名称不同:特别是对于内部bean名称,实际bean名称可能通过附加“#…”后缀来使其唯一。如果需要,使用BeanFactoryUtils.originalBeanName(String)方法提取原始bean名称(不带后缀)。*/void setBeanName(String name);
}

4.4 doInitBefore() :初始化前

    /*** 实施初始化之前** @param beanName bean名称* @param instance bean对象*/private Object doInitBefore(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);}return instance;}

方法解读:

  1. 初始化前这是属于SpringIOC提供的一个拓展点,需要一个非常非常非常重要的接口BeanPostProcessor ,它的定义如下:(其中注释源于Spring源码)
/*** 手写Spring的ABeanPostProcessor接口** @author zhangshen* @tag 【手写】* @Date 2023/8/8 19:04* @slogan 编码即学习**/
public interface BeanPostProcessor {/*** 在任何bean初始化回调(如InitializingBean的afterPropertiesSet或自定义初始化方法)之前,将此BeanPostProcessor应用于给定的新bean实例。这个bean已经被属性值填充了。返回的bean实例可能是原始bean实例的包装器。* 默认实现按原样返回给定的bean。* 参数:* Bean——新的Bean实例* beanName—bean的名称* 返回:* 要使用的bean实例,无论是原始的还是包装的;如果为空,则不会调用后续的BeanPostProcessors* 抛出:* BeansException -在错误的情况下*/default Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}/*** 在任何bean初始化回调(如InitializingBean的afterPropertiesSet或自定义init-method)之后,将此BeanPostProcessor应用于给定的新bean实例。这个bean已经被属性值填充了。返回的bean实例可能是原始bean实例的包装器。* 对于FactoryBean,将为FactoryBean实例和由FactoryBean创建的对象调用这个回调(从Spring 2.0开始)。后处理器可以通过相应的FactoryBean instanceof检查来决定是应用于FactoryBean还是已创建的对象,或者两者都应用。* 这个回调也将在由InstantiationAwareBeanPostProcessor触发的短路之后被调用。postProcessBeforeInstantiation方法,与所有其他BeanPostProcessor回调相反。* 默认实现按原样返回给定的bean。* 参数:* Bean——新的Bean实例* beanName—bean的名称* 返回:* 要使用的bean实例,无论是原始的还是包装的;如果为空,则不会调用后续的BeanPostProcessors* 抛出:* BeansException -在错误的情况下* 参见:* org.springframework.beans.factory.InitializingBean。afterPropertiesSet, org.springframework.beans.factory.FactoryBean* 以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景*/default Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
}

从上面的注释希望大家在后面真的阅读源码的时候能知道,BeanPostProcessor这个拓展点就是作用在初始化前后的

4.5 doInit() :初始化

    /*** 实施初始化方法** @param instance bean对象*/private void doInit(Object instance) throws Exception {if (instance instanceof InitializingBean) {((InitializingBean) instance).afterPropertiesSet();}}

方法解读:

  1. 初始化方法,在这里又是需要一个新的接口,InitializingBean,在工作使用频率挺高的。当然也可以通过自定义初始化方法,不一定就是这个。接口代码如下:(其中注释源于Spring源码)
/*** 手写Spring的ABeanPostProcessor接口** @author zhangshen* @tag 【手写】* @Date 2023/8/8 19:04* @slogan 编码即学习**/
public interface InitializingBean {/*** 在设置了所有bean属性并满足BeanFactoryAware、ApplicationContextAware等要求后,由包含bean的BeanFactory调用。* 此方法允许bean实例在设置了所有bean属性后对其总体配置和最终初始化执行验证。* 抛出:* 异常-在配置错误的情况下(例如设置基本属性失败),或者由于任何其他原因导致初始化失败*/void afterPropertiesSet() throws Exception;
}

4.6 doInitAfter() :初始化后

    /*** 实施初始化之后** @param beanName bean名称* @param instance bean对象*/private Object doInitAfter(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);}return instance;}

方法解读:

  1. 初始化后,跟初始化前一样,是属于SpringIOC提供的一个拓展点。并且使用的也是BeanPostProcessor接口

五、实现aop()方法

这个就不实现了,大家知道有这个东西就好

六、MyApplicationContext完整源码

package org.tuling.spring.handwriten;import cn.hutool.core.util.StrUtil;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.*;/*** 手写一个简易的Spring容器** @author zhangshen* @tag 【Spring】 【手写】* @Date 2023/8/7 19:37* @slogan 编码即学习**/
public class MyApplicationContext {private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();private Map<String, Object> singletonPool = new HashMap<>();private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();public MyApplicationContext(Class clazz) {// 扫描scan(clazz);// IOCioc();}/*** 进行IOC过程*/private void ioc() {// 循环遍历beanDefinitionMapSet<Map.Entry<String, BeanDefinition>> entries = beanDefinitionMap.entrySet();for (Map.Entry<String, BeanDefinition> entry : entries) {String beanName = entry.getKey();BeanDefinition beanDefinition = entry.getValue();if (beanDefinition.getScope().equals(BeanDefinition.SINGLETON)) {// 创建BeanObject bean = createBean(beanName, beanDefinition);// AOPaop();singletonPool.put(beanName, bean);}}}/*** 创建bean** @param beanName       bean名称* @param beanDefinition 对应的bean定义*/private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();Object instance = null;try {// 实例化。本应该实现推断构造方法,但是这里从简使用默认的instance = clazz.getConstructor().newInstance();// 依赖注入doDependecyInjection(clazz, instance);// 各种Aware回调doAwareCallBack(beanName, instance);// 初始化前instance = doInitBefore(beanName, instance);// 初始化doInit(instance);// 初始化后instance = doInitAfter(beanName, instance);} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}// 返回return instance;}/*** 实施初始化之后** @param beanName bean名称* @param instance bean对象*/private Object doInitAfter(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);}return instance;}/*** 实施初始化方法** @param instance bean对象*/private void doInit(Object instance) throws Exception {if (instance instanceof InitializingBean) {((InitializingBean) instance).afterPropertiesSet();}}/*** 实施初始化之前** @param beanName bean名称* @param instance bean对象*/private Object doInitBefore(String beanName, Object instance) {for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);}return instance;}/*** 实现各种Aware的回调处理** @param beanName bean名称* @param instance bean对象*/private void doAwareCallBack(String beanName, Object instance) {if (instance instanceof BeanNameAware) {((BeanNameAware) instance).setBeanName(beanName);}}/*** 依赖注入** @param clazz    需要被注入的对象的类信息* @param instance 需要被注入的对象*/private void doDependecyInjection(Class clazz, Object instance) throws IllegalAccessException {Field[] declaredFields = clazz.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.isAnnotationPresent(Autowired.class)) {declaredField.setAccessible(true);declaredField.set(instance, getBean(declaredField.getName()));}}}/*** 进行AOP过程*/private void aop() {}/*** 根据beanName获取Bean** @param beanName bean名称* @return bean对象*/public Object getBean(String beanName) {if (StrUtil.isEmpty(beanName)) {return null;}// 不存在beanDefinition,则肯定不是beanif (!beanDefinitionMap.containsKey(beanName)) {throw new NoSuchBeanDefinitionException("没有对应的bean定义");}BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);// 创建对象if (beanDefinition.getScope().equals(BeanDefinition.SINGLETON)) {// 获取单例Object singletonBean = singletonPool.get(beanName);if (singletonBean == null) {singletonBean = createBean(beanName, beanDefinition);singletonPool.put(beanName, singletonBean);}return singletonBean;} else {// 获取多例Object prototype = createBean(beanName, beanDefinition);return prototype;}}/*** 扫描方法** @param clazz 配置类*/private void scan(Class clazz) {// 获取扫描路径String basePackage = doGetScanPackage(clazz);if (StrUtil.isEmpty(basePackage)) {return;}// 从电脑磁盘中加载文件,并且扫描doLoadClassFromDiskAndScan(basePackage);}/*** 从磁盘中加载class,并且扫描** @param basePackage 扫描路径*/private void doLoadClassFromDiskAndScan(String basePackage) {// 将Java包名转换为电脑路径名basePackage = basePackage.replace(".", "/");// 加载ClassLoader classLoader = MyApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(basePackage);File file = new File(resource.getFile());if (file.isDirectory()) {for (File listFile : file.listFiles()) {// 获取全限定名String fullyQualifiedClassName = transferToFullyQualifiedClassName(listFile);try {Class<?> beanClass = classLoader.loadClass(fullyQualifiedClassName);if (!beanClass.isAnnotationPresent(Component.class)) {continue;}// 记录Bean信息try {doRecordBeanInfo(beanClass);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}} catch (ClassNotFoundException e) {e.printStackTrace();}}}}/*** 过滤,并且记录Bean信息*/private void doRecordBeanInfo(Class<?> beanClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// 获取bean的名字String beanName = doGetBeanName(beanClass);// 记录BeanPostProcessorif (BeanPostProcessor.class.isAssignableFrom(beanClass)) {BeanPostProcessor instance = (BeanPostProcessor) beanClass.getConstructor().newInstance();beanPostProcessorList.add(instance);}BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(beanClass);if (beanClass.isAnnotationPresent(Scope.class)) {Scope scopeAnnotation = beanClass.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);}// 记录BeanDefinitionbeanDefinitionMap.put(beanName, beanDefinition);}/*** 获取当前Bean的名字** @param aClass 要加载的类* @return bean的名字*/private String doGetBeanName(Class<?> aClass) {Component componentAnnotaion = aClass.getAnnotation(Component.class);String beanName = componentAnnotaion.value();if (StrUtil.isEmpty(beanName)) {beanName = Introspector.decapitalize(aClass.getSimpleName());}return beanName;}/*** 将电脑磁盘上的文件,转换为AppClassLoader能识别的类全限定名(包 + 类名)** <p>* 由于JVM的类加载器有三种,默认加载用户自定义class文件的,其实是ApplicationClassLoader* 该类加载器所能识别的,其实是包名* </p>** @param listFile 电脑磁盘上的文件*/private String transferToFullyQualifiedClassName(File listFile) {String absolutePath = listFile.getAbsolutePath();absolutePath = absolutePath.substring(absolutePath.indexOf("org"), absolutePath.indexOf(".class"));absolutePath = absolutePath.replace("\\", ".");return absolutePath;}/*** 获取包扫描路径** @param clazz 配置类* @return 扫描路径*/private String doGetScanPackage(Class clazz) {if (clazz.isAnnotationPresent(ComponentScan.class)) {ComponentScan componentScanAnnotation = (ComponentScan) clazz.getAnnotation(ComponentScan.class);return componentScanAnnotation.value();}return null;}
}

相关文章:

【Spring专题】手写简易Spring容器过程分析

前置知识 《【Spring专题】Spring底层核心原理解析》 思路整理 我们在上一节《【Spring专题】Spring底层核心原理解析》课里面有简单分析过一个Spring容器的一般流程&#xff0c;所以&#xff0c;本节课我们这里尝试写一下简易的Spring容器。 手写源码示例 一、手写前的准…...

fastadmin自定义键值组件Fieldlist

需求场景&#xff1a; 后台设置前端的固定话费充值金额。编辑时要求能够增删改&#xff0c;给到前端的数据&#xff0c;是要根据金额正序排列&#xff0c;用fastadmin的键值组件(Fieldlist)&#xff0c;使用Art-Template模板语法自定义模板。 最终效果如下图所示&#xff1a; …...

yolov2检测网数据集标注_labelme使用_json2txt格式转换

yolov2检测网数据集标注_labelme使用_json2txt格式转换 一、安装Anaconda二、创建labelme虚拟环境三、使用labelme标注健康非健康猫狗数据3.1 打开数据集所在文件夹3.2 进行标注数据集3.3 json2txt3.4 按文件目录和训练测试数据集重分配 四、数据喂给服务器网络参考链接 一、安…...

C/C++面试总结

一、关键字static、const、extern、volatile作用 1、const 1.修饰常量 用const修饰的变量是不可变的&#xff0c;修饰后的变量只能使用&#xff0c;不能修改。 2.修饰指针 如果const位于*的左侧&#xff0c;eg&#xff1a;const int* a&#xff0c;则const就是用来修饰指针…...

Python爬虫的Selenium(学习于b站尚硅谷)

目录 一、Selenium  1.为什么要学习Selenium  &#xff08;1&#xff09;什么是Selenium  &#xff08;2&#xff09;为什么使用selenium?  &#xff08;3&#xff09;代码演示 2. selenium的基本使用  &#xff08;1&#xff09;如何安装selenium  &#xff08;2…...

springboot 对接 minio 分布式文件系统

1. minio介绍 Minio 是一个基于Go语言的对象存储服务。它实现了大部分亚马逊S3云存储服务接口&#xff0c;可以看做是是S3的开源版本&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等&#xff0c;而一个对象…...

前端小练习:案例4.3D图片旋转展示(旋转木马)

一.效果预览图 二.实现思路 1.实现旋转木马效果的第一步是先准备好自己需要的图片&#xff0c;创建html文件 2.旋转木马的实现&#xff0c;关键点在3D形变和关键帧动画。 3.步骤&#xff0c;定义一个div使其居中&#xff0c;&#xff0c;把图片放进div盒子里&#xff0c;因为图…...

Linux这17个操作技巧是每个运维工程师应知必会的吧?

今天跟大家分享17个linux运维中常用的操作技巧&#xff01;掌握好这些技巧&#xff0c;或许某一天能够让老板给你涨工资&#xff01; 1、查找当前目录下所有以.tar结尾的文件然后移动到指定目录&#xff1a; find . -name “*.tar” -exec mv {}./backup/ ; ❝ 注解&#xff1…...

音视频基础:分辨率、码率、帧率之间关系

基础 人类视觉系统 分辨率 像素&#xff1a; 是指由图像的小方格组成的&#xff0c;这些小方块都有一个明确的位置和被分配的色彩数值&#xff0c;小方格颜色和位置就决定该图像所呈现出来的样子&#xff1b;可以将像素视为整个图像中不可分割的单位或者是元素&#xff1b;像素…...

Java基础八 - HTTP相关/Cookie/Session/网络攻击

一、 反射/序列化/拷贝 1. 反射 //反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力 //在Yaml数据驱动自动化框架比较适用&#xff0c;能获取到当前的类名及方法名 import java.lang.reflect.*;public class ReflectionExample {public static void main(Str…...

【车道线】TwinLiteNet 复现过程全纪录

码字不易&#xff0c;喜欢的请点赞收藏&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 论文全文翻译&#xff1a;【freespace】TwinLiteNet: An Efficient and Lightweight Model for Driveable Area and Lane Segmentation_莫克_Cheney的博客-CSDN博客 目录…...

七牛云获取qn(url、bucket、access-key、secret-key)

1.注册账号 2.access-key和secret-key&#xff1a; 点击“密钥管理” 复制AK和SK即可 域名&#xff1a; bucket&#xff1a; 这个就是对象存储空间名字 先新建一个空间&#xff08;没买需要先购买&#xff09;&#xff0c;步骤如下&#xff1a; 填写存储空间名字&#xff0…...

定时任务实现 - Cron表达式知识

Cron表达式 cron表达式是一个字符串&#xff0c;由6到7个字段组成&#xff0c;用空格分隔。其中前6个字段是必须的&#xff0c;最后一个是可选的。每个字段的含义为&#xff1a;秒 分 时 日 月 周 年 字符解释: 枚举&#xff1a;, (cron“7,9,23****?”)&#xff1a;任意时刻…...

【java】抽象

java抽象 抽象类抽象方法抽象类和抽象方法 抽象类 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就…...

Qt应用开发(基础篇)——时间微调输入框 QDateTimeEdit、QDateEdit、QTimeEdit

一、前言 QAbstractSpinBox是全部微调输入框的父类&#xff0c;这是一种允许用户通过点击上下箭头按钮或输入数字来调整数值的图形用户界面控件&#xff0c;父类提供了当前值text、对齐方式align、只读readOnly等通用属性和方法。在上一篇数值微调输入框中有详细介绍。 QDateTi…...

日撸代码300行:第63天(集成学习之 AdaBoosting-1)

代码来自闵老师”日撸 Java 三百行&#xff08;61-70天&#xff09; 日撸 Java 三百行&#xff08;61-70天&#xff0c;决策树与集成学习&#xff09;_闵帆的博客-CSDN博客 学习过程中理解算法参考了&#xff1a;&#xff08;十三&#xff09;通俗易懂理解——Adaboost算法原…...

抽象父类获取子类的泛型 或接口泛型

jie通过getClass().getGenericSuperclass()或者子类的泛型 getClass().getGenericInterfaces();获取多个接口的泛型 GenericTypeResolver.resolveTypeArgument(GenericityService.class, GenericitySuper.class) 抽象父类 public abstract class GenericitySuper<T> …...

题目:2341.数组能形成多少数对

​​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;2341. 数组能形成多少数对 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 使用哈希表对数组中元素及其出现次数计数后对其进行统计即可。 解题代码&#xff1a; class Solution {public …...

NB-IOT 和蜂窝通信(2/3/4/5G)的区别和特点是什么?

NB-IOT 和蜂窝通信(2/3/4/5G)的区别和特点是什么? 参考链接:https://www.sohu.com/a/221664826_472880 NB IOT是窄带物联网技术,主要解决的是低速率数据传输,可使用GSM900或DCS1800频段,在频段使用上比较灵活,可以和GSM,UMTS或LTE共存,具备优异的MCL(最小耦合损耗…...

vue3 动态导入src/page目录下的所有子文件,并自动注册所有页面组件

main.js添加一下代码&#xff1a; const importAll (modules) > {Object.keys(modules).forEach((key) > {const component key.replace(/src/, /).replace(.vue, );const componentName key.split(/).slice(-2, -1)[0] -page;app.component(componentName, modules…...

python优雅地爬虫

申明&#xff1a;仅用作学习用途&#xff0c;不提供任何的商业价值。 背景 我需要获得新闻&#xff0c;然后tts&#xff0c;在每天上班的路上可以听一下。具体的方案后期我也会做一次分享。先看我喜欢的万能的老路&#xff1a;获得html内容-> python的工具库解析&#xff0…...

mysql8查看执行sql历史日志、慢sql历史日志,配置开启sql历史日志general_log、慢sql历史日志slow_query_log

0.本博客sql总结 -- 1.查看参数 -- 1.1.sql日志和慢sql日志输出方式(TABLE/FILE)。global参数 SHOW GLOBAL VARIABLES LIKE log_output; -- 1.2.sql日志开关。global参数 SHOW GLOBAL VARIABLES LIKE general_log%; -- 1.3.慢sql日志开关。global参数 SHOW GLOBAL VARIABLE…...

vscode关闭绑定元素“xxx”隐式具有“any”类型这类错误

在ts的项目里面&#xff0c;真的经常看到any类型的报错&#xff0c;真的很烦的 所以为了眼不见心不乱&#xff0c;我决定消除这个错误提示 在tsconfig.json里面配置 "noImplicitAny": false 就可以了 {"compilerOptions": {"target": "E…...

View绘制流程-Window创建

前言&#xff1a; View绘制流程中&#xff0c;主要流程是这样的&#xff1a; 1.用户进入页面&#xff0c;首先创建和绑定Window&#xff1b; 2.首次创建以及后续vsync信号来临时&#xff0c;会请求执行刷新流程&#xff1b; 3.刷新流程完成后&#xff0c;会通知SurfaceFlin…...

Jenkins build包时虽然单元测试失败了,但是仍然可以成功build包(最终结束时build success)

1.尝试方案1&#xff1a; 尽管单元测试失败&#xff0c;Jenkins Maven仍然可以获得成功-Java 学习之路 将 -Dmaven.test.failure.ignorefalse 添加到 MAVEN_OPTS artifactoryMaven {goals "-U clean install -Dmaven.test.skipfalse -DallowSnapshotstrue -Dmaven.te…...

【vue3】基础知识点-setup语法糖

学习vue3&#xff0c;都会从基础知识点学起。了解setup函数&#xff0c;ref&#xff0c;recative&#xff0c;watch、comptued、pinia等如何使用 今天说vue3组合式api&#xff0c;setup函数 在学习过程中一开始接触到的是这样的&#xff0c;定义数据且都要通过return返回 <…...

idol!! 2023牛客暑期多校训练营6 C

登录—专业IT笔试面试备考平台_牛客网 题目大意&#xff1a;定义n!!等于与n的奇偶性相同的所有小于等于n的数的阶乘之和&#xff0c;问n!!的末尾有多少0 1<n<1e18 思路&#xff1a;因为末尾0的来源是2*5&#xff0c;而2的个数明显比5的个数多得多&#xff0c;所以末尾…...

深入理解Jdk5引入的Java泛型:类型安全与灵活性并存

深入理解Jdk5引入的Java泛型&#xff1a;类型安全与灵活性并存 ​ 在Java的中&#xff0c;有一个强大的工具&#xff0c;它可以让你在编写代码时既保持类型安全&#xff0c;又享受灵活性。**这个工具就是——泛型&#xff08;Generics&#xff09;。**本文将引导你深入了解Java…...

idea在控制台中输出文字显示乱码

VM options中加入下面这行 -Dfile.encodingutf-8...

hacksudo3 通关详解

环境配置 一开始桥接错网卡了 搞了半天 改回来就行了 信息收集 漏洞发现 扫个目录 大概看了一眼没什么有用的信息 然后对着login.php跑了一下弱口令 sqlmap 都没跑出来 那么利用点应该不在这 考虑到之前有过dirsearch字典太小扫不到东西的经历 换个gobuster扫一下 先看看g…...