手撸一个IOC容器
手撸一个IOC容器,完成依赖的注入以及类的查找。
分两步
1、进行类的加载,将带有特殊注解的类注册到容器当中
2、进行依赖的注入,完成带有特定标签的属性的值的自动注入。
一、创建一堆注解,用于表示标识类需要被加载到容器中 @Component 、@Controller、@Service、@Repository
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Repository { } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Service { }
二、创建一个单例BeanContainer,作为IOC容器
/** * 下面是实现一个单例的容器 */ private BeanContainer(){} public static BeanContainer getInstance(){ return ContainerHolder.HOLDER.instance; } //利用枚举的特性,创建单例,可以防止反射和序列化造成多个实例的问题。 private enum ContainerHolder{ HOLDER; private BeanContainer instance; ContainerHolder(){ instance = new BeanContainer(); } }
/** * 存放所有的被标记的目标对象 * 想当与从一个包中查找出的带有特定标签(待注入的类)的类都放在这个MAP当中 * object相当于newInstance创建的实例 */ private final static ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<Class<?>, Object>(); /** * 将需要被处理的注解放到一个列表当中 */ private final static List<Class<? extends Annotation>> beanAnno = new ArrayList<Class<? extends Annotation>>(){{ add( Component.class); add( Controller.class); add( Repository.class); add( Service.class); }
核心代码loadBeans,把包内的类加载到容器当中
/** * 加载所有的Bean * @param packageName */ public void loadBeans(String packageName){ Set<Class<?>> classSet = new HashSet<Class<?>>(); classSet = ClassUtil.extractPackageClass(packageName); if(classSet == null || classSet.isEmpty()){ System.out.println("包下没有任何类"); return; } //如果存在class for (Class<?> clazz : classSet){ //查看某个类型是不是有列表中的注解 for (Class<? extends Annotation> annoClass:beanAnno){ if(clazz.isAnnotationPresent(annoClass)){ beanMap.put(clazz,getNewInstance(clazz,true)); } } } } /** * 获取新实例 * @param clazz * @param accessible * @param <T> * @return */ private <T> T getNewInstance(Class<?> clazz,boolean accessible ) { try{ Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(accessible); return (T)constructor.newInstance(); } catch (Exception ex){ ex.printStackTrace(); throw new RuntimeException(); } }
三、创建一些容器的操作
/** * 添加bean * @param clazz * @param obj * @return */ public Object addBean(Class<?> clazz,Object obj){ return beanMap.put(clazz,obj); } /** * 删除Bean * @param clazz * @return */ public Object removeBean(Class<?> clazz){ return beanMap.remove(clazz); } /** * 获取bean * @param clazz * @return */ public Object getBean(Class<?> clazz){ return beanMap.get(clazz); } /** * 获取所有的bean * @return */ public Set<Object> getBeans(){ return new HashSet<Object>(beanMap.values()); } /** * 获取所有的Classes * @return */ public Set<Class<?>> getClasses(){ return beanMap.keySet(); } /** * 获取指定注解的类 * @param annoClass * @return */ public Set<Class<?>> getClassesByAnno(Class<? extends Annotation> annoClass){ Set<Class<?>> classes = getClasses(); if(classes ==null || classes.isEmpty()){ System.out.println("没找到指定注解的类型"); } Set<Class<?>> classSet = new HashSet<Class<?>>(); for(Class<?> clazz : classes){ if(clazz.isAnnotationPresent(annoClass)){ classSet.add(clazz); } } return classSet; } /** * 获取指定接口的类 * @param interfaceOrClass * @return */ public Set<Class<?>> getClassesByInterfaceOrClass(Class<?> interfaceOrClass){ Set<Class<?>> classes = getClasses(); if(classes ==null || classes.isEmpty()){ System.out.println("没找到指定接口的类型"); } Set<Class<?>> classSet = new HashSet<Class<?>>(); for(Class<?> clazz : classes){ if(clazz.isAssignableFrom(interfaceOrClass)){ classSet.add(clazz); } } return classSet; }
创建一个工具类
public class ClassUtil { public static final String FILE_PROTOCOL = "file"; /** * 获取包下的所有的类的集合 * @param packageName */ public static Set<Class<?>> extractPackageClass(String packageName){ Set<Class<?>> classSet = null; //获取类的加载器,主要为了解决实际路径的问题,包名没办法定位 ClassLoader classLoader = getClassLoader(); //通过类加载器加载资源 URL url = classLoader.getResource(packageName.replace('.', '/')); if(url==null){ System.out.println("资源不存在"); return classSet; } //根据不同的资源类型,通过不同的方式获取资源 //如果是文件协议 if(url.getProtocol().equalsIgnoreCase(FILE_PROTOCOL)){ classSet = new HashSet<Class<?>>(); File packageDir = new File(url.getPath()); extractClassFile(classSet,packageDir,packageName); } return classSet; } /** * 提取类文件(需要进行递归,判断是不是文件夹还是文件) * @param classSet * @param fileSoucre * @param packageName */ private static void extractClassFile(final Set<Class<?>> classSet, File fileSoucre, final String packageName) { //如果是文件的情况 if(!fileSoucre.isDirectory()){ return; } else { //文件夹的情况,列出文件夹下的所有文件 File[] files = fileSoucre.listFiles(new FileFilter() { public boolean accept(File file) { if(file.isDirectory()){ return true; } else { //获取文件的绝对路径 String absolutePath = file.getAbsolutePath(); if(absolutePath.endsWith(".class")){ //class文件直接加载 add2ClassSet(absolutePath); return true; } } return false; } /** * 添加class到classSet当中 * @param absolutePath */ private void add2ClassSet(String absolutePath) { System.out.println(absolutePath); //从绝对路径中获取到包名+类名 absolutePath = absolutePath.replace(File.separator,"."); //根据传进来的packagename,去掉路径中的包名,只得到 String className = absolutePath.substring(absolutePath.indexOf(packageName)); className = className.substring(0,className.lastIndexOf('.')); Class<?> aClass = loadClass(className); classSet.add(aClass); } private Class<?> loadClass(String className) { try{ return Class.forName(className); } catch (Exception ex){ System.out.println("load class error"); throw new RuntimeException(); } } }); if(files!=null){ //遍历每一个文件 for (File f : files){ extractClassFile(classSet,f,packageName); } } } } /** * 获取ClassLoader * @return */ public static ClassLoader getClassLoader(){ //通过这个方式获取ClassLoader实例 return Thread.currentThread().getContextClassLoader(); } }
四、实现依赖注入,创建一个注解@Autowire,限定为Field使用
@Retention(value = RUNTIME) @Target(ElementType.FIELD) public @interface Autowire { /** * 定义一个属性,用于存储默认的实现 * @return */ public String value() default ""; }
创建一个DependcyInjector类,可以实现类中的依赖注入。
public class DependcyInjector { private BeanContainer beanContainer; public DependcyInjector() { //获取容器的单例 beanContainer = BeanContainer.getInstance(); } /** * 执行依赖注入 */ public void doIoc(){ //1、遍历Bean容器当中所有的Class对象 Set<Class<?>> iocClasses = beanContainer.getClasses(); for (Class<?> clazz : iocClasses){ //2、遍历类中的所有的属性 Field[] fields = clazz.getDeclaredFields(); for(Field field : fields){ //3、判断属性是否带有AutoWire注解,然后通过注入该属性 if(field.isAnnotationPresent(Autowire.class)){ //4、获取这些被Autowire注解修饰的类 Class<?> autowireType = field.getType(); //5、获取这个类型的实例,要考虑多个实现的情况如何获取,获取哪一个 String autowireValue = field.getDeclaredAnnotation(Autowire.class).value(); Object fieldInstance = getFieldInstance(autowireType, autowireValue); if(fieldInstance ==null){ System.out.println(MessageFormat.format("注入失败,属性{0}实现类不存在",autowireType.getSimpleName())); } //6、将属性的实现类实例,注入到属性当中 Object classBean = beanContainer.getBean(clazz); //设置值到Field当中 SetField(field,fieldInstance,classBean,true); } } } } /** * 设置属性 */ private void SetField(Field field,Object fieldInstance,Object target,boolean access) { field.setAccessible(access); try{ field.set(target,fieldInstance); }catch (Exception ex){ ex.printStackTrace(); } } /** * 获取属性的实例 * @param fieldClass * @param autowireValue * @return */ private Object getFieldInstance(Class<?> fieldClass,String autowireValue) { //从容器中获取实例 Object filedClassInstance = beanContainer.getBean(fieldClass); if(filedClassInstance !=null){ return filedClassInstance; } else { //可能传入的不是类,而是接口,需要获取接口对应的实现类 Class<?> implementClass = getImplementClass(fieldClass, autowireValue); if(implementClass!=null){ return beanContainer.getBean(implementClass); } return null; } } /** * 获取接口对应的实现类 * @param interfaceClass * @return */ private Class<?> getImplementClass(Class<?> interfaceClass,String autowireValue) { Set<Class<?>> implClasses = beanContainer.getClassesByInterfaceOrClass(interfaceClass); //如果实现类不存在 if(implClasses == null || implClasses.isEmpty()){ System.out.println(MessageFormat.format("接口{0}的实现类型不存在",interfaceClass.getName().toString())); return null; } //如果autowirevalue是默认值,并且实现类只有一个,此时返回对应的默认的实现类 if(autowireValue.equals("") && implClasses.size() ==1){ return implClasses.iterator().next(); } //如果指定了实现类名称 if(!autowireValue.equals("")){ //存在多个 for (Class<?> clazz : implClasses){ if(clazz.getSimpleName().equals(autowireValue)){ return clazz; } } System.out.println(MessageFormat.format("接口{0}存在的实现类中不存在名为{1}的实现",interfaceClass.getName().toString(),autowireValue)); } return null; } }
五、使用测试
创建几个测试用的类
@Component public class User { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } @Service public class Student extends User{ public String getLevel() { return level; } public void setLevel(String level) { this.level = level; } private String level; public String Hello(){ return "HelloWorld"; } }
//学生服务 @Service public class StudentService { @Autowire private Student stu ; public Student getStudent(){ stu.setLevel("2"); stu.setAge(11); stu.setName("123123"); return stu; } }
//Hello服务 @Service public class HelloService { @Autowire private StudentService studentService; public void sayHello(){ System.out.println(studentService.getStudent().getName() + " Hello"); } }
Main方法运行
public static void main(String[]args){ //初始化容器 BeanContainer ioc = BeanContainer.getInstance(); ioc.loadBeans("org.simpleframework.entity"); ioc.loadBeans("org.simpleframework.service"); //实现依赖注入 DependcyInjector injector = new DependcyInjector(); injector.doIoc(); //执行代码 HelloService helloService =(HelloService)ioc.getBean(HelloService.class); helloService.sayHello(); }
执行结果可以看到类都加载到BeanContainer,并且服务内方法也成功调用了:
E:\Projects\myioc\target\classes\org\simpleframework\entity\Student.class E:\Projects\myioc\target\classes\org\simpleframework\entity\User.class E:\Projects\myioc\target\classes\org\simpleframework\service\HelloService.class E:\Projects\myioc\target\classes\org\simpleframework\service\StudentService.class 123123 Hello
总结,可以简单归纳如图