模仿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目录结构
package iocdemo; /** * @Author:cpq * @Description: 此类使通过xml配置到IOC中 */ public class Dao1 { public void show(){ System.out.println("Dao1的show方法*******1111****"); } }
package iocdemo.scan; import iocdemo.ComponentCustom; /** * @Author:cpq * @Description: 通过注解加入到IOC容器中 */ //@ComponentCustom(name = "dao222") //@ComponentCustom("dao22") @ComponentCustom public class Dao2 { private String name2; public void show(){ System.out.println("Dao2的show方法*******2222****"); } }
package iocdemo; import iocdemo.scan.Dao2; /** * @Author:cpq * @Description: 此类使通过xml配置到IOC中,并且通过注解@ResourceCustom注入成员变量的值 */ public class Service1 { //@ResourceCustom(name = "dao1") //@ResourceCustom("dao1") @ResourceCustom private Dao1 dao1; //dao1在xml中配置了 //@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; /** * @Author:cpq * @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; /** * @Author:cpq * @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; /** * @Author:cpq * @Description: IOC工具类 */ 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(); //将bean的id、实例化的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()、@ComponentCustom这4种写法。使用@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,若其成员变量使用了@ResourceCustom且IOC中有这个类,则注入,详情请看: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()、@ResourceCustom这4种写法 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(); } }
度盘:https://pan.baidu.com/s/1kWc61PP(不推荐使用)
****:http://download.****.net/download/u010606397/10203730(墙裂推荐使用)