• 【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);
    
        }
    }
    

    结果

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

  • 相关阅读:
    linux下守护进程的创建
    【转】ps命令详解
    【转】适配器模式
    【转】设计模式六大原则(6):开闭原则
    【转】设计模式六大原则(5):迪米特法则
    【转】设计模式六大原则(4):接口隔离原则
    【转】设计模式六大原则(3):依赖倒置原则
    【转】设计模式六大原则(2):里氏替换原则
    网站会不会因为同IP网站被K而受到惩罚
    网页加载速度是否影响网站排名
  • 原文地址:https://www.cnblogs.com/bjxly/p/13751047.html
Copyright © 2020-2023  润新知