首页
题库
公司真题
专项练习
面试题库
在线编程
面试
面试经验
AI 模拟面试
简历
求职
学习
基础学习课
实战项目课
求职辅导课
专栏&文章
竞赛
搜索
我要招人
发布职位
发布职位、邀约牛人
更多企业解决方案
AI面试、笔试、校招、雇品
HR免费试用AI面试
最新面试提效必备
登录
/
注册
何人听我楚狂声
字节跳动_抖音_后端开发工程师
关注
已关注
取消关注
#声哥今天更新了吗#
白天把《四月是你的谎言》看完了,心情十分沉重,就放到晚上更新了
@何人听我楚狂声:
手撸一个 Spring —— 2. 基于注解
精华
上一篇中,读取 xml 解析为 BeanDefinition 的类 XmlBeanDefinitionReader 的代码在如下链接:https://github.com/CN-GuoZiyang/My-Spring-IOC/blob/82967670e5/src/main/java/top/guoziyang/springframework/reader/XmlBeanDefinitionReader.java,微信公众号似乎不允许内置链接。 上一节我们已经实现了一个包含基本功能的 IOC 容器,并且也解决了各种依赖问题。没看过上一篇的同学可以在我的讨论帖记录中查找。 这一节我们来对这个框架进行扩展,实现基于注解自动注入 bean 的方式,免去繁琐的配置文件。 本节最终的代码为:https://github.com/CN-GuoZiyang/My-Spring-IOC/tree/8a3a9c640e532c5d4aa8d62f18b42fa336c94f2e 定义注解 首先我们需要定义一些注解类,仿照 Spring,我们定义如下的五个注解:、@Component、@Scope、@Autowired、@Qualifier 和 @Value。他们分别的作用如下: @Component:这个注解用于类,表示这个类是一个需要被注册进容器的 bean @Scope:这个注解用于类,默认值是 "singleton",还可使用 "prototype",标明这个 bean 是单例还是多例的 @Autowired:这个注解用于属性,表示向属性自动注入容器中对应类型的 bean @Qualifier:这个注解用于属性,需要传递一个字符串,表示给这个属性注入对应名称的 bean @Value:这个注解用于属性,表示向这个属性注入某个值(基本类型) 我们来定义注解,如下: @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Component { String name() default "";} @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface Scope { String value() default "singleton";} @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Autowired{} @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Qualifier { String value();} @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface Value { public String value();} 定义注解同样需要一些注解(禁止套娃),@Retention 注解用来标示注解类型的生命周期,由于我们的这些注解都是需要在运行时通过反射获取的,所以这个注解的值都是 RetentionPolicy.RUNTIME。@Target 注解用来定义注解能够被应用于源码的哪些位置,例如 ElementType.FIELD 为属性,ElementType.TYPE 是类。 由于不是 SpringBoot,我们仍然需要在配置文件中书写自动注入的扫描范围,来指导框架扫描。配置文件如下: <?xml version="1.0" encoding="UTF-8"?><beans> <component-scan base-package="top.guoziyang.main"></component-scan></beans> 启动后,框架会自动扫描该包及其子包下所有使用注解标明的 Bean,并注入容器。 扫描注解 由于配置文件发生了改变,自然我们需要改变 xml 文件的解析方式,在 XmlBeanDefinitionReader 类的 parseBeanDefinitions() 方法中,一旦我们发现了 component-scan 标签,说明我们是使用注解来注入 Bean 的: protected void parseBeanDefinitions(Element root) { ... for(int i = 0; i < nodeList.getLength(); i ++) { if(nodeList.item(i) instanceof Element) { Element ele = (Element)nodeList.item(i); if(ele.getTagName().equals("component-scan")) { basePackage = ele.getAttribute("base-package"); break; } } } if(basePackage != null) { parseAnnotation(basePackage); return; } ...} parseAnnotation() 方***获取到目标包下所有的类,并遍历解析: protected void parseAnnotation(String basePackage) { Set<Class<?>> classes = getClasses(basePackage); for(Class clazz : classes) { processAnnotationBeanDefinition(clazz); }} getClass() 就是用来获取到包下的所有类,它的实现较为繁琐,具体代码在:https://github.com/CN-GuoZiyang/My-Spring-IOC/blob/8a3a9c640e/src/main/java/top/guoziyang/springframework/reader/XmlBeanDefinitionReader.java#L141,当然这个功能也可以用 guava 直接实现。 processAnnotationBeanDefinition() 这个方法利用了反射机制来获取类上的注解,以判断对应的类是否是需要注册的 bean,并寻找相关的注解,如 @Scope,形成对应的 BeanDefinition: protected void processAnnotationBeanDefinition(Class<?> clazz) { if(clazz.isAnnotationPresent(Component.class)) { String name = clazz.getAnnotation(Component.class).name(); if(name == null || name.length() == 0) { name = clazz.getName(); } String className = clazz.getName(); boolean singleton = true; if(clazz.isAnnotationPresent(Scope.class) && "prototype".equals(clazz.getAnnotation(Scope.class).value())) { singleton = false; } BeanDefinition beanDefinition = new BeanDefinition(); processAnnotationProperty(clazz, beanDefinition); beanDefinition.setBeanClassName(className); beanDefinition.setSingleton(singleton); getRegistry().put(name, beanDefinition); }} processAnnotationProperty() 则是对类的每一个属性进行判断,来确定每个属性是否需要注入等: protected void processAnnotationProperty(Class<?> clazz, BeanDefinition beanDefinition) { Field[] fields = clazz.getDeclaredFields(); for(Field field : fields) { String name = field.getName(); if(field.isAnnotationPresent(Value.class)) { Value valueAnnotation = field.getAnnotation(Value.class); String value = valueAnnotation.value(); if(value != null && value.length() > 0) { // 优先进行值注入 beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value)); } } else if(field.isAnnotationPresent(Autowired.class)) { if(field.isAnnotationPresent(Qualifier.class)) { Qualifier qualifier = field.getAnnotation(Qualifier.class); String ref = qualifier.value(); if(ref == null || ref.length() == 0) { throw new IllegalArgumentException("the value of Qualifier should not be null!"); } BeanReference beanReference = new BeanReference(ref); beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference)); } else { String ref = field.getType().getName(); BeanReference beanReference = new BeanReference(ref); beanDefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanReference)); } } }} 其实就是将原本从 xml 获取到的各种信息,通过反射的方式从注解中获取了出来。整体的逻辑并没有什么变化。 实际上,由于产生的结果一致(生成 beanDefinition 并存入 registry),可以仿照 Spring 的实现使用委托模式,这样耦合度就不会太高。但是由于使用注解同样还需要读取配置文件,较为繁琐,就没有解耦(实际上是我偷懒了)。 写了这么多,其实都仅仅是对 XmlBeanDefinitionReader 这个类的改动,因为这个类的职责就是,从文件中解析出 BeanDefinition。这个类的实现在 https://github.com/CN-GuoZiyang/My-Spring-IOC/blob/8a3a9c640e/src/main/java/top/guoziyang/springframework/reader/XmlBeanDefinitionReader.java 中。 看看效果 由于我们使用了注解,就需要修改相应的 bean,加上上面定义的注解即可: @Component(name = "helloWorldService")@Scope("prototype")public class HelloWorldServiceImpl implements HelloWorldService { @Value("Hello, world") private String text; @Override public void saySomething() { System.out.println(text); }} @Component(name = "wrapService")public class WrapService { @Autowired private HelloWorldService helloWorldService; public void say() { helloWorldService.saySomething(); }} 测试代码如下: public class Main() { public static void annotationTest() throws Exception { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-annotation.xml"); WrapService wrapService = (WrapService) applicationContext.getBean("wrapService"); wrapService.say(); HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService"); HelloWorldService helloWorldService2 = (HelloWorldService) applicationContext.getBean("helloWorldService"); System.out.println("prototype验证:相等" + (helloWorldService == helloWorldService2)); WrapService wrapService2 = (WrapService) applicationContext.getBean("wrapService"); System.out.println("singleton验证:相等" + (wrapService == wrapService2)); }} 其实和第一节的测试一样,结果也是一致的。 结束语 到这里,自己手写的 IOC 容器的简单实现就完成了!还是挺有成就感的。使用体验和 Spring 基本没啥差别(误)。 下一篇文章,会基于已经实现的 IOC 容器,在其上层手撸一个 SpringMVC 的简单实现。 所以,不要停下来啊!
点赞 8
评论 2
声哥今天更新了吗
全部评论
推荐
最新
楼层
暂无评论,快来抢首评~
相关推荐
2025-12-30 20:29
广东工业大学 Java
沐潼科技实习二面面经
1:自我介绍+对岗位内容提问2:我给你提个开放回答:上海苗条和广东米粉的区别是什么 (我从工艺、人群、地区特色等方面分点回答)3:小学老师没教过你写作文吗,描述一朵花你就说这朵花多美就行了,你这是在用AI回答我。直接告诉我多好吃就行了4:直接做题:根据一个需求文档,快速写出两个接口,15min,文档做题。需求内容为:给你一个线上报错的飞书表格日志,读取用户输入的单元格信息,返回需要获取的报错详情的单元格起点和终点。例如:用户输入“!A1B2”则返回A1和B2。然后第二个接口,根据起点和终点从飞书文档中读取文档信息并转换成字符串。要求超过十条日志记录不合法(缺少字段)或用户参数不合法就退出记录。...
查看4道真题和解析
点赞
评论
收藏
分享
01-02 12:23
复旦大学 Java
校招善人,日常帮看简历,挖掘项目亮点 无偿
点赞
评论
收藏
分享
2025-11-11 22:04
门头沟学院 内外饰设计工程师
第一次实习,听mentor一句劝
作为带过好几个实习生的老mentor,我见过有同学带着一腔热血来实习,最后却只带走一份单薄的履历。实习,是你从学校到职场最关键的过渡期,它的价值远不止一份实习证明。今天,我不讲大道理,就从我作为Mentor的视角,给你们几条能立刻用上的建议。记住,你的目标不是当个好学生,而是成为一个值得信赖的职场新人。一、 心态转变:从被动答题到主动解题这是我最想强调的一点。学生思维是:等待老师布置明确的作业,然后完成它。职场思维是:主动发现模糊的问题,然后解决它。反面事例:接到任务后,埋头就做,遇到困难不吭声,直到截止日期才说“这个我不会”。Mentor期待的是啥呢?首先是确认目标:接到任务后,先用自己的话复述一遍:“我理解这个任务是要达成XX效果,对吗?” 确保方向没错。然后是主动思考:不要只带问题来,要带“选择题”。问“这个数据我不会查,我尝试了A和B方法都失败了,您看是方法C更合适,还是我有其他没考虑到的渠道?” 这证明了你的思考和努力。最后是闭环思维:任务完成后,主动告知结果:“XX任务已完成,数据/文件已发您邮箱,并同步在团队网盘了。其中有个小发现是……,供您参考。” 让一切有始有终。二、 沟通方式:实习生的很多错误,都源于“想当然”和“不敢问”。反面教材:在做一个PPT时,因为不确定公司模板,就套用了自己觉得好看的模板,结果不能用。那么怎么确认,怎么提问呢?第一个,不懂就问,但别重复问:第一次问,是学习;同样的问题问第三次,就是不用心。准备一个笔记本,把关键信息、操作流程、注意事项都记下来。第二个,及时汇报,别等追问:特别是遇到卡壳或可能延期时,一定要提前说。Mentor不怕你慢,就怕你失联。没事儿更新一下进度:目前已完成80%,但在XX环节遇到点阻力,正在想办法沟通等回复,预计今天下班前确定结果,到时候给您,这样说能让人极度安心。第三个,珍惜1on1机会:和Mentor的定期沟通,不是你被动接受批评,而是你主动获取信息和反馈的黄金时间。提前准备好:a) 本周工作进展;b) 遇到的困惑/挑战;c) 希望学习的新技能;d) 对团队业务的任何好奇。三、 工作习惯: 专业性体现在细节里职业素养不是空话,它藏在每一个你容易忽略的细节中。1. 邮件/沟通软件礼仪:邮件:标题清晰(如【实习生XX-XX项目周报】),正文称呼得体,结尾有落款。别用“在吗?”开头。工作群:别发表情包刷屏,沟通事情简明扼要。收到任务或通知,回复“收到,谢谢”,这是基本的确认和尊重。2. 文件管理与命名:我会观察实习生的桌面,看他们的使用习惯,乱糟糟的桌面说明他没条理。文件命名要使用统一的命名规则:日期_项目名_内容_版本_姓名。例如:20231027_秋招海报_初版_张三。这能为整个团队节省大量沟通成本。3. 对待杂活的态度:复印、整理数据、会议纪要……这些dirty work是不可避免的。但优秀的人是能从中找到价值的:整理数据时,可以留意数据之间的关联或异常,做会议纪要时,可以梳理出会议的决策和待办事项。四、 终极目标:带走三样东西1. 一段能讲出STAR法则的实战经历:这直接决定了你未来求职简历的厚度。2. 一位可以为你未来背书的Mentor/同事:好好表现,离职时保持联系,他们可能成为你未来求职的推荐人和内推渠道。3. 对行业和岗位的真实认知:通过这次实习,你想清楚自己是更热爱这个行业,还是想赶紧跑路?这个答案,价值千金。最后,作为你们的Mentor,我想说:大胆去试,勇敢去问,别怕犯错。实习期是你犯错成本最低的时候。展现出你的靠谱、主动和思考,我们做Mentor的,会非常乐意把更核心的任务交给你,因为带你,也是在为团队培养未来的战友。希望这些建议能帮你少走弯路,打一场漂亮的实习战!
家族企业:
实习一年比在大学多年都有用
第一次找实习,我建议__
点赞
评论
收藏
分享
2025-11-26 02:29
北京虾皮信息科技有限公司_今日头条_前端工程师(准入职员工)
和女朋友一起北漂啦
女朋友签约了滴滴我签约了字节明年就可以开始北漂生活啦
尼公子的虾滑海鲜粥:
你好,举报在哪里,替我点一下
点赞
评论
收藏
分享
01-03 21:48
蚌埠坦克学院 嵌入式软件开发
准备嵌入式面试的关键时刻到了
——1 月不冲,3、4 月只能被动挨打不知不觉已经 1 月了。对准备嵌入式方向的同学来说,这其实是一个非常关键、甚至有点“危险”的时间点。因为再往后看——3~4 月,春招就会陆续启动。而从“现在”到“能上战场”,中间真正可控、可系统准备的时间,其实已经不多了。一、1 月份,已经不是“了解阶段”了很多人到现在还停留在:STM32 外设“差不多会用”FreeRTOS “大概懂”Linux 驱动“听过、看过几篇文章”面试题“等快面试了再刷”说句实话:这个阶段再抱着“先看看”的心态,已经有点晚了。1 月份开始,你要意识到一件事:👉 你不是在学知识,而是在为“面试筛选”做准备。面试不会问你“你觉得这个...
点赞
评论
收藏
分享
评论
点赞成功,聊一聊 >
3
收藏
分享
评论
提到的真题
返回内容
全站热榜
更多
1
...
🔥2026创作新起点:《新年启航计划》来袭,三大赛道等你来冲!
1.3W
2
...
一大波手撕正在靠近!
9725
3
...
为什么你的实习是“无效实习”?又该如何做
4376
4
...
腾讯游戏后端一面
2334
5
...
uu们,面试的时候一定要大大方方的啊!
2244
6
...
双非终于上岸了!!!!
2070
7
...
千里智驾毁约
1766
8
...
帕斯亚科技UE客户端面经
1750
9
...
快手Java日常实习一面
1729
10
...
双非本科,嵌入式秋招上岸的一次总结
1438
创作者周榜
更多
正在热议
更多
#
有深度的简历长什么样?
#
1964次浏览
38人参与
#
应届生第一份工作最好去大厂吗?
#
123530次浏览
1093人参与
#
入职第一天
#
1721次浏览
27人参与
#
非技术er求职现状
#
126977次浏览
772人参与
#
你不能接受的企业文化有哪些
#
1425次浏览
34人参与
#
工作后会跟朋友渐行渐远吗
#
54780次浏览
401人参与
#
实习最想跑路的瞬间
#
107153次浏览
649人参与
#
CVTE工作体验
#
17274次浏览
39人参与
#
双非本科的出路是什么?
#
192332次浏览
1516人参与
#
帆软软件工作体验
#
8543次浏览
34人参与
#
我的上岸简历长这样
#
757026次浏览
11284人参与
#
秋招感动瞬间
#
111703次浏览
509人参与
#
机械人选offer,最看重什么?
#
152255次浏览
789人参与
#
运营/市场/管培生岗位评价
#
28893次浏览
179人参与
#
多益网络求职进展汇总
#
60216次浏览
272人参与
#
为什么那么多公司毁约
#
216931次浏览
1463人参与
#
上班苦还是上学苦呢?
#
317641次浏览
2047人参与
#
2023届毁约公司名单
#
234172次浏览
1063人参与
#
小米求职进展汇总
#
997441次浏览
6500人参与
#
秋招想进国企该如何准备
#
119709次浏览
602人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务