1 需求说明

  1. 自己写一个简单的 Spring 容器, 通过读取类的注解 (@Component @Controller @Service @Reponsitory),将对象注入到 IOC 容器

bean id= MyComponent bean 对象=
com.hj.spring.component.MyComponent@13221655

bean id= UserDao bean 对象= com.hj.spring.component.UserDao@2f2c9b19
bean id= UserService bean 对象=
com.hj.spring.component.UserService@31befd9f

bean id= UserController bean 对象=
com.hj.spring.component.UserController@1c20c684 i am user da

  1. 我们不使用 Spring 原生框架,自己手写 IO+Annotaion+反射+集合 技术实现, 打通 Spring 注解方式开发的技术难点

2 思路分析

  1. 思路分析+程序结构

    • 我们使用注解方式完成, 不使用 xml 来配置

    • 程序框架图

3 代码实现

● 应用实例

  1. 手动实现注解的方式来配置 Controller / Service / Respository / Component
  2. 我们使用自定义注解来完成.

● 代码实现
代码结构:

  1. 创建 ComponentScan.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 解读* 1. @Target(ElementType.TYPE)指定我们的ComponentScan注解可以修饰 Type程序元素* 2. @Retention(RetentionPolicy.RUNTIME) 指定ComponentScan注解 保留范围* 3. String value() default ""; 表示ComponentScan 可以传入 value*/@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {String value() default "";
}
  1. 创建 HJSpringConfig.java
/*** @author hj* @version 1.0* 这是一个配置类, 作用类似我们原生spring的 beans.xml 容器配置文件*/
@ComponentScan(value = "com.hj.springboot_06_ssmp.component")
public class HJSpringConfig {}
  1. 创建 SpringApplicationContext.java
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;/*** @author hj* SpringApplicationContext 类的作用类似Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//ioc我存放的就是通过反射创建的对象(基于注解方式)private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();public SpringApplicationContext(Class configClass) {this.configClass = configClass;//获取要扫描的包//1. 先得到HJSpringConfig配置的的@ComponentScan(value = "com.hj.springboot_06_ssmp.component")ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//注解System.out.println("注解内容= " + componentScan);//2. 通过componentScan的value=> 即要扫描的包String path = componentScan.value();System.out.println("要扫描的包= " + path);}public static void main(String[] args) {SpringApplicationContext springApplicationContext = new SpringApplicationContext(HJSpringConfig.class);}
}

第一步:获取要扫描的包

//1. 先得到HJSpringConfig配置的的@ComponentScan(value = "com.hj.springboot_06_ssmp.component")
ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//注解
System.out.println("注解内容= " + componentScan);//2. 通过componentScan的value=> 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包= " + path);

运行结果:

注解内容= @com.hj.springboot_06_ssmp.annotation.ComponentScan(value=com.hj.springboot_06_ssmp.component)
要扫描的包= com.hj.springboot_06_ssmp.component

第二步:得到要扫描的包下的所有资源(类.class)

//1.得到类的加载器
ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径
path = path.replace(".", "/");//一定要把. 替换成 /
URL resource = classLoader.getResource(path);
System.out.println("resource=" + resource);

运行结果:

resource=file:/F:/hspJava/SpringBoot/springboot/springboot_06_ssmp/target/classes/com/hj/springboot_06_ssmp/component

注意这里得到的资源路径是对应target文件下的:

//3. 将要加载的资源(.class) 路径下的文件进行遍历=>io
File file = new File(resource.getFile());//目录也是一个文件
System.out.println(file.isDirectory());
if(file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println("=======================");System.out.println(f.getAbsolutePath());//F:\hspJava\SpringBoot\springboot\springboot_06_ssmp\target\classes\com\hj\springboot_06_ssmp\component\UserService.class//获取到 com.hj.spring.component.UserServiceString fileAbsolutePath = f.getAbsolutePath();}
}

运行结果:

//这里我们只处理.class文件
if (fileAbsolutePath.endsWith(".class")) {//1. 获取到类名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));System.out.println("className=" + className);//2. 获取类的完整的路径(全类名)//老师解读 path.replace("/",".") => com.hspedu.spring.component.String classFullName = path.replace("/", ".") + "." + className;System.out.println("classFullName=" + classFullName);//3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..try {//这时,我们就得到该类的Class对象//Class clazz = Class.forName(classFullName)//老师说一下//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 可以反射类的Class//3. 区别是 : 上面方式后调用该类的静态方法, 下面方法不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @ComponentClass<?> aClass = classLoader.loadClass(classFullName);if (aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Controller.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class)) {//这里老师演示一个Component注解指定value,分配id//老师就是演示了一下机制.if(aClass.isAnnotationPresent(Component.class)) {//获取到该注解Component component = aClass.getDeclaredAnnotation(Component.class);String id = component.value();if(!"".endsWith(id)) {//不为空className = id;//替换}}//这时就可以反射对象,并放入到容器中Class<?> clazz = Class.forName(classFullName);Object instance = clazz.newInstance();//放入到容器中, 将类名的首字母小写作为id//StringUtilsioc.put(StringUtils.uncapitalize(className) , instance);}} catch (Exception e) {e.printStackTrace();}
}

4. 总代码

/*** @author hj* SpringApplicationContext 类的作用类似Spring原生ioc容器*/
public class SpringApplicationContext {private Class configClass;//ioc我存放的就是通过反射创建的对象(基于注解方式)private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();public SpringApplicationContext(Class configClass) {this.configClass = configClass;//获取要扫描的包//1. 先得到HJSpringConfig配置的的@ComponentScan(value = "com.hj.springboot_06_ssmp.component")ComponentScan componentScan =(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);//注解System.out.println("注解内容= " + componentScan);//2. 通过componentScan的value=> 即要扫描的包String path = componentScan.value();System.out.println("要扫描的包= " + path);//得到要扫描的包下的所有资源(类.class)//1.得到类的加载器ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();//2. 通过类的加载器获取到要扫描的包的资源 url=》类似一个路径path = path.replace(".", "/");//一定要把. 替换成 /URL resource = classLoader.getResource(path);//得到全路径System.out.println("resource=" + resource);System.out.println(resource.getFile());//3. 将要加载的资源(.class) 路径下的文件进行遍历=>ioFile file = new File(resource.getFile());//目录也是一个文件System.out.println(file.isDirectory());if(file.isDirectory()) {File[] files = file.listFiles();for (File f : files) {System.out.println("=======================");System.out.println(f.getAbsolutePath());//F:\hspJava\SpringBoot\springboot\springboot_06_ssmp\target\classes\com\hj\springboot_06_ssmp\component\UserService.class//获取到 com.hj.spring.component.UserServiceString fileAbsolutePath = f.getAbsolutePath();//这里我们只处理.class文件if (fileAbsolutePath.endsWith(".class")) {//1. 获取到类名String className =fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));System.out.println("className=" + className);//2. 获取类的完整的路径(全类名)//老师解读 path.replace("/",".") => com.hspedu.spring.component.String classFullName = path.replace("/", ".") + "." + className;System.out.println("classFullName=" + classFullName);//3. 判断该类是不是需要注入容器, 就看该类是不是有注解 @Component @Service..try {//这时,我们就得到该类的Class对象//Class clazz = Class.forName(classFullName)//老师说一下//1. Class clazz = Class.forName(classFullName) 可以反射加载类//2. classLoader.loadClass(classFullName); 可以反射类的Class//3. 区别是 : 上面方式后调用该类的静态方法, 下面方法不会//4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @ComponentClass<?> aClass = classLoader.loadClass(classFullName);if (aClass.isAnnotationPresent(Component.class) ||aClass.isAnnotationPresent(Controller.class) ||aClass.isAnnotationPresent(Service.class) ||aClass.isAnnotationPresent(Repository.class)) {//这里老师演示一个Component注解指定value,分配id//老师就是演示了一下机制.if(aClass.isAnnotationPresent(Component.class)) {//获取到该注解Component component = aClass.getDeclaredAnnotation(Component.class);String id = component.value();if(!"".endsWith(id)) {//不为空className = id;//替换}}//这时就可以反射对象,并放入到容器中Class<?> clazz = Class.forName(classFullName);Object instance = clazz.newInstance();//放入到容器中, 将类名的首字母小写作为id//StringUtilsioc.put(StringUtils.uncapitalize(className) , instance);}} catch (Exception e) {e.printStackTrace();}}}}}//编写方法返回对容器中对象public Object getBean(String name) {return ioc.get(name);}public static void main(String[] args) {SpringApplicationContext springApplicationContext = new SpringApplicationContext(HJSpringConfig.class);}
}

最后如果你觉得这篇文章对你有帮助的话,请转发、收藏和点赞哦!谢谢支持!

带你简化理解Spring 基于注解配置的原理相关推荐

  1. (spring-第4回【IoC基础篇】)spring基于注解的配置

    (spring-第4回[IoC基础篇])spring基于注解的配置 基于XML的bean属性配置:bean的定义信息与bean的实现类是分离的. 基于注解的配置:bean的定义信息是通过在bean实现 ...

  2. spring 基于注解的控制器配置

    http://ttaale.iteye.com/blog/787586 spring 基于注解的控制器配置 博客分类: spring SpringBeanServletMVCWeb 13.12. 基于 ...

  3. Spring 基于注解的配置

    转载自  Spring 基于注解的配置 基于注解的配置 从 Spring 2.5 开始就可以使用注解来配置依赖注入.而不是采用 XML 来描述一个 bean 连线,你可以使用相关类,方法或字段声明的注 ...

  4. 从源码分析 Spring 基于注解的事务

    从源码分析 Spring 基于注解的事务 在spring引入基于注解的事务(@Transactional)之前,我们一般都是如下这样进行拦截事务的配置: <!-- 拦截器方式配置事务 --> ...

  5. Spring基于注解TestContext 测试框架使用详解

    原创整理不易,转载请注明出处:Spring基于注解TestContext 测试框架使用详解 代码下载地址:http://www.zuidaima.com/share/1775574182939648. ...

  6. 一文带你深入理解 Spring 事务原理

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 关注订阅号「程序员小乐」,收看更多精彩内容 每日英文 Man has to ...

  7. Spring基于注解的方式二

    Spring基于注解二 上一次介绍了很多的关于spring的基本的注解,这篇文章描述一下关于Spring注解的基本的原理,从简单的例子入手 @Configuration @Import({Color. ...

  8. Spring基于注解的方式一

    Spring基于注解的方式一 Spring注解简介 之前的时候我们学习的Spring都是基于Spring配置文件的形式来编写,现在很多的情况下使用SpringBoot的时候是基于注解的形式,这里我们首 ...

  9. Spring基于注解的自动装配

    Spring基于注解的自动装配 基于XML的自动装配是在配置文件的bean里设置autowire属性,有byType,byName的方式.而基于注解的自动装配同样是这样只不过我们直接在成员变量上直接标 ...

最新文章

  1. 《现代教育技术》Win8版发布 项目总结
  2. css中的一些常用选择器
  3. cdlinux miniwdep 配置无线网卡
  4. C++版二叉树非递归遍历
  5. 防止html标签转义
  6. pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org',
  7. 咸鱼3D打印—3D打印的基本流程
  8. python使用requests库爬取淘宝食品信息,包含sign参数破解
  9. Windows删除打开方式多余项
  10. 关于4月2号OpenAI大面积封停亚洲(中国大陆)帐号的问题和应对策略
  11. HDU-6148 Valley Numer(数位DP)
  12. 奖励稀疏_好奇心解决稀疏奖励任务
  13. 软件测试职业规划(转)
  14. 网络基础(四) — QUIC协议
  15. oracle查看日期是第几周,oracle查看日期是第几周-Oracle
  16. 01熵与热力学重要模型
  17. 原型和Axure的作用
  18. 2020C证(安全员)实操考试视频及C证(安全员)在线考试
  19. 测度上Lebesgue积分的确定
  20. visio方向键不能移动对象

热门文章

  1. android自定义秒表,Android实现的秒表计时器示例
  2. 快捷下载中国原创音乐基地音乐(包括金豆和无法下载音乐)
  3. 华为手机怎么语音服务器,原来华为手机实现文字转语音这么简单!今天才知道,真是绝了...
  4. 三电极体系 电化学传感器
  5. 数据库技术之MVCC
  6. 简易的共享交通系统管理系统
  7. python查看excel所有sheetname
  8. 仓库管理系统(warehouse management system)
  9. 【细胞分割】原子力显微镜图像分析【含GUI Matlab源码 1371期】
  10. pyautogui使用经验