解析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的自动装配相关推荐

  1. JavaSE阶段配置文件解析、反射以及注解综合小练习

    JavaSE阶段配置文件解析.反射以及注解综合小练习 练习要求 先创建一个项目wzry,在项目中创建一个resource源文件夹,在该文件夹下,有一个 web.properties配置文件.该配置文件 ...

  2. Spring的bean的注创建、依赖注入、自动装配

    一.bean 定义:被称作 bean 的对象是构成应用程序的支柱也是由 Spring IoC 容器管理的.bean 是一个被实例化,组装,并通过 Spring IoC 容器所管理的对象. bean作用 ...

  3. 使用基于注解的mybatis时,利用反射和注解生成sql语句

    在开发时遇到一个问题,在使用基于注解的mybatis插入一个对象到mysql时,在写sql语句时需要列出对象的所有属性,所以在插入一个拥有10个以上属性的对象时sql语句就会变得很长,写起来也很不方便 ...

  4. Spring详解(四)------注解配置IOC、DI

    Annotation(注解)是JDK1.5及以后版本引入的.它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查.注解是以'@注解名'在代码中存在的. 前面讲解 IOC 和 DI 都是通过 ...

  5. java spring原理详解,spring原理详解,两大核心IOC和AOP

    大家好,我是java梦之旅,一个被Bug耽误了才艺的程序员,专注于Java领域的知识分享和技术交流,每天会给大家带来Java学习的干货教程,喜欢我的同学可以关注我,一起学习,一起加油! 1.概念:sp ...

  6. Spring IOC容器-自动装配

    1 autowire="byName" 根据名称自动装配,自动去IOC容器中找与属性名同名的引用的对象,并自动注入. <!-- ###############自动装配#### ...

  7. Spring Boot项目利用MyBatis Generator进行数据层代码自动生成

    概 述 MyBatis Generator (简称 MBG) 是一个用于 MyBatis和 iBATIS的代码生成器.它可以为 MyBatis的所有版本以及 2.2.0之后的 iBATIS版本自动生成 ...

  8. Spring Boot Framework的关键组件和内部构造(自动装配、起步依赖、CLI、Actuator)

    Spring Boot Framework的关键组件和内部组件 在我之前的文章"Spring Boot简介"中,我们讨论了Spring Boot基础知识.现在我们将讨论" ...

  9. Spring boot 梳理 - 在bean中使用命令行参数-自动装配ApplicationArguments

    If you need to access the application arguments that were passed to SpringApplication.run(-​), you c ...

最新文章

  1. HDLBits答案(22)_基于有限状态机的计数器
  2. linux es数据库 head,Elasticsearch 5.3.x 使用 Head 插件
  3. sqlmap md5怎么解密_UC浏览器代理流量解密
  4. CRNN+CTCLoss中文手写汉字识别
  5. Data Guard组件等相关介绍
  6. C#把Xml转换为DataSet的两种方法
  7. 十一种值得女生交往的男生
  8. 玩转群晖NAS套件系列一:cloud sync套件的安装与使用保姆级教程!
  9. vue移动端可以左右滑动的滑块
  10. mac 修改 hosts 文件之后,刷新 DNS 缓存
  11. python中返回上一步操作的代码_pycharm最常用的快捷键总结
  12. CTF压缩包隐写类(zip、RAR、zip伪加密)
  13. RequestHead详解
  14. java 首字母小写_Java中属性名首字母大小写问题
  15. Shell脚本:变量和运算符
  16. javaWEB——主页面新闻展示删除查看修改主题绑定
  17. cf768g The Winds Of Winter
  18. linux 25端口漏洞,Linux通过栈溢出进行提权实战(dpwwn03)
  19. 怎样学Java才是硬道理
  20. avplayer学习笔记

热门文章

  1. cors解决ajax跨域
  2. .NET开发不可错过的25款必备工具
  3. Hystrix之Dashboard的常见问题
  4. Visual Studio 2017通过SSH支持Git
  5. linux下同步库的创建
  6. DM6467T开发板领航——dmai经验谈
  7. 利用UltraEdit将十六进制转换成ASCII 字符串(调试查看内存有用哦)
  8. mysql常用命令操作
  9. Python文件夹与文件的操作
  10. C语言学习笔记(4)