模仿spring ioc,实现通过xml、注解配置bean,并可以通过注解注入bean

主要用到自定义注解、反射、dom4j的技术。不了解自定义注解的同学请搜索:深入理解Java:注解(Annotation)基本概念、深入理解Java:注解(Annotation)自定义注解入门、深入理解Java:注解(Annotation)--注解处理器 这3篇文章。

实现思路:

1、定义一个Map<String, Object> beanContainer用来存储bean。

2、xml中配置bean,<bean id="dao1" class="iocdemo.Dao1"></bean>,class属性值是类的全名,可通过反射创建实体类,id值作为key,实体类作为值存储在beanContainer中。

3、使用自定义注解@ComponentCustom标注类,通过 private Map<String, Object> componentBean(String scanPackage)方法扫描后将此类存放在beanContainer中

4、使用自定义注解@ResourceCustom标注成员变量,通过private void injectBean()方法注入实体类。

5、看不懂,没关系,Talk is cheap, I will show you the code.


demo目录结构


模仿spring ioc,实现通过xml、注解配置bean,并可以通过注解注入bean


package iocdemo;

/**
 * @Authorcpq
 * @Description此类使通过xml配置到IOC */
public class Dao1 {
    public void show(){
        System.out.println("Dao1show方法*******1111****");
    }
}

package iocdemo.scan;

import iocdemo.ComponentCustom;

/**
 * @Authorcpq
 * @Description通过注解加入到IOC容器中
 */
//@ComponentCustom(name = "dao222")
//@ComponentCustom("dao22")
@ComponentCustom
public class Dao2 {
    private String name2;
    public void show(){
        System.out.println("Dao2show方法*******2222****");
    }
}

package iocdemo;

import iocdemo.scan.Dao2;

/**
 * @Authorcpq
 * @Description此类使通过xml配置到IOC中,并且通过注解@ResourceCustom注入成员变量的值
 */
public class Service1 {
    //@ResourceCustom(name = "dao1")
    //@ResourceCustom("dao1")
    @ResourceCustom
    private Dao1 dao1;  //dao1xml中配置了

    //@ResourceCustom(name = "dao222")
    //@ResourceCustom("dao22")
    @ResourceCustom
    private Dao2 dao2;  //dao2使用了自定义注解

    public void fieldMethod(){
        dao1.show();
        dao2.show();
    }
}


此xml配置bean

<?xml version="1.0" encoding="UTF-8"?>
<beans>
  <bean id="dao1" class="iocdemo.Dao1"></bean>

  <bean id="service1" class="iocdemo.Service1"></bean>
</beans>


自定义注解,类似于spring中的@component

package iocdemo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Authorcpq
 * @Description自定义注解
 */
//运行时执行
@Retention(RetentionPolicy.RUNTIME)
//用于描述类、接口(包括注解类型) enum声明
@Target({ElementType.TYPE})
public @interface ComponentCustom {
    public String value() default "";
    public String name() default "";
}


自定义注解,类似于JDK的@Resource

package iocdemo;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Authorcpq
 * @Description:自定义注解
 */
//运行时执行
@Retention(RetentionPolicy.RUNTIME)
//适用于成员变量、方法
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface ResourceCustom {
    public String value() default "";
    public String name() default "";
}


工具类,可下载此demo运行public void t()方法测试。

package iocdemo;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.junit.Test;

import java.io.File;
import java.io.FileFilter;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @Authorcpq
 * @DescriptionIOC工具类
 */
public class IOCUtil {
    //ioc容器
    Map<String, Object> beanContainer = new HashMap<String, Object>();

    //使用dom4j读取xml配置的bean,并通过反射实例化bean
    private Map<String, Object> xmlBean(String path){
        try {
            //classLoader.getResourceAsStream默认定位到classpath根目录,因为斜杠/也表示根目录,所以path不能以斜杠/开头。
            InputStream is = this.getClass().getClassLoader().getResourceAsStream(path);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(is);
            Element beans = document.getRootElement();
            for (Iterator<Element> beanList = beans.elementIterator(); beanList.hasNext();){
                Element element = beanList.next();
                //通过bean标签的class属性,用反射实例化bean
                Object clazz = Class.forName(element.attributeValue("class")).newInstance();
                //beanid、实例化的bean放到容器中
                beanContainer.put(element.attributeValue("id"), clazz);
            }
        }catch (Exception e){
            System.out.println("读取xml出错");
        }
        return beanContainer;
    }

    //扫描scanPackage包中的带有@ComponentCustom注解的类
    private Map<String, Object> componentBean(String scanPackage){
        try {
            //获取扫描包路径
            String path = this.getClass().getClassLoader().getResource(scanPackage.replaceAll("\\.","/")).getPath();
            File dir = new File(path);
            //目录不存在、非目录,beanContainer不新增bean,直接返回
            if(!dir.exists() && !dir.isDirectory()){
                return beanContainer;
            }
            //筛选出目录中以class结尾的文件
            File[] files = dir.listFiles(new FileFilter() {
                public boolean accept(File pathname) {
                    return pathname.getName().endsWith(".class");
                }
            });
            for (File file: files){
                //获取文件名,不包含后缀
                String fileName = file.getName().split(".class")[0];
                //反射获取创建类
                Object clazz = Class.forName(String.format("%s.%s", scanPackage, fileName)).newInstance();
                Class classObj = clazz.getClass();
                //类是否有@ComponentCustom注解
                if(classObj.isAnnotationPresent(ComponentCustom.class)){
                    //获取@ComponentCustom注解的参数值
                    ComponentCustom component = clazz.getClass().getAnnotation(ComponentCustom.class);
                    //判断@ComponentCustom(name = "dao2")@ComponentCustom("dao2") @ComponentCustom()@ComponentCustom4种写法。使用@ComponentCustom这种写法时,类名首字母转小写后作为key
                    String name = !"".equals(component.name()) ?
                            component.name() : !"".equals(component.value()) ?
                            component.value() : fileName.substring(0,1).toLowerCase()+fileName.substring(1);
                    //实例化后的类放到容器中
                    beanContainer.put(name, clazz);
                }
            }
        }catch (Exception e){
            System.out.println("扫描目录失败");
        }
        return beanContainer;
    }

    //IOC容器中的bean,若其成员变量使用了@ResourceCustomIOC中有这个类,则注入,详情请看:Service1这个类
    private void injectBean(){
        try {
            for (String key: beanContainer.keySet()){
                Object bean = beanContainer.get(key);
                //获取所有成员变量
                Field[] fields = bean.getClass().getDeclaredFields();
                for (Field field:fields){
                    //判断成员变量是否包含了@ResourceCustom注解
                    if(field.isAnnotationPresent(ResourceCustom.class)){
                        //获取@ResourceCustom注解的参数值
                        ResourceCustom resource = field.getAnnotation(ResourceCustom.class);
                        //判断@ResourceCustom(name = "dao1")@ResourceCustom("dao1") @ResourceCustom()@ResourceCustom4种写法
                        String name = !"".equals(resource.name()) ?
                                resource.name() : !"".equals(resource.value()) ?
                                resource.value() : field.getName();
                        String beanId = name != "" ? name : field.getName();
                        //通过反射给私有成员赋值
                        field.setAccessible(true);
                        field.set(bean, beanContainer.get(beanId));
                    }
                }
            }
        }catch (Exception e){
            System.out.println("bean属性注入失败");
        }
    }

    //获取IOC容器中的bean
    public Object getBean(String id){
        //读取xml中的bean
        xmlBean("iocdemo/bean.xml");
        //扫描包中带有@ComponentCustom注解的bean
        componentBean("iocdemo.scan");
        //使用了@ResourceCustom注解的成员变量注入值(值为实例化后的类)
        injectBean();
        //返回IOC容器中的类
        return beanContainer.get(id);
    }

    //运行此方法测试
    @Test
    public void t(){
        Service1 s = (Service1) getBean("service1");
        s.fieldMethod();
    }
}



demo链接

度盘:https://pan.baidu.com/s/1kWc61PP(不推荐使用)

CSDN:http://download.csdn.net/download/u010606397/10203730(墙裂推荐使用)