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可以通过对象的定义自动分辨某个对象的协作对象.自动装配是针对单个对象(按:针对每个协 ...
随机推荐
- PHP高级进阶之路
一:常见模式与框架 学习PHP技术体系,设计模式,流行的框架 常见的设计模式,编码必备 Laravel.ThinkPHP开发必不可少的最新框架 YII.Symfony4.1核心源码剖析 二:微服务架构 ...
- spring-boot 启动图标修改-启动彩蛋
spring boot启动总会显示这样的图标,但是我想不一样 到网上找了一圈,恩,找到一个不错的,做个记录 首先我们在resource目录下面放入一个banner.txt文件,Spring Boot启 ...
- Docker搭建Gitlab服务器
1.使用docker搜索gitlab镜像 docker search gitlab 2.下载镜像: docker pull docker.io/gitlab/gitlab-ce 3.查看docker镜 ...
- Tcp之心跳包
Tcp之心跳包 心跳包 跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着. 事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很 ...
- 16、Nginx Rewrite重写
1.Rewrite基本概述 1.1.什么是rewrite Rewrite主要实现url地址重写, 以及地址重定向,就是将用户请求web服务器的地址重新定向到其他URL的过程. 1.2.Rewrite使 ...
- 第二篇.1、python基础之数据类型与变量
一.变量 1 什么是变量之声明变量 #变量名=变量值 age=18 gender1='male' gender2='female' 2 为什么要有变量 变量作用:“变”=>变化,“量”=> ...
- 使用GDB和GEF进行调试
使用GDB进行调试 这是编译ARM二进制文件和使用GDB进行基本调试的简单介绍.在您按照教程进行操作时,您可能需要按照自己的习惯使用ARM程序集.在这种情况下,你要么需要一个备用的ARM设备,或者你只 ...
- 架构师成长之路5.6-Saltstack配置管理(jinja模板)
点击架构师成长之路 架构师成长之路5.6-Saltstack配置管理(jinja模板) 配置管理工具: Pupper:1. 采用ruby编程语言:2. 安装环境相对较复杂:3.不支持远程执行,需要FU ...
- 时序数据库influxDB存储数据grafana展示数据
一.influxDB简介 InfluxDB是一款用Go语言编写的开源分布式时序.事件和指标数据库,无需外部依赖.该数据库现在主要用于存储涉及大量的时间戳数据,如DevOps监控数据,APP metri ...
- Can you answer these queries? HDU - 4027 (线段树,区间开平方,区间求和)
A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use ...