内容简介:IOC控制反转就是通过反射机制帮我们托管了所有的类。我想要自己实现的就是使用XML注入Bean和使用注解(SpringIOC的XML版本使用Dom4j和反射技术解析XML和注入类
IOC控制反转就是通过反射机制帮我们托管了所有的类。
我想要自己实现的就是使用XML注入Bean和使用注解( @Service
之类的)注入Bean
Spring的Xml版本IOC原理
SpringIOC的XML版本使用Dom4j和反射技术解析XML和注入类
所有的Bean在ApplicationContext创建的时候就会初始化
XML版本注入
自行解析XML
一个自己解析XML的小Demo,使用Dom4j解析XML,如下
public class XmlUtils {
public static void main(String[] args) throws DocumentException {
XmlUtils xmlUtils = new XmlUtils();
xmlUtils.readXml("student.xml");
}
public void readXml(String xmlPath) throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(getResourceAsSteam(xmlPath));
Element rootElement = document.getRootElement();
getNodes(rootElement);
}
private static void getNodes(Element rootElement) {
//获取节点名称
System.out.print("节点名称:" + rootElement.getName()+"\t\t");
//获取节点属性
List<Attribute> attributes = rootElement.attributes();
for (Attribute attribute : attributes) {
System.out.print("属性:"+attribute.getName()+"---"+attribute.getText()+"\t\t");
}
//获取属性值
String value = rootElement.getTextTrim();
if (!StringUtils.isEmpty(value)) {
System.out.print("节点值:" + value+"\t\t");
}
System.out.println();
//遍历子节点
Iterator<Element> elementIterator = rootElement.elementIterator();
while (elementIterator.hasNext()) {
Element next = elementIterator.next();
getNodes(next);
}
}
private InputStream getResourceAsSteam(String xmlPath) {
return this.getClass().getClassLoader().getResourceAsStream(xmlPath);
}
}
复制代码
自己实现XML获取Bean的ApplicationContext
-
实现步骤
- 读取配置XML
- 查看传入的BeanId和Xml中的BeanId是否一致
- 使用反射创建对象并且返回
- 按照上面的步骤实现自己的ApplicationContext 核心方法代码如下(这个getBean方法就是按照上面的步骤实现的,我把步骤的具体实现都抽取出去了)
/** 用于GetBean的方法*/
public Object getBean(String beanId) throws DocumentException, IllegalAccessException, InstantiationException, ClassNotFoundException {
if (StringUtils.isEmpty(beanId)) {
throw new RuntimeException("BeanId为空");
}
//解析Xml,获取所有节点
List<Element> elements = readXml();
if (elements == null||elements.isEmpty()) {
throw new RuntimeException("没有任何Bean信息");
}
//查找对应的ClassName
String className = getClassName(beanId, elements);
if (StringUtils.isEmpty(className)) {
throw new RuntimeException("没有配置类信息");
}
//利用反射机制创建Bean
return newInstance(className);
}
复制代码
全文如下
public class ExtClassPathXmlApplicationContext {
private String xmlPath;
public ExtClassPathXmlApplicationContext(String xmlPath) {
this.xmlPath = xmlPath;
}
/** 用于GetBean的方法*/
public Object getBean(String beanId) throws DocumentException, IllegalAccessException, InstantiationException, ClassNotFoundException {
if (StringUtils.isEmpty(beanId)) {
throw new RuntimeException("BeanId为空");
}
//解析Xml,获取所有节点
List<Element> elements = readXml();
if (elements == null||elements.isEmpty()) {
throw new RuntimeException("没有任何Bean信息");
}
//查找对应的ClassName
String className = getClassName(beanId, elements);
if (StringUtils.isEmpty(className)) {
throw new RuntimeException("没有配置类信息");
}
//利用反射机制创建Bean
return newInstance(className);
}
/**解析Xml文件,获取所有节点*/
private List<Element> readXml() throws DocumentException {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(getResourceAsSteam());
Element rootElement = document.getRootElement();
List<Element> elements = rootElement.elements();
return elements;
}
}
复制代码
然后在主方法里创建上面的Context,使用getBean方法,就可以拿到想要的Bean了(和Spring的ClassPathApplicationContext一样)
使用注解注入Bean
一些需要注意的性质
- 需要把已知带有注解的类装入一个集合里,便于随时取用
- 加载时才初始化上面的集合,还需要注意线程安全问题
- 使用懒加载模式加载Bean
- 只实现了单例,所有getBean都是创建了一个实例
- 只实现了使用默认的beanId进行注入(类名第一个字母小写),不能自定义ID
实现注解装配Bean并且通过getBean方法获取Bean
-
实现步骤
- 使用反射机制,扫包,获取所有的类(使用了一个开源的扫包的 工具 类。。没有自己实现)
- 判断每个类上是否有注入bean的注解
- 使用反射机制进行初始化类
- 按照上面的步骤实现自己的ApplicationContext 核心代码如下
/**初始化Bean容器*/
private void initBeans() throws IllegalAccessException, InstantiationException {
beans = new ConcurrentHashMap<String, Object>();
//使用扫包工具获得包下所有的类
List<Class<?>> classes = ClassUtils.getClasses(packageName);
//判断所有的类上面是否有注解,有的话就会加入到Bean容器里面去
findClassExistAnnotation(classes);
if (beans == null || beans.isEmpty()) {
throw new RuntimeException("没有类加上了注解");
}
}
复制代码
全文如下
public class ExtAnnotationApplicationContext {
private String packageName;
/**保存有Service注解的类*/
private ConcurrentHashMap<String, Object> beans = null;
public ExtAnnotationApplicationContext(String packageName) throws InstantiationException, IllegalAccessException {
this.packageName = packageName;
initBeans();
}
/**初始化Bean容器*/
private void initBeans() throws IllegalAccessException, InstantiationException {
beans = new ConcurrentHashMap<String, Object>();
//使用扫包工具获得包下所有的类
List<Class<?>> classes = ClassUtils.getClasses(packageName);
//判断所有的类上面是否有注解,有的话就会加入到Bean容器里面去
findClassExistAnnotation(classes);
if (beans == null || beans.isEmpty()) {
throw new RuntimeException("没有类加上了注解");
}
}
/**扫包,把有注解的类加入到bean容器里*/
private void findClassExistAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException {
for (Class classInfo : classes) {
//判断是否有注解
Annotation annotation = classInfo.getAnnotation(ExtService.class);
if (annotation != null) {
//到这里表示有这个注解
String className = classInfo.getName();
//默认Id是首字母小写
beans.put(toLowerCaseFirestOne(classInfo.getSimpleName()), newInstance(classInfo));
}
}
}
/**类名的首字母小写*/
private String toLowerCaseFirestOne(String className) {
return new StringBuilder().append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString();
}
/**获取Bean的方法*/
public Object getBean(String beanId) throws IllegalAccessException, InstantiationException {
if (StringUtils.isEmpty(beanId)) {
throw new RuntimeException("BeanID为空");
}
return beans.get(beanId);
}
/**利用反射机制创建Bean*/
private Object newInstance(Class classInfo) throws IllegalAccessException, InstantiationException {
if (classInfo == null) {
throw new RuntimeException("没有这个ID的bean");
}
return classInfo.newInstance();
}
/**依赖注入传入类的属性*/
private void attrAssign(Class<?> classInfo) {
//获取这个类所有的属性
Field[] fields = classInfo.getFields();
//判断当前属性是否有注解
for (Field field : fields) {
ExtService extService = field.getAnnotation(ExtService.class);
if (extService != null) {
//到这里说明这个属性里有这个注解
String fieldName = field.getName();
}
}
}
}
复制代码
现在获取到Bean只用在类上加上自己的Service注解然后使用getBean方法传入类名的首字母小写就可以了
实现自动装配(依赖注入)
-
自动装配/依赖注入原理(实现步骤)
- 使用反射机制获取当前类的所有属性
- 判断当前类是否存在注解
- 使用默认名称在Bean容器里查找对象,然后赋值
- 核心代码
/**自动注入注入这个对象的属性*/
private void attrAssign(Object object) throws IllegalAccessException {
//获取这个类所有的属性
Field[] fields = object.getClass().getDeclaredFields();
//判断当前属性是否有注解
for (Field field : fields) {
ExtService extService = field.getAnnotation(ExtService.class);
if (extService != null) {
//到这里说明这个属性里有这个注解,在从容器里获取对象然后给这个属性赋值
String fieldName = field.getName();
Object target = beans.get(fieldName);
if (target == null) {
throw new RuntimeException("注入\"" + fieldName + "\"属性失败,bean容器里没有这个对象");
}
//允许访问私有属性
field.setAccessible(true);
//第一个参数是这个属性所在的对象
field.set(object,target);
}
}
}
复制代码
这个方法我们需要在Bean容器初始化完成之后,把所有的bean容器的Object里做一遍,达到依赖注入的效果,如下(如果给所有的类都实现注入Bean容器里的bean的话,就是依赖注入 @Autowired
了)
public ExtAnnotationApplicationContext(String packageName) throws InstantiationException, IllegalAccessException {
this.packageName = packageName;
initBeans();
//在所有Bean容器里所有bean自动注入所有的Bean
for (Map.Entry<String, Object> entry : beans.entrySet()) {
System.out.println("beanId:"+entry.getKey());
Object bean = entry.getValue();
attrAssign(bean);
}
}
复制代码
增加了依赖注入的Context全文如下
public class ExtAnnotationApplicationContext {
private String packageName;
/**保存有Service注解的类*/
private ConcurrentHashMap<String, Object> beans = null;
public ExtAnnotationApplicationContext(String packageName) throws InstantiationException, IllegalAccessException {
this.packageName = packageName;
initBeans();
//在所有Bean容器里所有bean自动注入所有的Bean
for (Map.Entry<String, Object> entry : beans.entrySet()) {
System.out.println("beanId:"+entry.getKey());
Object bean = entry.getValue();
attrAssign(bean);
}
}
/**初始化Bean容器*/
private void initBeans() throws IllegalAccessException, InstantiationException {
beans = new ConcurrentHashMap<String, Object>();
//使用扫包工具获得包下所有的类
List<Class<?>> classes = ClassUtils.getClasses(packageName);
//判断所有的类上面是否有注解,有的话就会加入到Bean容器里面去
findClassExistAnnotation(classes);
if (beans == null || beans.isEmpty()) {
throw new RuntimeException("没有类加上了注解");
}
}
/**扫包,把有注解的类加入到bean容器里*/
private void findClassExistAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException {
for (Class classInfo : classes) {
//判断是否有注解
Annotation annotation = classInfo.getAnnotation(ExtService.class);
if (annotation != null) {
//到这里表示有这个注解
String className = classInfo.getName();
//默认Id是首字母小写
beans.put(toLowerCaseFirestOne(classInfo.getSimpleName()), newInstance(classInfo));
}
}
}
/**类名的首字母小写*/
private String toLowerCaseFirestOne(String className) {
return new StringBuilder().append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString();
}
/**获取Bean的方法*/
public Object getBean(String beanId) throws IllegalAccessException, InstantiationException {
if (StringUtils.isEmpty(beanId)) {
throw new RuntimeException("BeanID为空");
}
return beans.get(beanId);
}
/**利用反射机制创建Bean*/
private Object newInstance(Class classInfo) throws IllegalAccessException, InstantiationException {
if (classInfo == null) {
throw new RuntimeException("没有这个ID的bean");
}
return classInfo.newInstance();
}
/**自动注入注入这个对象的属性*/
private void attrAssign(Object object) throws IllegalAccessException {
//获取这个类所有的属性
Field[] fields = object.getClass().getDeclaredFields();
//判断当前属性是否有注解
for (Field field : fields) {
ExtResource extResource = field.getAnnotation(ExtResource.class);
if (extResource != null) {
//允许访问私有属性
field.setAccessible(true);
//到这里说明这个属性里有这个注解,在从容器里获取对象然后给这个属性赋值
String fieldName = field.getName();
Object target = beans.get(fieldName);
if (target == null) {
throw new RuntimeException("注入\"" + fieldName + "\"属性失败,bean容器里没有这个对象");
}
//第一个参数是这个属性所在的对象
field.set(object,target);
}
}
}
}
复制代码
以上所述就是小编给大家介绍的《手写源码(二):自己实现SpringIOC》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 手写源码(四):自己实现Mybatis
- 手写源码(三):自己实现SpringMVC
- 手写源码(一):自己实现Spring事务
- EventBus 源码详细分析:手写 EventBus 核心代码
- 阿里架构师手写Tomcat——Session源码解析
- node进阶——之事无巨细手写koa源码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
算法心得:高效算法的奥秘(原书第2版)
(美)Henry S. Warren, Jr. / 爱飞翔 / 机械工业出版社 / 2014-3 / 89.00
【编辑推荐】 由在IBM工作50余年的资深计算机专家撰写,Amazon全五星评价,算法领域最有影响力的著作之一 Google公司首席架构师、Jolt大奖得主Hoshua Bloch和Emacs合作创始人、C语言畅销书作者Guy Steele倾情推荐 算法的艺术和数学的智慧在本书中得到了完美体现,书中总结了大量高效、优雅和奇妙的算法,并从数学角度剖析了其背后的原理 【读者评价......一起来看看 《算法心得:高效算法的奥秘(原书第2版)》 这本书的介绍吧!