Fork me on GitHub

【JAVA反射】简单实现Spring的IOC+DI

为什么要实现这个功能

闲来无事,就想着玩玩Spring的类注入实现,本文只为了更好的学习和理解Spring的IOC+DI实现原理,不用于项目实践,有不对的欢迎指正,不喜请绕行。

实现目的

在不引入Spring框架的情况下,实现在业务类中自动注入功能接口
不说废话直接来干的!

实现

思路

  • 扫描此包下所有类文件,以获取类全名列表
  • 通过反射将所有标记MyService注解的类放入容器中
  • 再通过反射将对类中标记MyAutowired注解的属性注入实现类,对于一个接口两个实现类情况,此处只是通过名称来获取实现类

创建Annotation

  • MyService 用于注解类
package com.icodesoft.service;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyService {

}
  • MyAutowired 用于注解属性
package com.icodesoft.service;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyAutowired {
    String name();
}

创建功能接口以及实现类

  • 功能接口
package com.icodesoft.service;

public interface IService {
    String getName();
}
  • 实现类user
package com.icodesoft.service;

@MyService
public class UserService implements IService {
    @Override
    public String getName() {
        return "我User Service类已成功注入啦!";
    }
}
  • 实现类proudct
package com.icodesoft.service;

@MyService
public class ProductService implements IService {
    @Override
    public String getName() {
        return "我Product Service类已成功注入啦!";
    }
}

创建业务类

  • 业务类TestService
package com.icodesoft.service;

@MyService
public class TestService {
    @MyAutowired(name = "productService")
    private IService service;

    public String getServiceName() {
        return this.service.getName();
    }
}

到目前为此我们的所有业务类只依赖于功能接口而非实现类,下面我们来实现怎样把功能实现类注入到 TestService 类 的private IService service;属性中

创建类MyFactoryBean,此类我将类容器以及注入功能全放这个类中,看客们可以奖其他分开实现功能单一

package com.icodesoft;

import com.icodesoft.service.MyAutowired;
import com.icodesoft.service.MyService;
import org.apache.commons.lang3.ArrayUtils;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.*;

/**
 * 扫描此包下所有类文件,以获取类全名列表
 * 通过反射将所有标记MyService注解的类放入容器中
 * 再通过反射将对类中标记MyAutowired注解的属性注入实现类
 */
public class MyFactoryBean {

    public static Map<String, Object[]> allBean = new HashMap<>();
    private static String packagePath; // 包全名, 如 com.icodesoft.service

    public static List<String> scan(String packageName) throws Exception {
        packagePath = packageName.replaceAll("\\.", "/");
        List<String> list = null;
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Enumeration<URL> urls = classLoader.getResources(packagePath);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            if (url.getProtocol().equals("file")) {

                // 扫描此包下所有类文件,以获取类全名列表
                list = scanFiles(url.getPath(), packageName);
            }
        }
        System.out.println("*******: " + list);
        // 通过反射将所有标记MyService注解的类放入容器中
        initBeans(list);

        // 再通过反射将对类中标记MyAutowired注解的属性注入实现类
        di();
        return list;
    }

    private static void di() throws Exception {
        for (String className : allBean.keySet()) {
            Class<?> clzz = Class.forName(className);
            if (clzz.isInterface()) continue;
            Object o = allBean.get(className)[0];
            Field[] declaredFields = clzz.getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                if (field.isAnnotationPresent(MyAutowired.class)) {
                    MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                    String name = annotation.name();
                    Class<?> fieldType = field.getType();
                    Object[] beans = allBean.get(fieldType.getName());
                    if (fieldType.isInterface() && beans.length > 1) {
                        if (name == null || name.trim().isEmpty())
                            throw new ClassNotFoundException("当接口有多个实现类时,必须指定name值");
                        for (Object bean : beans) {
                            if (name.equalsIgnoreCase(bean.getClass().getSimpleName())) {
                                field.set(o, bean);
                                break;
                            }
                        }
                    } else {
                        field.set(o, beans[0]);
                    }
                }
            }
        }
    }

    private static void initBeans(List<String> classNames) {
        for (String className : classNames) {
            try {
                Class<?> clzz = Class.forName(className);

                if (clzz.isInterface() || clzz.isAnnotation() || Modifier.isPrivate(clzz.getDeclaredConstructor().getModifiers()))
                    continue;

                if (clzz.isAnnotationPresent(MyService.class)) {
                    Object object = clzz.getDeclaredConstructor().newInstance();
                    Class<?>[] interfaces = clzz.getInterfaces();
                    if (interfaces.length > 0) {
                        for (Class<?> classInterface : interfaces) {
                            if (allBean.containsKey(classInterface.getName())) {
                                Object[] objects = allBean.get(classInterface.getName());
                                allBean.put(classInterface.getName(), ArrayUtils.add(objects, object));
                            } else {
                                allBean.put(classInterface.getName(), new Object[]{object});
                            }
                        }
                    }
                    allBean.put(className, new Object[]{object});
                }

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    // 扫描此包下所有类文件,以获取类全名列表
    private static List<String> scanFiles(String path, String basePkg) {
        File direc = new File(path);
        List<String> classNames = new ArrayList<>();
        File[] files = direc.listFiles();
        if (null == files) {
            return classNames;
        }

        for (int i = 0; i < files.length; ++i) {
            File file = files[i];
            if (file.isDirectory()) {
                List<String> list = scanFiles(file.getAbsolutePath(), basePkg + "." + file.getName());
                classNames.addAll(list);
            } else if (file.getName().endsWith(".class")) {
                String className = file.getName().substring(0, file.getName().lastIndexOf("."));
                if (-1 != className.lastIndexOf("$")) {
                    continue;
                }
                String result = basePkg + "." + className;
                classNames.add(result);
            }
        }
        return classNames;
    }
}

我们运行起来玩一玩看能否成功

public class DemoApplication {
    static {
        try {
            MyFactoryBean.scan("com.icodesoft.service");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception {
        System.out.println(": " + MyFactoryBean.allBean);
        TestService testService = (TestService) MyFactoryBean.allBean.get(TestService.class.getName())[0];
        String serviceName = testService.getServiceName();
        System.out.println("==================>serviceName: " + serviceName);

    }
}

结果

可以看到程序运行正常并已成功注入功能类

posted @ 2020-09-29 18:05  逍遥メ风  阅读(259)  评论(0编辑  收藏  举报