博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring——原理解析-利用反射和注解模拟IoC的自动装配
阅读量:5066 次
发布时间:2019-06-12

本文共 6955 字,大约阅读时间需要 23 分钟。

解析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
beanPool; private HashMap
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
getComponentClassName( String packageName) { List
classes = getClassName(packageName); HashMap
components = new HashMap
(); 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
getClassName(String packageName) { String filePath = ClassLoader.getSystemResource("").getPath() + packageName.replace(".", "\\"); List
fileNames = getClassName(filePath, null); return fileNames; } private static List
getClassName(String filePath , List
className) { List
myClassName = new ArrayList
(); 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

你可能感兴趣的文章
jquery实现限制textarea输入字数
查看>>
thinkphp5 csv格式导入导出(多数据处理)
查看>>
fur168.com 改成5917电影
查看>>
PHP上传RAR压缩包并解压目录
查看>>
Codeforces 719B Anatoly and Cockroaches
查看>>
jenkins常用插件汇总
查看>>
c# 泛型+反射
查看>>
第九章 前后查找
查看>>
Python学习资料
查看>>
多服务器操作利器 - Polysh
查看>>
[LeetCode] Candy
查看>>
Jmeter学习系列----3 配置元件之计数器
查看>>
jQuery 自定义函数
查看>>
jq 杂
查看>>
jquery datagrid 后台获取datatable处理成正确的json字符串
查看>>
作业一
查看>>
AJAX
查看>>
ActiveMQ与spring整合
查看>>
web服务器
查看>>
Git的使用--打tag
查看>>