Spring——原理解析-利用反射和注解模拟IoC的自动装配
解析Spring的IoC容器基于注解实现的自动装配(自动注入依赖)的原理
1.本文案例
使用注解和反射机制来模拟Spring中IoC的自动装配功能
定义两个注解:@Component,用来标注组件;@Autowired,用来标记需要被织入的属性。
定义一个@Component注解处理器,用来扫描所有组件。
定义一个bean工厂,用来实例化组件。
测试:有两个组件,一个组件被设置到另一个组件的属性中。
2.定义注解
2.1.定义@Component注解
这个注解表示被标注的就是一个组件,将会被容器自动扫描并创建实例
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component {public String id(); }
注解的定义有点类似于接口的定义
注解定义
public @interface Component { }
接口定义
public interface Component { }
区别只是在于interface这个标识符前面有没有@符号。
并且注解的定义,还需要使用到几个原生注解:
@Target(ElementType.TYPE)
这个注解表明自定义的注解Component是用来标记谁的,其中ElementType.TYPE表示这个注解使用来标记类型的,也就是可以标记类、接口等。此外还有FIELD、METHOD等,分别表示用来标记字段、方法等。
@Retention(RetentionPolicy.RUNTIME)
表示这个自定义的注解需要保留到什么时候,如只保留到源码中,编译之后就没有了;或者保留到运行时,就是在运行的时候也一直有。这里设置为运行时。
然后这个注解中有这样一行:
public String id();
有点类似于接口中方法的声明,不过在注解中,这个表示注解的一个属性,后面用到的时候可以看看是怎么使用的,就明白了。
2.2.定义 @Autowired注解
这个注解是一个针对成员变量的注解,使用这个注解则表示,这个字段需要由程序来为其赋值的。
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowire {public String id();}
3.定义 Beanfactory(也就是注解处理器)
自定义注解完之后,实际上并没有什么用处。要想让注解发挥用处,重点在于注解处理器。
首先来明确下这个处理器干了那些事情,首先根据给定的组件的包名,扫描这个包,找出其中所有的被@Component注解标注的类,将类型的信息保存下来。
然后提供一个getBean()方法,允许根据bean的id来获取bean。
接下来看看这个BeanFactory是如何编写的。
3.1.BeanFactory.java
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap;public class BeanFactory {private HashMap<String, Object> beanPool;private HashMap<String, String> components;public BeanFactory(String packageName) {beanPool = new HashMap<>();scanComponents(packageName);}private void scanComponents(String packageName) {components = ComponentScanner.getComponentClassName(packageName);}public Object getBean(String id) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {if (beanPool.containsKey(id)) {return beanPool.get(id);}if (components.containsKey(id)) {Object bean = Class.forName(components.get(id)).newInstance();bean = assemblyMember(bean);beanPool.put(id, bean);return getBean(id);}throw new ClassNotFoundException();}private Object assemblyMember(Object obj) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {Class cl = obj.getClass();for (Field f : cl.getDeclaredFields()) {Autowire at = f.getAnnotation(Autowire.class);if (at != null) {Method setMethod = cl.getMethod("set" + captureName(f.getName()), f.getType());setMethod.invoke(obj, getBean(at.id()));}}return obj;}public static String captureName(String name) {char[] cs=name.toCharArray();cs[0]-=32;return String.valueOf(cs);}}
3.2.ComponentScann.java
这个BeanFactory在构造函数中使用到了一个类,用来扫描出一个包中所有的类的信息。
import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List;public class ComponentScanner {public static HashMap<String, String> getComponentClassName(String packageName) {List<String> classes = getClassName(packageName);HashMap<String, String> components = new HashMap<String, String>();try {for (String cl : classes) {cl = cl.replace("workspace_java.LearningJava.bin.", "");Component comp = Class.forName(cl).getAnnotation(Component.class);if (comp != null) {components.put(comp.id(), cl);}}} catch (ClassNotFoundException e) {// TODO Auto-generated catch block e.printStackTrace();}return components;}public static List<String> getClassName(String packageName) {String filePath = ClassLoader.getSystemResource("").getPath() + packageName.replace(".", "\\"); List<String> fileNames = getClassName(filePath, null);return fileNames;}private static List<String> getClassName(String filePath, List<String> className) { List<String> myClassName = new ArrayList<String>(); File file = new File(filePath); File[] childFiles = file.listFiles(); for (File childFile : childFiles) { if (childFile.isDirectory()) { myClassName.addAll(getClassName(childFile.getPath(), myClassName)); } else { String childFilePath = childFile.getPath(); childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf(".")); childFilePath = childFilePath.replace("\\", "."); myClassName.add(childFilePath); } } return myClassName; }public static void main(String[] args) {getComponentClassName("com.oolong.javase.annotation");}}
4.测试
定义一个模拟的数据库访问接口
@Component(id = "dataAccessInterface") public class DataAccessInterface {public String queryFromTableA() {return "query result";} }
这个类使用了Component这个注解,并且注意,这里使用了这个注解的id属性。
定义一个模拟的业务接口
@Component(id="businessObject") public class BusinessObject {@Autowire(id="dataAccessInterface")private DataAccessInterface dai;public void print() {System.out.println(dai.queryFromTableA());}public void setDai(DataAccessInterface dai) {this.dai = dai;} }
这个接口除了使用@Component这个注解标注之外,还有个成员变量,使用了Autowire这个注解标注。使用这个注解标注,表示这个成员变量的初始化将会交给BeanFactory来进行。
测试
public class BeanFactoryTester {public static void main(String[] args) {BeanFactory beanFactory = new BeanFactory("com.oolong.javase.annotation");BusinessObject obj = (BusinessObject) beanFactory.getBean("businessObject");obj.print();}}
这里使用BeanFactory创建了一个BusinessObject的对象之后,调用这个对象的print方法,最终打印出来一个结果。
而回到这个类的定义中,可以看到:
public void print() {System.out.println(dai.queryFromTableA()); }
这个方法调用的是成员变量dai的queryFromTableA方法。而在这个类中,只有这个成员变量的声明,而没有赋值。
这个赋值又是在哪里进行的呢?
这个就是有我们编写的这个BeanFactory执行的。通过注解和反射机制,自动为类注入依赖。
转载于:https://www.cnblogs.com/weilu2/p/spring_ioc_analysis_principle_bsici_on_reflection_annotation.html
Spring——原理解析-利用反射和注解模拟IoC的自动装配相关推荐
- JavaSE阶段配置文件解析、反射以及注解综合小练习
JavaSE阶段配置文件解析.反射以及注解综合小练习 练习要求 先创建一个项目wzry,在项目中创建一个resource源文件夹,在该文件夹下,有一个 web.properties配置文件.该配置文件 ...
- Spring的bean的注创建、依赖注入、自动装配
一.bean 定义:被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象. bean作用 ...
- 使用基于注解的mybatis时,利用反射和注解生成sql语句
在开发时遇到一个问题,在使用基于注解的mybatis插入一个对象到mysql时,在写sql语句时需要列出对象的所有属性,所以在插入一个拥有10个以上属性的对象时sql语句就会变得很长,写起来也很不方便 ...
- Spring详解(四)------注解配置IOC、DI
Annotation(注解)是JDK1.5及以后版本引入的.它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查.注解是以'@注解名'在代码中存在的. 前面讲解 IOC 和 DI 都是通过 ...
- java spring原理详解,spring原理详解,两大核心IOC和AOP
大家好,我是java梦之旅,一个被Bug耽误了才艺的程序员,专注于Java领域的知识分享和技术交流,每天会给大家带来Java学习的干货教程,喜欢我的同学可以关注我,一起学习,一起加油! 1.概念:sp ...
- Spring IOC容器-自动装配
1 autowire="byName" 根据名称自动装配,自动去IOC容器中找与属性名同名的引用的对象,并自动注入. <!-- ###############自动装配#### ...
- Spring Boot项目利用MyBatis Generator进行数据层代码自动生成
概 述 MyBatis Generator (简称 MBG) 是一个用于 MyBatis和 iBATIS的代码生成器.它可以为 MyBatis的所有版本以及 2.2.0之后的 iBATIS版本自动生成 ...
- Spring Boot Framework的关键组件和内部构造(自动装配、起步依赖、CLI、Actuator)
Spring Boot Framework的关键组件和内部组件 在我之前的文章"Spring Boot简介"中,我们讨论了Spring Boot基础知识.现在我们将讨论" ...
- Spring boot 梳理 - 在bean中使用命令行参数-自动装配ApplicationArguments
If you need to access the application arguments that were passed to SpringApplication.run(-), you c ...
最新文章
- HDLBits答案(22)_基于有限状态机的计数器
- linux es数据库 head,Elasticsearch 5.3.x 使用 Head 插件
- sqlmap md5怎么解密_UC浏览器代理流量解密
- CRNN+CTCLoss中文手写汉字识别
- Data Guard组件等相关介绍
- C#把Xml转换为DataSet的两种方法
- 十一种值得女生交往的男生
- 玩转群晖NAS套件系列一:cloud sync套件的安装与使用保姆级教程!
- vue移动端可以左右滑动的滑块
- mac 修改 hosts 文件之后,刷新 DNS 缓存
- python中返回上一步操作的代码_pycharm最常用的快捷键总结
- CTF压缩包隐写类(zip、RAR、zip伪加密)
- RequestHead详解
- java 首字母小写_Java中属性名首字母大小写问题
- Shell脚本:变量和运算符
- javaWEB——主页面新闻展示删除查看修改主题绑定
- cf768g The Winds Of Winter
- linux 25端口漏洞,Linux通过栈溢出进行提权实战(dpwwn03)
- 怎样学Java才是硬道理
- avplayer学习笔记