首页 文章 精选 留言 我的

精选列表

搜索[水印魔术手],共7377篇文章
优秀的个人博客,低调大师

让 AI 拥有「手」和「眼」:我把 Claude 关进一个 PHP 容器后,Vibe Coding 才算真正闭环

接上集:阿杰用 php-vibe-coding-frame 解决了「AI 写代码的幻觉」,三行闭包一次跑通。可他很快发现,框架只是第一道坎。周五晚上十一点,阿杰盯着屏幕上的 500 错误发呆。这是他这周第三次了。上集他听老陈的建议,把项目从「全家桶」框架换成了 php-vibe-coding-frame——AI 写代码的幻觉问题确实压下去了。可新问题比想象中来得更狠:

优秀的个人博客,低调大师

《Spring 手撸专栏》第 7 章:所向披靡,实现应用上下文,自动识别、资源加载、扩展机制

持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 目录 一、前言 二、目标 三、设计 四、实现 1. 工程结构 2. 定义 BeanFactoryPostProcessor 3. 定义 BeanPostProcessor 4. 定义上下文接口 5. 应用上下文抽象类实现 6. 获取Bean工厂和加载资源 7. 上下文中对配置信息的加载 8. 应用上下文实现类(ClassPathXmlApplicationContext) 9. 在Bean创建时完成前置和后置处理 五、测试 1. 事先准备 2. 实现 BeanPostProcessor 和 BeanFactoryPostProcessor 3. 配置文件 4. 不用应用上下文 5. 使用应用上下文 六、总结 七、系列推荐 一、前言 你这代码,可不能写死了呀! 依照项目落地经验来看,我们在承接紧急的产品需求时候,通常会选择在原有同类项目中进行扩展,如果没有相关类型项目的储备,也可能会选择临时搭建出一个工程来实现产品的需求。但这个时候就会遇到非常现实的问题,选择完整的设计和开发就可能满足不了上线时间,临时拼凑式的完成需求又可能不具备上线后响应产品的临时调整。 上线后的调整有哪些呢?项目刚一上线,运营了还不到半天,老板发现自己的配置的活动好像金额配置的太小了,用户都不来,割不到韭菜呀。赶紧半夜联系产品,来来来,你给我这改改,那修修,把人均优惠1万元放大大的,把可能两字缩小放在后面。再把优惠的奖金池配置从10元调整11元,快快快,赶紧修改,你修改了咱们能赚1个亿!!! 好家伙,项目是临时开发堆出来的,没有后台系统、没有配置中心、没有模块拆分,老板一句句改改改,产品来传达催促,最后背锅的可就是研发了。你这不能写死,这优惠配置得抽出来,这文案也后台下发吧,这接口入参也写死了,再写一个新接口吧! 一顿操作猛如虎,研发搬砖修接口,运营折腾好几宿,最后PV150! 无论业务、产品、运营如何,但就研发自身来讲,尽可能的要不避免临时堆出一个服务来,尤其是在团队建设初期或者运营思路经常调整的情况下,更要注重设计细节和实现方案。哪怕去报风险延期,也不要让自己背上一个明知是烂坑还要接的活。 而本章节说到不把代码写死,就是因为我们需要继续在手写 Spring 框架中继续扩展新的功能,如一个Bean的定义和实例化的过程前后,是否可以满足我们进行自定义扩展,对Bean对象执行一些修改、增强、记录等操作呢?这个过程基本就是你在使用 Spring 容器框架时候做的一些中间件扩展开发。 二、目标 如果你在自己的实际工作中开发过基于 Spring 的技术组件,或者学习过关于 SpringBoot 中间件设计和开发 等内容。那么你一定会继承或者实现了 Spring 对外暴露的类或接口,在接口的实现中获取了 BeanFactory 以及 Bean 对象的获取等内容,并对这些内容做一些操作,例如:修改 Bean 的信息,添加日志打印、处理数据库路由对数据源的切换、给 RPC 服务连接注册中心等。 在对容器中 Bean 的实例化过程添加扩展机制的同时,还需要把目前关于 Spring.xml 初始化和加载策略进行优化,因为我们不太可能让面向 Spring 本身开发的 DefaultListableBeanFactory 服务,直接给予用户使用。修改点如下: DefaultListableBeanFactory、XmlBeanDefinitionReader,是我们在目前 Spring 框架中对于服务功能测试的使用方式,它能很好的体现出 Spring 是如何对 xml 加载以及注册Bean对象的操作过程,但这种方式是面向 Spring 本身的,还不具备一定的扩展性。 就像我们现在需要提供出一个可以在 Bean 初始化过程中,完成对 Bean 对象的扩展时,就很难做到自动化处理。所以我们要把 Bean 对象扩展机制功能和对 Spring 框架上下文的包装融合起来,对外提供完整的服务。 三、设计 为了能满足于在 Bean 对象从注册到实例化的过程中执行用户的自定义操作,就需要在 Bean 的定义和初始化过程中插入接口类,这个接口再有外部去实现自己需要的服务。那么在结合对 Spring 框架上下文的处理能力,就可以满足我们的目标需求了。整体设计结构如下图: 满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口: BeanFactoryPostProcess 和 BeanPostProcessor,也几乎是大家在使用 Spring 框架额外新增开发自己组建需求的两个必备接口。 BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息 BeanDefinition 执行修改操作。 BeanPostProcessor,也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象。这部分与后面要实现的 AOP 有着密切的关系。 同时如果只是添加这两个接口,不做任何包装,那么对于使用者来说还是非常麻烦的。我们希望于开发 Spring 的上下文操作类,把相应的 XML 加载 、注册、实例化以及新增的修改和扩展都融合进去,让 Spring 可以自动扫描到我们的新增服务,便于用户使用。 四、实现 1. 工程结构 small-spring-step-06└──src├──main│└──java│└──cn.bugstack.springframework│├──beans││├──factory│││├──factory││││├──AutowireCapableBeanFactory.java││││├──BeanDefinition.java││││├──BeanFactoryPostProcessor.java││││├──BeanPostProcessor.java││││├──BeanReference.java││││├──ConfigurableBeanFactory.java││││└──SingletonBeanRegistry.java│││├──support││││├──AbstractAutowireCapableBeanFactory.java││││├──AbstractBeanDefinitionReader.java││││├──AbstractBeanFactory.java││││├──BeanDefinitionReader.java││││├──BeanDefinitionRegistry.java││││├──CglibSubclassingInstantiationStrategy.java││││├──DefaultListableBeanFactory.java││││├──DefaultSingletonBeanRegistry.java││││├──InstantiationStrategy.java││││└──SimpleInstantiationStrategy.java│││├──support││││└──XmlBeanDefinitionReader.java│││├──BeanFactory.java│││├──ConfigurableListableBeanFactory.java│││├──HierarchicalBeanFactory.java│││└──ListableBeanFactory.java││├──BeansException.java││├──PropertyValue.java││└──PropertyValues.java│├──context││├──support│││├──AbstractApplicationContext.java│││├──AbstractRefreshableApplicationContext.java│││├──AbstractXmlApplicationContext.java│││└──ClassPathXmlApplicationContext.java││├──ApplicationContext.java││└──ConfigurableApplicationContext.java│├──core.io││├──ClassPathResource.java││├──DefaultResourceLoader.java││├──FileSystemResource.java││├──Resource.java││├──ResourceLoader.java││└──UrlResource.java│└──utils│└──ClassUtils.java└──test└──java└──cn.bugstack.springframework.test├──bean│├──UserDao.java│└──UserService.java├──common│├──MyBeanFactoryPostProcessor.java│└──MyBeanPostProcessor.java└──ApiTest.java 工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码 Spring 应用上下文和对Bean对象扩展机制的类关系,如图 7-3 图 7-3 在整个类图中主要体现出来的是关于 Spring 应用上下文以及对 Bean 对象扩展机制的实现。 以继承了 ListableBeanFactory 接口的 ApplicationContext 接口开始,扩展出一系列应用上下文的抽象实现类,并最终完成 ClassPathXmlApplicationContext 类的实现。而这个类就是最后交给用户使用的类。 同时在实现应用上下文的过程中,通过定义接口: BeanFactoryPostProcessor、 BeanPostProcessor 两个接口,把关于对 Bean 的扩展机制串联进去了。 2. 定义 BeanFactoryPostProcessor cn.bugstack.springframework.beans.factory.config.BeanFactoryPostProcessor publicinterfaceBeanFactoryPostProcessor{/***在所有的BeanDefinition加载完成后,实例化Bean对象之前,提供修改BeanDefinition属性的机制**@parambeanFactory*@throwsBeansException*/voidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException;} 在 Spring 源码中有这样一段描述 Allows for custom modification of an application context's bean definitions,adapting the bean property values of the context's underlying bean factory. 其实也就是说这个接口是满足于在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制。 3. 定义 BeanPostProcessor cn.bugstack.springframework.beans.factory.config.BeanPostProcessor publicinterfaceBeanPostProcessor{/***在Bean对象执行初始化方法之前,执行此方法**@parambean*@parambeanName*@return*@throwsBeansException*/ObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException;/***在Bean对象执行初始化方法之后,执行此方法**@parambean*@parambeanName*@return*@throwsBeansException*/ObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException;} 在 Spring 源码中有这样一段描述 Factory hook that allows for custom modification of new bean instances,e.g. checking for marker interfaces or wrapping them with proxies.也就是提供了修改新实例化 Bean 对象的扩展点。 另外此接口提供了两个方法: postProcessBeforeInitialization 用于在 Bean 对象执行初始化方法之前,执行此方法、 postProcessAfterInitialization用于在 Bean 对象执行初始化方法之后,执行此方法。 4. 定义上下文接口 cn.bugstack.springframework.context.ApplicationContext publicinterfaceApplicationContextextendsListableBeanFactory{} context 是本次实现应用上下文功能新增的服务包 ApplicationContext,继承于 ListableBeanFactory,也就继承了关于 BeanFactory 方法,比如一些 getBean 的方法。另外 ApplicationContext 本身是 Central 接口,但目前还不需要添加一些获取ID和父类上下文,所以暂时没有接口方法的定义。 cn.bugstack.springframework.context.ConfigurableApplicationContext publicinterfaceConfigurableApplicationContextextendsApplicationContext{/***刷新容器**@throwsBeansException*/voidrefresh()throwsBeansException;} ConfigurableApplicationContext 继承自 ApplicationContext,并提供了 refresh 这个核心方法。 如果你有看过一些 Spring 源码,那么一定会看到这个方法。 接下来也是需要在上下文的实现中完成刷新容器的操作过程。 5. 应用上下文抽象类实现 cn.bugstack.springframework.context.support.AbstractApplicationContext publicabstractclassAbstractApplicationContextextendsDefaultResourceLoaderimplementsConfigurableApplicationContext{@Overridepublicvoidrefresh()throwsBeansException{//1.创建BeanFactory,并加载BeanDefinitionrefreshBeanFactory();//2.获取BeanFactoryConfigurableListableBeanFactorybeanFactory=getBeanFactory();//3.在Bean实例化之前,执行BeanFactoryPostProcessor(Invokefactoryprocessorsregisteredasbeansinthecontext.)invokeBeanFactoryPostProcessors(beanFactory);//4.BeanPostProcessor需要提前于其他Bean对象实例化之前执行注册操作registerBeanPostProcessors(beanFactory);//5.提前实例化单例Bean对象beanFactory.preInstantiateSingletons();}protectedabstractvoidrefreshBeanFactory()throwsBeansException;protectedabstractConfigurableListableBeanFactorygetBeanFactory();privatevoidinvokeBeanFactoryPostProcessors(ConfigurableListableBeanFactorybeanFactory){Map<String,BeanFactoryPostProcessor>beanFactoryPostProcessorMap=beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);for(BeanFactoryPostProcessorbeanFactoryPostProcessor:beanFactoryPostProcessorMap.values()){beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);}}privatevoidregisterBeanPostProcessors(ConfigurableListableBeanFactorybeanFactory){Map<String,BeanPostProcessor>beanPostProcessorMap=beanFactory.getBeansOfType(BeanPostProcessor.class);for(BeanPostProcessorbeanPostProcessor:beanPostProcessorMap.values()){beanFactory.addBeanPostProcessor(beanPostProcessor);}}//...getBean、getBeansOfType、getBeanDefinitionNames方法} AbstractApplicationContext 继承 DefaultResourceLoader 是为了处理 spring.xml 配置资源的加载。 之后是在 refresh() 定义实现过程,包括: 提前实例化单例Bean对象 BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.) 获取 BeanFactory 创建 BeanFactory,并加载 BeanDefinition 另外把定义出来的抽象方法,refreshBeanFactory()、getBeanFactory() 由后面的继承此抽象类的其他抽象类实现。 6. 获取Bean工厂和加载资源 cn.bugstack.springframework.context.support.AbstractRefreshableApplicationContext publicabstractclassAbstractRefreshableApplicationContextextendsAbstractApplicationContext{privateDefaultListableBeanFactorybeanFactory;@OverrideprotectedvoidrefreshBeanFactory()throwsBeansException{DefaultListableBeanFactorybeanFactory=createBeanFactory();loadBeanDefinitions(beanFactory);this.beanFactory=beanFactory;}privateDefaultListableBeanFactorycreateBeanFactory(){returnnewDefaultListableBeanFactory();}protectedabstractvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory);@OverrideprotectedConfigurableListableBeanFactorygetBeanFactory(){returnbeanFactory;}} 在 refreshBeanFactory() 中主要是获取了 DefaultListableBeanFactory 的实例化以及对资源配置的加载操作 loadBeanDefinitions(beanFactory),在加载完成后即可完成对 spring.xml 配置文件中 Bean 对象的定义和注册,同时也包括实现了接口 BeanFactoryPostProcessor、BeanPostProcessor 的配置 Bean 信息。 但此时资源加载还只是定义了一个抽象类方法 loadBeanDefinitions(DefaultListableBeanFactory beanFactory),继续由其他抽象类继承实现。 7. 上下文中对配置信息的加载 cn.bugstack.springframework.context.support.AbstractXmlApplicationContext publicabstractclassAbstractXmlApplicationContextextendsAbstractRefreshableApplicationContext{@OverrideprotectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory){XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory,this);String[]configLocations=getConfigLocations();if(null!=configLocations){beanDefinitionReader.loadBeanDefinitions(configLocations);}}protectedabstractString[]getConfigLocations();} 在 AbstractXmlApplicationContext 抽象类的 loadBeanDefinitions 方法实现中,使用 XmlBeanDefinitionReader 类,处理了关于 XML 文件配置信息的操作。 同时这里又留下了一个抽象类方法,getConfigLocations(),此方法是为了从入口上下文类,拿到配置信息的地址描述。 8. 应用上下文实现类(ClassPathXmlApplicationContext) cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext publicclassClassPathXmlApplicationContextextendsAbstractXmlApplicationContext{privateString[]configLocations;publicClassPathXmlApplicationContext(){}/***从XML中加载BeanDefinition,并刷新上下文**@paramconfigLocations*@throwsBeansException*/publicClassPathXmlApplicationContext(StringconfigLocations)throwsBeansException{this(newString[]{configLocations});}/***从XML中加载BeanDefinition,并刷新上下文*@paramconfigLocations*@throwsBeansException*/publicClassPathXmlApplicationContext(String[]configLocations)throwsBeansException{this.configLocations=configLocations;refresh();}@OverrideprotectedString[]getConfigLocations(){returnconfigLocations;}} ClassPathXmlApplicationContext,是具体对外给用户提供的应用上下文方法。 在继承了 AbstractXmlApplicationContext 以及层层抽象类的功能分离实现后,在此类 ClassPathXmlApplicationContext 的实现中就简单多了,主要是对继承抽象类中方法的调用和提供了配置文件地址信息。 9. 在Bean创建时完成前置和后置处理 cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory publicabstractclassAbstractAutowireCapableBeanFactoryextendsAbstractBeanFactoryimplementsAutowireCapableBeanFactory{privateInstantiationStrategyinstantiationStrategy=newCglibSubclassingInstantiationStrategy();@OverrideprotectedObjectcreateBean(StringbeanName,BeanDefinitionbeanDefinition,Object[]args)throwsBeansException{Objectbean=null;try{bean=createBeanInstance(beanDefinition,beanName,args);//给Bean填充属性applyPropertyValues(beanName,bean,beanDefinition);//执行Bean的初始化方法和BeanPostProcessor的前置和后置处理方法bean=initializeBean(beanName,bean,beanDefinition);}catch(Exceptione){thrownewBeansException("Instantiationofbeanfailed",e);}addSingleton(beanName,bean);returnbean;}publicInstantiationStrategygetInstantiationStrategy(){returninstantiationStrategy;}publicvoidsetInstantiationStrategy(InstantiationStrategyinstantiationStrategy){this.instantiationStrategy=instantiationStrategy;}privateObjectinitializeBean(StringbeanName,Objectbean,BeanDefinitionbeanDefinition){//1.执行BeanPostProcessorBefore处理ObjectwrappedBean=applyBeanPostProcessorsBeforeInitialization(bean,beanName);//待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);invokeInitMethods(beanName,wrappedBean,beanDefinition);//2.执行BeanPostProcessorAfter处理wrappedBean=applyBeanPostProcessorsAfterInitialization(bean,beanName);returnwrappedBean;}privatevoidinvokeInitMethods(StringbeanName,ObjectwrappedBean,BeanDefinitionbeanDefinition){}@OverridepublicObjectapplyBeanPostProcessorsBeforeInitialization(ObjectexistingBean,StringbeanName)throwsBeansException{Objectresult=existingBean;for(BeanPostProcessorprocessor:getBeanPostProcessors()){Objectcurrent=processor.postProcessBeforeInitialization(result,beanName);if(null==current)returnresult;result=current;}returnresult;}@OverridepublicObjectapplyBeanPostProcessorsAfterInitialization(ObjectexistingBean,StringbeanName)throwsBeansException{Objectresult=existingBean;for(BeanPostProcessorprocessor:getBeanPostProcessors()){Objectcurrent=processor.postProcessAfterInitialization(result,beanName);if(null==current)returnresult;result=current;}returnresult;}} 实现 BeanPostProcessor 接口后,会涉及到两个接口方法, postProcessBeforeInitialization、 postProcessAfterInitialization,分别作用于Bean 对象执行初始化前后的额外处理。 也就是需要在创建 Bean 对象时,在 createBean 方法中添加 initializeBean(beanName, bean, beanDefinition); 操作。而这个操作主要主要是对于方法 applyBeanPostProcessorsBeforeInitialization、 applyBeanPostProcessorsAfterInitialization 的使用。 另外需要提一下,applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization 两个方法是在接口类 AutowireCapableBeanFactory 中新增加的。 五、测试 1. 事先准备 cn.bugstack.springframework.test.bean.UserDao publicclassUserDao{privatestaticMap<String,String>hashMap=newHashMap<>();static{hashMap.put("10001","小傅哥");hashMap.put("10002","八杯水");hashMap.put("10003","阿毛");}publicStringqueryUserName(StringuId){returnhashMap.get(uId);}} cn.bugstack.springframework.test.bean.UserService publicclassUserService{privateStringuId;privateStringcompany;privateStringlocation;privateUserDaouserDao;publicvoidqueryUserInfo(){returnuserDao.queryUserName(uId);}//...get/set} Dao、Service,是我们平常开发经常使用的场景。在 UserService 中注入 UserDao,这样就能体现出Bean属性的依赖了。 另外这里新增加了 company、location,两个属性信息,便于测试 BeanPostProcessor、BeanFactoryPostProcessor 两个接口对 Bean 属性信息扩展的作用。 2. 实现 BeanPostProcessor 和 BeanFactoryPostProcessor cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor publicclassMyBeanFactoryPostProcessorimplementsBeanFactoryPostProcessor{@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{BeanDefinitionbeanDefinition=beanFactory.getBeanDefinition("userService");PropertyValuespropertyValues=beanDefinition.getPropertyValues();propertyValues.addPropertyValue(newPropertyValue("company","改为:字节跳动"));}} cn.bugstack.springframework.test.common.MyBeanPostProcessor publicclassMyBeanPostProcessorimplementsBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{if("userService".equals(beanName)){UserServiceuserService=(UserService)bean;userService.setLocation("改为:北京");}returnbean;}@OverridepublicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{returnbean;}} 如果你在 Spring 中做过一些组件的开发那么一定非常熟悉这两个类,本文的测试也是实现了这两个类,对实例化过程中的 Bean 对象做一些操作。 3. 配置文件 基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类 <?xmlversion="1.0"encoding="UTF-8"?><beans><beanid="userDao"class="cn.bugstack.springframework.test.bean.UserDao"/><beanid="userService"class="cn.bugstack.springframework.test.bean.UserService"><propertyname="uId"value="10001"/><propertyname="company"value="腾讯"/><propertyname="location"value="深圳"/><propertyname="userDao"ref="userDao"/></bean></beans> 增强配置,有BeanFactoryPostProcessor、BeanPostProcessor,实现类 <?xmlversion="1.0"encoding="UTF-8"?><beans><beanid="userDao"class="cn.bugstack.springframework.test.bean.UserDao"/><beanid="userService"class="cn.bugstack.springframework.test.bean.UserService"><propertyname="uId"value="10001"/><propertyname="company"value="腾讯"/><propertyname="location"value="深圳"/><propertyname="userDao"ref="userDao"/></bean><beanclass="cn.bugstack.springframework.test.common.MyBeanPostProcessor"/><beanclass="cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor"/></beans> 这里提供了两个配置文件,一个是不包含BeanFactoryPostProcessor、BeanPostProcessor,另外一个是包含的。之所以这样配置主要对照验证,在运用 Spring 新增加的应用上下文和不使用的时候,都是怎么操作的。 4. 不用应用上下文 @Testpublicvoidtest_BeanFactoryPostProcessorAndBeanPostProcessor(){//1.初始化BeanFactoryDefaultListableBeanFactorybeanFactory=newDefaultListableBeanFactory();//2.读取配置文件&注册BeanXmlBeanDefinitionReaderreader=newXmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions("classpath:spring.xml");//3.BeanDefinition加载完成&Bean实例化之前,修改BeanDefinition的属性值MyBeanFactoryPostProcessorbeanFactoryPostProcessor=newMyBeanFactoryPostProcessor();beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);//4.Bean实例化之后,修改Bean属性信息MyBeanPostProcessorbeanPostProcessor=newMyBeanPostProcessor();beanFactory.addBeanPostProcessor(beanPostProcessor);//5.获取Bean对象调用方法UserServiceuserService=beanFactory.getBean("userService",UserService.class);Stringresult=userService.queryUserInfo();System.out.println("测试结果:"+result);} DefaultListableBeanFactory 创建 beanFactory 并使用 XmlBeanDefinitionReader 加载配置文件的方式,还是比较熟悉的。 接下来就是对 MyBeanFactoryPostProcessor 和 MyBeanPostProcessor 的处理,一个是在BeanDefinition 加载完成 & Bean实例化之前,修改 BeanDefinition 的属性值,另外一个是在Bean实例化之后,修改 Bean 属性信息。 测试结果 测试结果:小傅哥,改为:字节跳动,改为:北京Processfinishedwithexitcode0 通过测试结果可以看到,我们配置的属性信息已经与spring.xml 配置文件中不一样了。 5. 使用应用上下文 @Testpublicvoidtest_xml(){//1.初始化BeanFactoryClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:springPostProcessor.xml");//2.获取Bean对象调用方法UserServiceuserService=applicationContext.getBean("userService",UserService.class);Stringresult=userService.queryUserInfo();System.out.println("测试结果:"+result);} 另外使用新增加的 ClassPathXmlApplicationContext 应用上下文类,再操作起来就方便多了, 这才是面向用户使用的类,在这里可以一步把配置文件交给 ClassPathXmlApplicationContext,也不需要管理一些自定义实现的 Spring 接口的类。 测试结果 测试结果:小傅哥,改为:字节跳动,改为:北京Processfinishedwithexitcode0 这与不用应用上下文的测试结果是一样,不过现在的方式更加方便了。 六、总结 本文主要新增了 Spring 框架中两个非常重要的接口 BeanFactoryPostProcess、BeanPostProcessor 同时还添加了关于应用上下文的实现,ApplicationContext 接口的定义是继承 BeanFactory 外新增加功能的接口,它可以满足于自动识别、资源加载、容器事件、监听器等功能,同时例如一些国际化支持、单例Bean自动初始化等,也是可以在这个类里实现和扩充的。 通过本文的实现一定会非常了解 BeanFactoryPostProcess、BeanPostProcessor,以后再做一些关于 Spring 中间件的开发时,如果需要用到 Bean 对象的获取以及修改一些属性信息,那么就可以使用这两个接口了。同时 BeanPostProcessor 也是实现 AOP 切面技术的关键所在。 有人问: 面试问那么多,可是工作又用不到,是嘎哈么呢?,嘎哈么,那你说你开车上桥的时候,会每次都撞两边的护栏吗,不撞是吧,那不要修了哇,直接就铺一个平板,还省材料了。其实核心技术的原理学习,是更有助于你完成更复杂的架构设计,当你的知识能更全面覆盖所承接的需求时,也就能更好的做出合理的架构和落地。 七、系列推荐 小傅哥,一个有“副业”的码农! 久等了,小傅哥的《重学Java设计模式》终于出版了,彩印&纸质! 半年招聘筛选了400+份简历,告诉你怎么写容易被撩! 一次代码评审,差点过不了试用期! 大学四年到毕业工作5年的学习路线资源汇总 - END - 下方扫码关注 bugstack虫洞栈,与小傅哥一起学习成长、共同进步,做一个码场最贵Coder! 回复【设计模式】,获取《重学Java设计模式》,这是一本互联网真实案例的实践书籍,从实际业务中抽离出,交易、营销、秒杀、中间件、源码等众多场景进行学习代码设计。 回复【面经手册】,获取《面经手册•拿大厂Offer》,这是一本有深度的Java核心内容,从数据结构、算法、并发编程以及JVM系8不断深入讲解,让懂了就是真的懂。 你好,我是小傅哥。一线互联网 java 工程师、架构师,开发过交易&营销、写过运营&活动、设计过中间件也倒腾过中继器、IO板卡。不只是写Java语言,也搞过C#、PHP,是一个技术活跃的折腾者。 2020年写了一本PDF 《重学Java设计模式》 ,全网下载量50万+,帮助很多同学成长,现已出书。同年 github 的两个项目, CodeGuide 、 itstack-demo-design ,持续霸榜 Trending,成为全球热门项目。 2021年上架一本小册 《SpringBoot 中间件设计和开发》 ,16个互联网中间件场景、30个工程,是全网唯一一次手把手教你造轮子、写中间件,因为这样的技术离P7最近、离架构师最近、离高薪资最近! 本文分享自微信公众号 - bugstack虫洞栈(bugstack)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

如何使用Codex和Craftsman Agent Skills构建AI珠宝设计工作站-AI珠宝手串项链个性化定制

如果你使用 Codex、Claude 或其他支持 Agent Skills 的桌面 AI Agent,可以进一步把它们变成一个完整的AI 珠宝设计工作站。本文将介绍如何使用 Craftsman Agent Jewelry Designer Skills,让 AI Agent 从自然语言或参考图片出发,生成珠宝设计手稿、创建可交互的 3D 模型,并最终将设计交给制造工坊进行报价和生产。

优秀的个人博客,低调大师

《Spring 手撸专栏》第 14 章:笑傲江湖,通过注解配置和包自动扫描的方式完成Bean对象的注册

持续坚持原创输出,点击蓝字关注我吧 作者:小傅哥博客:https://bugstack.cn ❝ 沉淀、分享、成长,让自己和他人都能有所收获!😜 ❞ 目录 一、前言 二、目标 三、方案 四、实现 1. 工程结构 2. 处理占位符配置 3. 定义拦截注解 4. 处理对象扫描装配 5. 解析xml中调用扫描 五、测试 1. 事先准备 2. 属性配置文件 3. spring.xml 配置对象 4. 单元测试(占位符) 5. 单元测试(包扫描) 六、总结 七、系列推荐 一、前言 忒复杂,没等搞明白大促都过去了! 你经历过618和双11吗?你加入过大促时候那么多复杂的营销活动赚几毛钱吗?你开发过连读明白玩法都需要一周但只使用3天的大促需求吗?有时候对于有些产品的需求真的是太复杂了,复杂到开发、测试都需要在整个过程中不断的学习最后才可能读懂产品为啥这样的玩,要是一个长期的活动可能也就算了,培养用户心智吗!但这一整套拉新、助力、激活、下单、投保、领券、消费、开红包等等一连串的骚操作下来,如果在线上只用3天呢,或者是只用1天,那TM连参与的用户都没弄明白呢,活动就结束了,最后能打来什么样好的数据呢?对于这样流程复杂,估计连羊毛党都看不上!!! 以上只是举个例子,大部分时候并不会搞的这么恶心,评审也是过不去的!而同样的道理用在程序设计开发和使用中也是一样的,如果你把你的代码逻辑实现的过于分散,让外部调用方在使用的时候,需要调用你的接口多个和多次,还没有消息触达,只能定时自己轮训你的接口查看订单状态,每次还只能查10条,查多了你说不行,等等反人类的设计,都会给调用方带来要干你的体会。 所以,如果我们能在完成目的的情况下,都是希望尽可能流程简单、模式清晰、自动服务。那这在Spring的框架中也是有所体现的,这个框架的普及使用程度和它所能带来的方便性是分不开的,而我们如果能做到如此的方便,那肯定是一种好的设计和实现。 二、目标 其实到本章节我们已经把关于 IOC 和 AOP 全部核心内容都已经实现完成了,只不过在使用上还有点像早期的 Spring 版本,需要一个一个在 spring.xml 中进行配置。这与实际的目前使用的 Spring 框架还是有蛮大的差别,而这种差别其实都是在核心功能逻辑之上建设的在更少的配置下,做到更简化的使用。 这其中就包括:包的扫描注册、注解配置的使用、占位符属性的填充等等,而我们的目标就是在目前的核心逻辑上填充一些自动化的功能,让大家可以学习到这部分的设计和实现,从中体会到一些关于代码逻辑的实现过程,总结一些编码经验。 三、方案 首先我们要考虑🤔,为了可以简化 Bean 对象的配置,让整个 Bean 对象的注册都是自动扫描的,那么基本需要的元素包括:扫描路径入口、XML解析扫描信息、给需要扫描的Bean对象做注解标记、扫描Class对象摘取Bean注册的基本信息,组装注册信息、注册成Bean对象。那么在这些条件元素的支撑下,就可以实现出通过自定义注解和配置扫描路径的情况下,完成 Bean 对象的注册。除此之外再顺带解决一个配置中占位符属性的知识点,比如可以通过 ${token} 给 Bean 对象注入进去属性信息,那么这个操作需要用到 BeanFactoryPostProcessor,因为它可以处理 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制 而实现这部分内容是为了后续把此类内容结合到自动化配置处理中。整体设计结构如下图: 结合bean的生命周期,包扫描只不过是扫描特定注解的类,提取类的相关信息组装成BeanDefinition注册到容器中。 在XmlBeanDefinitionReader中解析<context:component-scan />标签,扫描类组装BeanDefinition然后注册到容器中的操作在ClassPathBeanDefinitionScanner#doScan中实现。 自动扫描注册主要是扫描添加了自定义注解的类,在xml加载过程中提取类的信息,组装 BeanDefinition 注册到 Spring 容器中。 所以我们会用到 <context:component-scan /> 配置包路径并在 XmlBeanDefinitionReader 解析并做相应的处理。 这里的处理会包括对类的扫描、获取注解信息等 最后还包括了一部分关于 BeanFactoryPostProcessor 的使用,因为我们需要完成对占位符配置信息的加载,所以需要使用到 BeanFactoryPostProcessor 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,修改 BeanDefinition 的属性信息。 这一部分的实现也为后续处理关于占位符配置到注解上做准备 四、实现 1. 工程结构 small-spring-step-12└──src├──main│└──java│└──cn.bugstack.springframework│├──aop││├──aspectj│││└──AspectJExpressionPointcut.java│││└──AspectJExpressionPointcutAdvisor.java││├──framework│││├──adapter││││└──MethodBeforeAdviceInterceptor.java│││├──autoproxy││││└──MethodBeforeAdviceInterceptor.java│││├──AopProxy.java│││├──Cglib2AopProxy.java│││├──JdkDynamicAopProxy.java│││├──ProxyFactory.java│││└──ReflectiveMethodInvocation.java││├──AdvisedSupport.java││├──Advisor.java││├──BeforeAdvice.java││├──ClassFilter.java││├──MethodBeforeAdvice.java││├──MethodMatcher.java││├──Pointcut.java││├──PointcutAdvisor.java││└──TargetSource.java│├──beans││├──factory│││├──config││││├──AutowireCapableBeanFactory.java││││├──BeanDefinition.java││││├──BeanFactoryPostProcessor.java││││├──BeanPostProcessor.java││││├──BeanReference.java││││├──ConfigurableBeanFactory.java││││├──InstantiationAwareBeanPostProcessor.java││││└──SingletonBeanRegistry.java│││├──support││││├──AbstractAutowireCapableBeanFactory.java││││├──AbstractBeanDefinitionReader.java││││├──AbstractBeanFactory.java││││├──BeanDefinitionReader.java││││├──BeanDefinitionRegistry.java││││├──CglibSubclassingInstantiationStrategy.java││││├──DefaultListableBeanFactory.java││││├──DefaultSingletonBeanRegistry.java││││├──DisposableBeanAdapter.java││││├──FactoryBeanRegistrySupport.java││││├──InstantiationStrategy.java││││└──SimpleInstantiationStrategy.java│││├──support││││└──XmlBeanDefinitionReader.java│││├──Aware.java│││├──BeanClassLoaderAware.java│││├──BeanFactory.java│││├──BeanFactoryAware.java│││├──BeanNameAware.java│││├──ConfigurableListableBeanFactory.java│││├──DisposableBean.java│││├──FactoryBean.java│││├──HierarchicalBeanFactory.java│││├──InitializingBean.java│││├──ListableBeanFactory.java│││└──PropertyPlaceholderConfigurer.java││├──BeansException.java││├──PropertyValue.java││└──PropertyValues.java│├──context││├──annotation│││├──ClassPathBeanDefinitionScanner.java│││├──ClassPathScanningCandidateComponentProvider.java│││└──Scope.java││├──event│││├──AbstractApplicationEventMulticaster.java│││├──ApplicationContextEvent.java│││├──ApplicationEventMulticaster.java│││├──ContextClosedEvent.java│││├──ContextRefreshedEvent.java│││└──SimpleApplicationEventMulticaster.java││├──support│││├──AbstractApplicationContext.java│││├──AbstractRefreshableApplicationContext.java│││├──AbstractXmlApplicationContext.java│││├──ApplicationContextAwareProcessor.java│││└──ClassPathXmlApplicationContext.java││├──ApplicationContext.java││├──ApplicationContextAware.java││├──ApplicationEvent.java││├──ApplicationEventPublisher.java││├──ApplicationListener.java││└──ConfigurableApplicationContext.java│├──core.io││├──ClassPathResource.java││├──DefaultResourceLoader.java││├──FileSystemResource.java││├──Resource.java││├──ResourceLoader.java││└──UrlResource.java│├──stereotype││└──Component.java│└──utils│└──ClassUtils.java└──test└──java└──cn.bugstack.springframework.test├──bean│├──IUserService.java│└──UserService.java└──ApiTest.java 工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码 在Bean的生命周期中自动加载包扫描注册Bean对象和设置占位符属性的类关系,如图 14-2 图 14-2 整个类的关系结构来看,其实涉及的内容并不多,主要包括的就是 xml 解析类 XmlBeanDefinitionReader 对 ClassPathBeanDefinitionScanner#doScan 的使用。 在 doScan 方法中处理所有指定路径下添加了注解的类,拆解出类的信息:名称、作用范围等,进行创建 BeanDefinition 好用于 Bean 对象的注册操作。 PropertyPlaceholderConfigurer 目前看上去像一块单独的内容,后续会把这块的内容与自动加载 Bean 对象进行整合,也就是可以在注解上使用占位符配置一些在配置文件里的属性信息。 2. 处理占位符配置 cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer publicclassPropertyPlaceholderConfigurerimplementsBeanFactoryPostProcessor{/***Defaultplaceholderprefix:{@value}*/publicstaticfinalStringDEFAULT_PLACEHOLDER_PREFIX="${";/***Defaultplaceholdersuffix:{@value}*/publicstaticfinalStringDEFAULT_PLACEHOLDER_SUFFIX="}";privateStringlocation;@OverridepublicvoidpostProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)throwsBeansException{//加载属性文件try{DefaultResourceLoaderresourceLoader=newDefaultResourceLoader();Resourceresource=resourceLoader.getResource(location);Propertiesproperties=newProperties();properties.load(resource.getInputStream());String[]beanDefinitionNames=beanFactory.getBeanDefinitionNames();for(StringbeanName:beanDefinitionNames){BeanDefinitionbeanDefinition=beanFactory.getBeanDefinition(beanName);PropertyValuespropertyValues=beanDefinition.getPropertyValues();for(PropertyValuepropertyValue:propertyValues.getPropertyValues()){Objectvalue=propertyValue.getValue();if(!(valueinstanceofString))continue;StringstrVal=(String)value;StringBuilderbuffer=newStringBuilder(strVal);intstartIdx=strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);intstopIdx=strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);if(startIdx!=-1&&stopIdx!=-1&&startIdx<stopIdx){StringpropKey=strVal.substring(startIdx+2,stopIdx);StringpropVal=properties.getProperty(propKey);buffer.replace(startIdx,stopIdx+1,propVal);propertyValues.addPropertyValue(newPropertyValue(propertyValue.getName(),buffer.toString()));}}}}catch(IOExceptione){thrownewBeansException("Couldnotloadproperties",e);}}publicvoidsetLocation(Stringlocation){this.location=location;}} 依赖于 BeanFactoryPostProcessor 在 Bean 生命周期的属性,可以在 Bean 对象实例化之前,改变属性信息。所以这里通过实现 BeanFactoryPostProcessor 接口,完成对配置文件的加载以及摘取占位符中的在属性文件里的配置。 这样就可以把提取到的配置信息放置到属性配置中了, buffer.replace(startIdx, stopIdx + 1, propVal); propertyValues.addPropertyValue 3. 定义拦截注解 cn.bugstack.springframework.context.annotation.Scope @Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceScope{Stringvalue()default"singleton";} 用于配置作用域的自定义注解,方便通过配置Bean对象注解的时候,拿到Bean对象的作用域。 不过一般都使用默认的 singleton cn.bugstack.springframework.stereotype.Component @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceComponent{Stringvalue()default"";} Component 自定义注解大家都非常熟悉了,用于配置到 Class 类上的。除此之外还有 Service、Controller,不过所有的处理方式基本一致,这里就只展示一个 Component 即可。 4. 处理对象扫描装配 cn.bugstack.springframework.context.annotation.ClassPathScanningCandidateComponentProvider publicclassClassPathScanningCandidateComponentProvider{publicSet<BeanDefinition>findCandidateComponents(StringbasePackage){Set<BeanDefinition>candidates=newLinkedHashSet<>();Set<Class<?>>classes=ClassUtil.scanPackageByAnnotation(basePackage,Component.class);for(Class<?>clazz:classes){candidates.add(newBeanDefinition(clazz));}returncandidates;}} 这里先要提供一个可以通过配置路径 basePackage=cn.bugstack.springframework.test.bean,解析出 classes 信息的工具方法 findCandidateComponents,通过这个方法就可以扫描到所有 @Component 注解的 Bean 对象了。 cn.bugstack.springframework.context.annotation.ClassPathBeanDefinitionScanner publicclassClassPathBeanDefinitionScannerextendsClassPathScanningCandidateComponentProvider{privateBeanDefinitionRegistryregistry;publicClassPathBeanDefinitionScanner(BeanDefinitionRegistryregistry){this.registry=registry;}publicvoiddoScan(String...basePackages){for(StringbasePackage:basePackages){Set<BeanDefinition>candidates=findCandidateComponents(basePackage);for(BeanDefinitionbeanDefinition:candidates){//解析Bean的作用域singleton、prototypeStringbeanScope=resolveBeanScope(beanDefinition);if(StrUtil.isNotEmpty(beanScope)){beanDefinition.setScope(beanScope);}registry.registerBeanDefinition(determineBeanName(beanDefinition),beanDefinition);}}}privateStringresolveBeanScope(BeanDefinitionbeanDefinition){Class<?>beanClass=beanDefinition.getBeanClass();Scopescope=beanClass.getAnnotation(Scope.class);if(null!=scope)returnscope.value();returnStrUtil.EMPTY;}privateStringdetermineBeanName(BeanDefinitionbeanDefinition){Class<?>beanClass=beanDefinition.getBeanClass();Componentcomponent=beanClass.getAnnotation(Component.class);Stringvalue=component.value();if(StrUtil.isEmpty(value)){value=StrUtil.lowerFirst(beanClass.getSimpleName());}returnvalue;}} ClassPathBeanDefinitionScanner 是继承自 ClassPathScanningCandidateComponentProvider 的具体扫描包处理的类,在 doScan 中除了获取到扫描的类信息以后,还需要获取 Bean 的作用域和类名,如果不配置类名基本都是把首字母缩写。 5. 解析xml中调用扫描 cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader publicclassXmlBeanDefinitionReaderextendsAbstractBeanDefinitionReader{protectedvoiddoLoadBeanDefinitions(InputStreaminputStream)throwsClassNotFoundException,DocumentException{SAXReaderreader=newSAXReader();Documentdocument=reader.read(inputStream);Elementroot=document.getRootElement();//解析context:component-scan标签,扫描包中的类并提取相关信息,用于组装BeanDefinitionElementcomponentScan=root.element("component-scan");if(null!=componentScan){StringscanPath=componentScan.attributeValue("base-package");if(StrUtil.isEmpty(scanPath)){thrownewBeansException("Thevalueofbase-packageattributecannotbeemptyornull");}scanPackage(scanPath);}//...省略其他//注册BeanDefinitiongetRegistry().registerBeanDefinition(beanName,beanDefinition);}privatevoidscanPackage(StringscanPath){String[]basePackages=StrUtil.splitToArray(scanPath,',');ClassPathBeanDefinitionScannerscanner=newClassPathBeanDefinitionScanner(getRegistry());scanner.doScan(basePackages);}} 关于 XmlBeanDefinitionReader 中主要是在加载配置文件后,处理新增的自定义配置属性 component-scan,解析后调用 scanPackage 方法,其实也就是我们在 ClassPathBeanDefinitionScanner#doScan 功能。 另外这里需要注意,为了可以方便的加载和解析xml,XmlBeanDefinitionReader 已经全部替换为 dom4j 的方式进行解析处理。 五、测试 1. 事先准备 @Component("userService")publicclassUserServiceimplementsIUserService{privateStringtoken;publicStringqueryUserInfo(){try{Thread.sleep(newRandom(1).nextInt(100));}catch(InterruptedExceptione){e.printStackTrace();}return"小傅哥,100001,深圳";}publicStringregister(StringuserName){try{Thread.sleep(newRandom(1).nextInt(100));}catch(InterruptedExceptione){e.printStackTrace();}return"注册用户:"+userName+" success!";}@OverridepublicStringtoString(){return"UserService#token={"+token+"}";}publicStringgetToken(){returntoken;}publicvoidsetToken(Stringtoken){this.token=token;}} 给 UserService 类添加一个自定义注解 @Component("userService") 和一个属性信息 String token。这是为了分别测试包扫描和占位符属性。 2. 属性配置文件 token=RejDlI78hu223Opo983Ds 这里配置一个 token 的属性信息,用于通过占位符的方式进行获取 3. spring.xml 配置对象 spring-property.xml <?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context"><beanclass="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer"><propertyname="location"value="classpath:token.properties"/></bean><beanid="userService"class="cn.bugstack.springframework.test.bean.UserService"><propertyname="token"value="${token}"/></bean></beans> 加载 classpath:token.properties 设置占位符属性值 ${token} spring-scan.xml <?xmlversion="1.0"encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context"><context:component-scanbase-package="cn.bugstack.springframework.test.bean"/></beans> 添加 component-scan 属性,设置包扫描根路径 4. 单元测试(占位符) @Testpublicvoidtest_property(){ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:spring-property.xml");IUserServiceuserService=applicationContext.getBean("userService",IUserService.class);System.out.println("测试结果:"+userService);} 测试结果 测试结果:UserService#token ={ RejDlI78hu223Opo983Ds }Processfinishedwithexitcode0 通过测试结果可以看到 UserService 中的 token 属性已经通过占位符的方式设置进去配置文件里的 token.properties 的属性值了。 5. 单元测试(包扫描) @Testpublicvoidtest_scan(){ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("classpath:spring-scan.xml");IUserServiceuserService=applicationContext.getBean("userService",IUserService.class);System.out.println("测试结果:"+userService.queryUserInfo());} 测试结果 测试结果:小傅哥,100001,深圳Processfinishedwithexitcode0 通过这个测试结果可以看出来,现在使用注解的方式就可以让 Class 注册完成 Bean 对象了。 六、总结 通过整篇的内容实现可以看出来,目前的功能添加其实已经不复杂了,都是在 IOC 和 AOP 核心的基础上来补全功能。这些补全的功能也是在完善 Bean 的生命周期,让整个功能使用也越来越容易。 在你不断的实现着 Spring 的各项功能时,也可以把自己在平常使用 Spring 的一些功能想法融入进来,比如像 Spring 是如何动态切换数据源的,线程池是怎么提供配置的,这些内容虽然不是最基础的核心范围,但也非常重要。 可能有些时候这些类实现的内容对新人来说比较多,可以一点点动手实现逐步理解,在把一些稍微较有难度的内容实现后,其实后面也就没有那么难理解了。 七、系列推荐 13年毕业,用两年时间从外包走进互联网大厂 工作两三年了,整不明白架构图都画啥? 面试现场:小伙伴美团一面的分享和分析(含解答) LinkedList插入速度比ArrayList快?你确定吗? Netty+JavaFx实战:仿桌面版微信聊天 - END - 下方扫码关注 bugstack虫洞栈,与小傅哥一起学习成长、共同进步,做一个码场最贵Coder! 回复【设计模式】,获取《重学Java设计模式》,这是一本互联网真实案例的实践书籍,从实际业务中抽离出,交易、营销、秒杀、中间件、源码等众多场景进行学习代码设计。 回复【面经手册】,获取《面经手册•拿大厂Offer》,这是一本有深度的Java核心内容,从数据结构、算法、并发编程以及JVM系8不断深入讲解,让懂了就是真的懂。 你好,我是小傅哥。一线互联网 java 工程师、架构师,开发过交易&营销、写过运营&活动、设计过中间件也倒腾过中继器、IO板卡。不只是写Java语言,也搞过C#、PHP,是一个技术活跃的折腾者。 2020年写了一本PDF 《重学Java设计模式》 ,全网下载量50万+,帮助很多同学成长,现已出书。同年 github 的两个项目, CodeGuide 、 itstack-demo-design ,持续霸榜 Trending,成为全球热门项目。 2021年上架一本小册 《SpringBoot 中间件设计和开发》 ,16个互联网中间件场景、30个工程,是全网唯一一次手把手教你造轮子、写中间件,因为这样的技术离P7最近、离架构师最近、离高薪资最近! 本文分享自微信公众号 - bugstack虫洞栈(bugstack)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

“Windows 任务管理器之父”用纯 C 手搓了一个文本编辑器,致敬经典 Petzold 编程风格

在现代IDE和语法高亮编辑器的时代,一个名为Retropad的项目选择了相反的方向:完全用纯C语言重写一个Windows记事本。这个项目由“Windows任务管理器之父”Dave W. Plummer创造,目标非常明确——在不使用任何现代框架的情况下,完整复现经典Windows记事本的每一个功能。该软件体积仅为2749字节,约合2.7KB,运行高效且资源占用极低。 Retropad的技术实现严格遵循了Charles Petzold在《Programming Windows》中开创的Win32 API编程范式。项目完全采用纯C编写,不依赖任何外部库,只使用Windows原生API。这种做法在今天看起来近乎复古,但恰恰是这种约束让它成为学习Win32编程的绝佳教材。 在功能上,Retropad几乎做到了对经典记事本的像素级复刻:完整的菜单系统和快捷键绑定(Ctrl+N/O/S打开新建保存、Ctrl+F搜索、Ctrl+H替换、Ctrl+G跳转、F5时间戳),自动隐藏的状态栏,查找替换对话框,字体选择器,拖拽文件打开,以及BOM感知的文件I/O——系统会自动检测UTF-8和UTF-16编码,保存时默认使用UTF-8 BOM格式。唯一被刻意省略的功能是打印支持,项目作者认为在现代浏览器和PDF阅读器普及的背景下,打印功能已经不再是编辑器的基础需求。 从构建系统看,Retropad同时支持Visual Studio 2022(MSVC+nmake)和MinGW-w64(gcc+make)两套工具链。项目结构简洁:retropad.c处理主窗口和UI逻辑,file_io.c/.h处理编码相关的文件操作,retropad.rc定义菜单、快捷键、对话框和图标资源。这种模块化设计让代码结构清晰易懂。 Retropad的意义不在于替代Windows自带的记事本——微软早已在其基础上增加了现代化的标签页和Copilot辅助功能。这个项目的价值在于它提供了一种可能性:在AI代码生成工具盛行的今天,人类仍然可以完整理解一个编辑器从窗口创建、菜单系统、文件I/O到Unicode处理的全部细节。每一行代码都是可读的,每一个系统调用都是可追溯的。这种透明性正是现代框架所缺乏的。 对于想学习Windows桌面开发的程序员来说,Retropad提供了一个极好的起点。它不需要配置复杂的现代开发环境,不需要理解异步编程或虚拟DOM,只需要一个文本编辑器、一份Windows SDK文档,和Charles Petzold的那本书。 参考来源: https://github.com/PlummersSoftwareLLC/retropad

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册