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执行的。通过注解和反射机制,自动为类注入依赖。
Spring——原理解析-利用反射和注解模拟IoC的自动装配的更多相关文章
- Spring原理解析-利用反射和注解模拟IoC的自动装配
- Java反射学习总结终(使用反射和注解模拟JUnit单元测试框架)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 本文是Java反射学习总结系列的最后一篇了,这里贴出之前文章的链接,有兴趣的可以打开看看. ...
- IDEA02 利用Maven创建Web项目、为Web应用添加Spring框架支持、bean的创建于获取、利用注解配置Bean、自动装配Bean、MVC配置
1 环境版本说明 Jdk : 1.8 Maven : 3.5 IDEA : 专业版 2017.2 2 环境准备 2.1 Maven安装及其配置 2.2 Tomcat安装及其配置 3 详细步骤 3.1 ...
- Spring注解驱动开发(三)-----自动装配
自动装配 概念 Spring利用依赖注入(DI),完成对IOC容器中中各个组件的依赖关系赋值. @Autowired-----自动注入 1.默认优先按照类型去容器中找对应的组件 application ...
- Java——利用反射机制将表单数据自动填充到JavaBean中
以一个案例介绍反射机制的一种常见的使用场景,以及具体实现. 1.本文案例 在编写Java Web应用程序时,使用表单提交数据是一个必不可少的环节,后台对于前台使用表单提交的数据需要能够从请求中解析,并 ...
- Spring详解(四)------注解配置IOC、DI
Annotation(注解)是JDK1.5及以后版本引入的.它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查.注解是以‘@注解名’在代码中存在的. 前面讲解 IOC 和 DI 都是通过 ...
- 不懂Ribbon原理的可以进来看看哦,分析SpringBoot自动装配完成了Ribbon哪些核心操作
前面详细的给大家介绍了SpringBoot的核心内容,有了这部分的基础支持的话,我们再来分析SpringCloud中的相关组件就很容器了,本文我们来给大家开始介绍Ribbon的相关内容,首先来介绍 ...
- Spring学习笔记 5. 尚硅谷_佟刚_Spring_自动装配
1,回顾以前的做法 一个人有姓名,有住址,有一辆车.其中住址和车也是一个类,这种情况下不用自动装配是十分容易实现的 (1)Person类 package com.zsq; public class P ...
- 第四章 Spring.Net 如何管理您的类___对象的自动装配
由于这几天都比较忙,所以对笔记暂时没有更新. Spring.NET具有自动装配的能力,也就是说,Spring.NET可以通过对象的定义自动分辨某个对象的协作对象.自动装配是针对单个对象(按:针对每个协 ...
随机推荐
- debezium关于cdc的使用(上)
博文原址:debezium关于cdc的使用(上) 简介 debezium是一个为了捕获数据变更(cdc)的开源的分布式平台.启动并指向数据库,当其他应用对此数据库执行inserts.updates.d ...
- echarts is not defined解决方案
最近在写一个类似于vue的一种cola-ui框架,有兴趣的朋友可以了解一下:http://legacy.cola-ui.com/: 项目中有个业务需要引入Echarts图表的需求,由于是前后端没有分离 ...
- html基础知识(总结自www.runoob.com)
HTML属性 属性 描述 class 为html元素定义一个或多个类名(classname)(类名从样式文件引入) id 定义元素的唯一id style 规定元素的行内样式(inline style) ...
- freemarker的replace的使用
1.replace替换: <#assign name="sdfsfdsa\ndfsafs\n"> 例子:${name?replace("\n",&q ...
- 自动化测试报告之allure使用基础指南
差不多三个月前些的教程,然后跳槽了,自定义模块还没有写....后续也不知道有时间补上没有,最近应该会毕竟专注app测试这块了 1.github下载allure安装包:https://githu ...
- verilog 实现DDS
一.DDS的原理 直接数字频率合成器(DDS),功能是通过输入频率输入字从而实现改变输出信号的频率的功能,它所利用的原理就是虽然对于一段正弦信号来说其幅度值是非线性的,但是其相位的值却是线性增加的,如 ...
- 制作嵌入式linux内核
拿到一个嵌入式linux内核代码,首先make distclean 接下来,如果在x86平台,就直接make menuconfig,如果是在ARM平台,就直接make menuconfig ARCH= ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(17)|装箱crates]
[易学易懂系列|rustlang语言|零基础|快速入门|(17)|装箱crates] 实用知识 装箱crates 我们今天来讲讲装箱技术crates. 什么是crates? 英语翻译是: 英 [kre ...
- package+explorer不显示项目的问题
昨天遇到了这个问题,百度了一下,怎么搞的都有,但是感觉都不理想,晚上的时候才在网上又发现这个方法,今天试了一下,效果还不错,分享一下. 点击 Window ---> Close All Pers ...
- qt类表