用简单代码实现IOC容器

C#写简单的IOC容器 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、IOC和DIP1.IOC2.DIP二、写一个超简单的IOC容器实例1.代码准备2.IOC容器3.调用结果总结 前言 IOC个人之前一直搞不明白,不够理解,写这篇文章也是为了加强学习。 一、IOC和DIP 1.IOC IOC中文叫做控制反转:一般开发,上端都依赖下端的对象,类似于在类中new一个其他类对象。这样上端对下端会有很强的依赖。控制反转就是将对下端对象的依赖转移到第三方容器,能够使程序拥有更好的扩展性。 2.DIP 阅读详情



 

本文作者:加耀            投稿  

手写IOC容器了解一下!

相信每一个java程序员在面试经历中,都被面试官问到过AOP和IOC,用官方的话语来回答AOP和IOC,那就是切面编程和控制反转及依赖注入。

具体什么是IOC呢,IOC(inversion of control)其含义是控制反转,即我们平时通过NEW出来的对象交由IOC来管理,当我们在代码中通过注入注解进行对象标记时,IOC容器会将对应的对象进行属性注入,这样就省去了我们自己NEW的过程,消除了大良耦合代码;

在这里,将对象实例交给IOC容器管理的过程可以称为控制反转,而对被IOC管理的对象中有特殊标识的需要进行注入的属性对象进行连续关联称为依赖注入DI(depend injection 依赖注入);控制反转和依赖注入是相辅相成的,统称为IOC;

在日常编码使用Spring框架时,我们通常会使用注解@Autowried或者是@Resource来标记当前类所需要注入的对象,省去了我们直接NEW的过程,从而减少代码耦合。但如果仅仅是帮我们NEW我们需要的对象,直接称之为注入即可,何来的依赖注入呢。

重点就在这个”依赖”二字上;举个代码中的简单的例子,比如我们在访问控制层注入了服务层的类或者是接口,我们如果是通过new的方式来获取到服务层的类的实例,这样访问控制层中注入的持久层的对象则为null;直接调用则会报错空指针异常;

而依赖注入则是我们在访问控制层注入业务服务层代码时,会将服务层中所注入的接口或者类依次注入,这里面存在一个依赖关系,将依赖的需要注入的类递归注入。这就是依赖注入名称的来源;

就像我们吃饭的时候,当我们需要米饭时,我们可以自己去盛米饭,这个过程是我们主动的;但是如果结合IOC,那么我们只需要告诉服务员,需要米饭,然后服务员就会盛一碗米饭过来,这个动作就交给服务员去做,而省去了自己去盛的这个过程。并且,服务员在上米饭的时候,还将饭碗和筷子一块送过来了,米饭需要用碗来盛,需要用筷子吃,这就是依赖注入的体现;

对于IOC和AOP我也写过相关的文章,如果还不了解的同学不妨先去阅读一下

在手写IOC容器之前,我们需要掌握一些java基础的知识点,分别有:注解、反射、IO流等知识点;我们先来看一下IOC容器的整体流程:

640?wx_fmt=jpeg
image-20190325092707843

首先,我们先创建一个Maven项目,然后在项目的resources目录下添加一个配置文件application.properties,在配置文件中指定需要扫描的包路径

640?wx_fmt=jpeg
image-20190325092746167

然后我们定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注入注解、获取配置文件注解,代码如下:

依赖注入注解@MyAutowired

/** * 类 名: MyAutowired * 描 述: 注入注解--将需要交给IOC容器管理的类放置 -- 定义在属性上的 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)@Documentedpublic @interface MyAutowired {    String value() default "";}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface MyAutowired {

    String value() default "";

}

访问控制层注解@MyController

业务服务层注解@MyService/** * 类 名: MyService * 描 述: 自定义注解 -- 服务层 -- 定义在类、接口、枚举上的 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interface MyService {    String value() default "";}

/**
 * 类 名: MyService
 * 描 述: 自定义注解 -- 服务层 -- 定义在类、接口、枚举上的
 */

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

    String value() default "";

}

数据持久层注解@MyMapping:

/** * 类 名: MyService * 描 述: 自定义注解 -- 数据持久层 -- 定义在类、接口、枚举上的 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documentedpublic @interface MyMapping {    String value() default "";}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface MyMapping {

    String value() default "";

}

配置文件获取注解@Value:

/* 类 名: Value * 描 述: 获取配置文件中的键值对 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)@Documentedpublic @interface Value {    String value() default "";}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface Value {

    String value() default "";

}

定义完注解后,我们可以开始编写代码了。根据上面的流程图,此时应该先获取读取配置文件,从配置文件中获取需要扫描的包路径。

我们先写一个配置文件工具类ConfigurationUtils,代码如下:

/** * 类 名: ConfigurationUtils * 描 述: */public class ConfigurationUtils {    /**     * 项目配置文件信息     */    public static Properties properties;    public ConfigurationUtils(String propertiesPath) {        properties = this.getBeanScanPath(propertiesPath);    }    /**     * @author: JiaYao     * @demand: 读取配置文件     */    private Properties getBeanScanPath(String propertiesPath) {        if (StringUtils.isEmpty(propertiesPath)) {            propertiesPath = "/application.properties";        }        Properties properties = new Properties();        // 通过类的加载器获取具有给定名称的资源        InputStream in = ConfigurationUtils.class.getResourceAsStream(propertiesPath);        try {            System.out.println("正在加载配置文件application.properties");            properties.load(in);            return properties;        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (in != null) {                    in.close();                }            } catch (IOException e) {                e.printStackTrace();            }        }        return properties;    }    /**     * @author: JiaYao     * @demand: 根据配置文件的key获取value的值     */    public static Object getPropertiesByKey(String propertiesKey) {        if (properties.size() > 0) {            return properties.get(propertiesKey);        }        return null;    }}
public class ConfigurationUtils {

    /**
     * 项目配置文件信息
     */

    public static Properties properties;

    public ConfigurationUtils(String propertiesPath) {
        properties = this.getBeanScanPath(propertiesPath);
    }

    /**
     * @author: JiaYao
     * @demand: 读取配置文件
     */

    private Properties getBeanScanPath(String propertiesPath) {
        if (StringUtils.isEmpty(propertiesPath)) {
            propertiesPath = "/application.properties";
        }
        Properties properties = new Properties();
        // 通过类的加载器获取具有给定名称的资源
        InputStream in = ConfigurationUtils.class.getResourceAsStream(propertiesPath);
        try {
            System.out.println("正在加载配置文件application.properties");
            properties.load(in);
            return properties;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return properties;
    }

    /**
     * @author: JiaYao
     * @demand: 根据配置文件的key获取value的值
     */

    public static Object getPropertiesByKey(String propertiesKey) {
        if (properties.size() > 0) {
            return properties.get(propertiesKey);
        }
        return null;
    }

}

上述代码中,我们通过读取配置文件获取到配置文件信息的key-value键值对;然后我们再根据配置文件中指定的扫描包路径进行包扫描

拿到包扫描路径后,我们就可以获取到当前路径下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个Set集合中进行存储。代码如下:

/** * @author: JiaYao * @demand: 类加载器 */private void classLoader() throws ClassNotFoundException, InstantiationException, IllegalAccessException {    // 加载配置文件所有配置信息    new ConfigurationUtils(null);    // 获取扫描包路径    String classScanPath = (String) ConfigurationUtils.properties.get("ioc.scan.path");    if (StringUtils.isNotEmpty(classScanPath)) {        classScanPath = classScanPath.replace(".", "/");    } else {        throw new RuntimeException("请配置项目包扫描路径 ioc.scan.path");    }    // 扫描项目根目录中所有的class文件    getPackageClassFile(classScanPath);    for (String className : classSet) {        addServiceToIoc(Class.forName(className));    }    // 获取带有MyService注解类的所有的带MyAutowired注解的属性并对其进行实例化    Set<String> beanKeySet = iocBeanMap.keySet();    for (String beanName : beanKeySet) {        addAutowiredToField(iocBeanMap.get(beanName));    }}
private void classLoader() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    // 加载配置文件所有配置信息
    new ConfigurationUtils(null);
    // 获取扫描包路径
    String classScanPath = (String) ConfigurationUtils.properties.get("ioc.scan.path");
    if (StringUtils.isNotEmpty(classScanPath)) {
        classScanPath = classScanPath.replace(".", "/");
    } else {
        throw new RuntimeException("请配置项目包扫描路径 ioc.scan.path");
    }
    // 扫描项目根目录中所有的class文件
    getPackageClassFile(classScanPath);
    for (String className : classSet) {
        addServiceToIoc(Class.forName(className));
    }
    // 获取带有MyService注解类的所有的带MyAutowired注解的属性并对其进行实例化
    Set<String> beanKeySet = iocBeanMap.keySet();
    for (String beanName : beanKeySet) {
        addAutowiredToField(iocBeanMap.get(beanName));
    }
}

我们需要扫描项目路径中所有以.class结尾的文件,将其添加到一个全局的Set集合中,代码如下:

/** * 类集合--存放所有的全限制类名 */private Set<String> classSet = new HashSet();/** * @author: JiaYao * @demand: 扫描项目根目录中所有的class文件 */private void getPackageClassFile(String packageName) {    URL url = this.getClass().getClassLoader().getResource(packageName);    File file = new File(url.getFile());    if (file.exists() && file.isDirectory()) {        File[] files = file.listFiles();        for (File fileSon : files) {            if (fileSon.isDirectory()) {                // 递归扫描                getPackageClassFile(packageName + "/" + fileSon.getName());            } else {                // 是文件并且是以 .class结尾                if (fileSon.getName().endsWith(".class")) {                    System.out.println("正在加载: " + packageName.replace("/", ".") + "." + fileSon.getName());                    classSet.add(packageName.replace("/", ".") + "." + fileSon.getName().replace(".class", ""));                }            }        }    } else {        throw new RuntimeException("没有找到需要扫描的文件目录");    }}
private Set<String> classSet = new HashSet();

/**
 * @author: JiaYao
 * @demand: 扫描项目根目录中所有的class文件
 */

private void getPackageClassFile(String packageName) {
    URL url = this.getClass().getClassLoader().getResource(packageName);
    File file = new File(url.getFile());
    if (file.exists() && file.isDirectory()) {
        File[] files = file.listFiles();
        for (File fileSon : files) {
            if (fileSon.isDirectory()) {
                // 递归扫描
                getPackageClassFile(packageName + "/" + fileSon.getName());
            } else {
                // 是文件并且是以 .class结尾
                if (fileSon.getName().endsWith(".class")) {
                    System.out.println("正在加载: " + packageName.replace("/", ".") + "." + fileSon.getName());
                    classSet.add(packageName.replace("/", ".") + "." + fileSon.getName().replace(".class", ""));
                }
            }
        }
    } else {
        throw new RuntimeException("没有找到需要扫描的文件目录");
    }
}

我们将所有的类的字节码对象都存储到一个全局的Set集合中之后,遍历这个set集合,获取在类上有指定注解的类,并将其交给IOC容器;我们先定义一个安全的Map用来存储这些对象

/** * IOC容器 如: String(loginController) --> Object(loginController实例) */private Map<String, Object> iocBeanMap = new ConcurrentHashMap(32);/** * @author: JiaYao * @demand: 控制反转 */private void addServiceToIoc(Class classZ) throws IllegalAccessException, InstantiationException {    // 预留位置,之后优化    if (classZ.getAnnotation(MyController.class) != null) {        iocBeanMap.put(toLowercaseIndex(classZ.getSimpleName()), classZ.newInstance());        System.out.println("控制反转访问控制层:" + toLowercaseIndex(classZ.getSimpleName()));    } else if (classZ.getAnnotation(MyService.class) != null) {        // 将当前类交由IOC管理        MyService myService = (MyService) classZ.getAnnotation(MyService.class);        iocBeanMap.put(StringUtils.isEmpty(myService.value()) ? toLowercaseIndex(classZ.getSimpleName()) : toLowercaseIndex(myService.value()), classZ.newInstance());        System.out.println("控制反转服务层:" + toLowercaseIndex(classZ.getSimpleName()));    } else if (classZ.getAnnotation(MyMapping.class) != null) {        MyMapping myMapping = (MyMapping) classZ.getAnnotation(MyMapping.class);        iocBeanMap.put(StringUtils.isEmpty(myMapping.value()) ? toLowercaseIndex(classZ.getSimpleName()) : toLowercaseIndex(myMapping.value()), classZ.newInstance());        System.out.println("控制反转持久层:" + toLowercaseIndex(classZ.getSimpleName()));    }}/** * @author: JiaYao * @demand: 类名首字母转小写 */public static String toLowercaseIndex(String name) {    if (StringUtils.isNotEmpty(name)) {        return name.substring(0, 1).toLowerCase() + name.substring(1, name.length());    }    return name;}
private Map<String, Object> iocBeanMap = new ConcurrentHashMap(32);

/**
 * @author: JiaYao
 * @demand: 控制反转
 */

private void addServiceToIoc(Class classZ) throws IllegalAccessException, InstantiationException {
    // 预留位置,之后优化
    if (classZ.getAnnotation(MyController.class) != null) {
        iocBeanMap.put(toLowercaseIndex(classZ.getSimpleName()), classZ.newInstance());
        System.out.println("控制反转访问控制层:" + toLowercaseIndex(classZ.getSimpleName()));
    } else if (classZ.getAnnotation(MyService.class) != null) {
        // 将当前类交由IOC管理
        MyService myService = (MyService) classZ.getAnnotation(MyService.class);
        iocBeanMap.put(StringUtils.isEmpty(myService.value()) ? toLowercaseIndex(classZ.getSimpleName()) : toLowercaseIndex(myService.value()), classZ.newInstance());
        System.out.println("控制反转服务层:" + toLowercaseIndex(classZ.getSimpleName()));
    } else if (classZ.getAnnotation(MyMapping.class) != null) {
        MyMapping myMapping = (MyMapping) classZ.getAnnotation(MyMapping.class);
        iocBeanMap.put(StringUtils.isEmpty(myMapping.value()) ? toLowercaseIndex(classZ.getSimpleName()) : toLowercaseIndex(myMapping.value()), classZ.newInstance());
        System.out.println("控制反转持久层:" + toLowercaseIndex(classZ.getSimpleName()));
    }
}

/**
 * @author: JiaYao
 * @demand: 类名首字母转小写
 */

public static String toLowercaseIndex(String name) {
    if (StringUtils.isNotEmpty(name)) {
        return name.substring(0, 1).toLowerCase() + name.substring(1, name.length());
    }
    return name;
}

然后我们再遍历这个IOC容器,获取到每一个类的实例,判断里面是有有依赖其他的类的实例,即依赖注入,代码如下:

     /**     * @author: JiaYao     * @demand: 依赖注入     */    private void addAutowiredToField(Object obj) throws IllegalAccessException, InstantiationException, ClassNotFoundException {        Field[] fields = obj.getClass().getDeclaredFields();        for (Field field : fields) {            if (field.getAnnotation(MyAutowired.class) != null) {                field.setAccessible(true);                MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);                Class<?> fieldClass = field.getType();                // 接口不能被实例化,需要对接口进行特殊处理获取其子类,获取所有实现类                if (fieldClass.isInterface()) {                    // 如果有指定获取子类名                    if (StringUtils.isNotEmpty(myAutowired.value())) {                        field.set(obj, iocBeanMap.get(myAutowired.value()));                    } else {                        List<Object> list = findSuperInterfaceByIoc(field.getType());                        if (list != null && list.size() > 0) {                            if (list.size() > 1) {                                throw new RuntimeException(obj.getClass() + "  注入接口 " + field.getType() + "   失败,请在注解中指定需要注入的具体实现类");                            } else {                                field.set(obj, list.get(0));                                // 递归依赖注入                                addAutowiredToField(field.getType());                            }                        } else {                            throw new RuntimeException("当前类" + obj.getClass() + "  不能注入接口 " + field.getType().getClass() + "  , 接口没有实现类不能被实例化");                        }                    }                } else {                    String beanName = StringUtils.isEmpty(myAutowired.value()) ? toLowercaseIndex(field.getName()) : toLowercaseIndex(myAutowired.value());                    Object beanObj = iocBeanMap.get(beanName);                    field.set(obj, beanObj == null ? field.getType().newInstance() : beanObj);                    System.out.println("依赖注入" + field.getName());//                递归依赖注入                }                addAutowiredToField(field.getType());            }            if (field.getAnnotation(Value.class) != null) {                field.setAccessible(true);                Value value = field.getAnnotation(Value.class);                field.set(obj, StringUtils.isNotEmpty(value.value()) ? getPropertiesByKey(value.value()) : null);                System.out.println("注入配置文件  " + obj.getClass() + " 加载配置属性" + value.value());            }        }    }
    private void addAutowiredToField(Object obj) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(MyAutowired.class) != null) {
                field.setAccessible(true);
                MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
                Class<?> fieldClass = field.getType();
                // 接口不能被实例化,需要对接口进行特殊处理获取其子类,获取所有实现类
                if (fieldClass.isInterface()) {
                    // 如果有指定获取子类名
                    if (StringUtils.isNotEmpty(myAutowired.value())) {
                        field.set(obj, iocBeanMap.get(myAutowired.value()));
                    } else {
                        List<Object> list = findSuperInterfaceByIoc(field.getType());
                        if (list != null && list.size() > 0) {
                            if (list.size() > 1) {
                                throw new RuntimeException(obj.getClass() + "  注入接口 " + field.getType() + "   失败,请在注解中指定需要注入的具体实现类");
                            } else {
                                field.set(obj, list.get(0));
                                // 递归依赖注入
                                addAutowiredToField(field.getType());
                            }
                        } else {
                            throw new RuntimeException("当前类" + obj.getClass() + "  不能注入接口 " + field.getType().getClass() + "  , 接口没有实现类不能被实例化");
                        }
                    }
                } else {
                    String beanName = StringUtils.isEmpty(myAutowired.value()) ? toLowercaseIndex(field.getName()) : toLowercaseIndex(myAutowired.value());
                    Object beanObj = iocBeanMap.get(beanName);
                    field.set(obj, beanObj == null ? field.getType().newInstance() : beanObj);
                    System.out.println("依赖注入" + field.getName());
//                递归依赖注入
                }
                addAutowiredToField(field.getType());
            }
            if (field.getAnnotation(Value.class) != null) {
                field.setAccessible(true);
                Value value = field.getAnnotation(Value.class);
                field.set(obj, StringUtils.isNotEmpty(value.value()) ? getPropertiesByKey(value.value()) : null);
                System.out.println("注入配置文件  " + obj.getClass() + " 加载配置属性" + value.value());
            }
        }
    }

上述代码中,我们通过判断带指定注解的类里面是否有注入其它的类,然后进行递归注入;但是有一个问题,接口和抽象类不能被实例化,所以在处理接口时,就出现了一个难题。通常我们习惯注入接口,但是接口不能被实例化,我们需要对接口赋值它的子类,如何获取到接口的实现类呢?

翻遍了JDK1.8的API,没有找到能够提供这样的方法。于是这里做了写了一个循环,遍历IOC容器中的每一个类是否有实现接口,如果是相同的接口则记录,但是这样做会非常消耗性能的,其代码如下:

/** * @author: JiaYao * @demand: 判断需要注入的接口所有的实现类 */private List<Object> findSuperInterfaceByIoc(Class classz) {    Set<String> beanNameList = iocBeanMap.keySet();    ArrayList<Object> objectArrayList = new ArrayList<>();    for (String beanName : beanNameList) {        Object obj = iocBeanMap.get(beanName);        Class<?>[] interfaces = obj.getClass().getInterfaces();        if (useArrayUtils(interfaces, classz)) {            objectArrayList.add(obj);        }    }    return objectArrayList;}
private List<Object> findSuperInterfaceByIoc(Class classz) {
    Set<String> beanNameList = iocBeanMap.keySet();
    ArrayList<Object> objectArrayList = new ArrayList<>();
    for (String beanName : beanNameList) {
        Object obj = iocBeanMap.get(beanName);
        Class<?>[] interfaces = obj.getClass().getInterfaces();
        if (useArrayUtils(interfaces, classz)) {
            objectArrayList.add(obj);
        }
    }
    return objectArrayList;
}

对于接口的注入,暂时还没有想到有什么好的方式可以优化,也不知道Spring是怎么做的。当一个接口有多个实现类时,需要用过自定义名称进行交给IOC管理和注入注解进行获取。

截至到这里,我们就完成了整个IOC容器的创建以及依赖注入功能了。我们可以写一个简单的测试类来试一下我们写的这个IOC容器;

测试代码:访问控制层

@MyControllerpublic class LoginController {    @Value(value = "ioc.scan.pathTest")    private String test;    @MyAutowired(value = "test")    private LoginService loginService;    public String login() {        return loginService.login();    }}
public class LoginController {

    @Value(value = "ioc.scan.pathTest")
    private String test;

    @MyAutowired(value = "test")
    private LoginService loginService;

    public String login() {
        return loginService.login();
    }

}

测试代码:业务服务接口层:

public interface LoginService {    String login();}interface LoginService {

    String login();
}

测试代码:具体服务层(这里尝试了写两个实现类,多态情况下)

@MyService(value = "test")public class LoginServiceImpl implements LoginService {    @MyAutowired    private LoginMapping loginMapping;    @Override    public String login() {        return loginMapping.login();    }}@MyServicepublic class TestLoginServiceImpl implements LoginService {    @Override    public String login() {        return "测试多态情况下依赖注入";    }}"test")
public class LoginServiceImpl implements LoginService {

    @MyAutowired
    private LoginMapping loginMapping;

    @Override
    public String login() {
        return loginMapping.login();
    }
}

@MyService
public class TestLoginServiceImpl implements LoginService {

    @Override
    public String login() {
        return "测试多态情况下依赖注入";
    }
}

测试代码:数据持久层接口层

public interface LoginMapping {    String login();}interface LoginMapping {

    String login();
}

测试代码:数据持久层具体持久层

@MyMappingpublic class LoginMappingImpl implements LoginMapping {    @Override    public String login() {        return "项目启动成功";    }}
public class LoginMappingImpl implements LoginMapping {

    @Override
    public String login() {
        return "项目启动成功";
    }
}

然后我们写一个启动类PlatformApplication

/** * 类 名: PlatformApplication * * @author: jiaYao */public class PlatformApplication {    public static void main(String[] args) throws Exception {        // 从容器中获取对象(自动首字母小写)        MyApplicationContext applicationContext = new MyApplicationContext();        LoginController loginController = (LoginController) applicationContext.getIocBean("LoginController");        String login = loginController.login();        System.out.println(login);    }}
public class PlatformApplication {

    public static void main(String[] args) throws Exception {
        // 从容器中获取对象(自动首字母小写)
        MyApplicationContext applicationContext = new MyApplicationContext();
        LoginController loginController = (LoginController) applicationContext.getIocBean("LoginController");
        String login = loginController.login();
        System.out.println(login);
    }

}

启动程序后我们通过断点观察一下代码情况

640?wx_fmt=jpeg
image-20190325093910145

控制台输出日志信息如下:

正在加载配置文件application.properties正在加载: cn.jiayao.platform.common.MyApplicationContext.class正在加载: cn.jiayao.platform.config.ConfigurationUtils.class正在加载: cn.jiayao.platform.core.annotation.MyAutowired.class正在加载: cn.jiayao.platform.core.annotation.MyController.class正在加载: cn.jiayao.platform.core.annotation.MyMapping.class正在加载: cn.jiayao.platform.core.annotation.MyService.class正在加载: cn.jiayao.platform.core.annotation.Value.class正在加载: cn.jiayao.platform.modular.controller.LoginController.class正在加载: cn.jiayao.platform.modular.dao.impl.LoginMappingImpl.class正在加载: cn.jiayao.platform.modular.dao.LoginMapping.class正在加载: cn.jiayao.platform.modular.service.impl.LoginServiceImpl.class正在加载: cn.jiayao.platform.modular.service.impl.TestLoginServiceImpl.class正在加载: cn.jiayao.platform.modular.service.LoginService.class正在加载: cn.jiayao.platform.PlatformApplication.class正在加载: cn.jiayao.platform.utils.MyArrayUtils.class控制反转访问控制层:loginController控制反转持久层:loginMappingImpl控制反转服务层:loginServiceImpl控制反转服务层:testLoginServiceImpl注入配置文件  class cn.jiayao.platform.modular.controller.LoginController 加载配置属性ioc.scan.pathTest项目启动成功
正在加载: cn.jiayao.platform.config.ConfigurationUtils.class
正在加载: cn.jiayao.platform.core.annotation.MyAutowired.class
正在加载: cn.jiayao.platform.core.annotation.MyController.class
正在加载: cn.jiayao.platform.core.annotation.MyMapping.class
正在加载: cn.jiayao.platform.core.annotation.MyService.class
正在加载: cn.jiayao.platform.core.annotation.Value.class
正在加载: cn.jiayao.platform.modular.controller.LoginController.class
正在加载: cn.jiayao.platform.modular.dao.impl.LoginMappingImpl.class
正在加载: cn.jiayao.platform.modular.dao.LoginMapping.class
正在加载: cn.jiayao.platform.modular.service.impl.LoginServiceImpl.class
正在加载: cn.jiayao.platform.modular.service.impl.TestLoginServiceImpl.class
正在加载: cn.jiayao.platform.modular.service.LoginService.class
正在加载: cn.jiayao.platform.PlatformApplication.class
正在加载: cn.jiayao.platform.utils.MyArrayUtils.class
控制反转访问控制层:loginController
控制反转持久层:loginMappingImpl
控制反转服务层:loginServiceImpl
控制反转服务层:testLoginServiceImpl
注入配置文件  class cn.jiayao.platform.modular.controller.LoginController 加载配置属性ioc.scan.pathTest
项目启动成功

这样,一个简单的IOC容器就创建完成了;

但是此处还有几点需要再次优化一下,目前对于Maven中引用的jar包无法进行注入,因为没有将jar包中的对象交给IOC容器管理,然后就是对接口的注入,由于接口的特殊性,不能被实例化,如何高效的获取到接口的实现类这个问题还待优化;

Gitlab地址:

最后

乐于输出干货的Java技术公众号:Java3y。公众号内有200多篇原创技术文章、海量视频资源、精美脑图,不妨来关注一下!

640?wx_fmt=jpeg

有帮助?在看!转发! 640?


推荐阅读:

Claude Code长期记忆:优雅的上下文管理 ClaudeCode的上下文管理系统采用三层架构设计,通过智能压缩和优化策略在记忆容量与性能间取得平衡。系统包含短期记忆(高速工作区)、中期记忆(智能蒸馏器)和长期记忆(持久化知识库)。当Token使用率达到92%阈值时启动wU2压缩器,采用8段式结构化摘要保留关键信息,并配合智能文件恢复和渐进式预警机制。该系统通过分层处理、优雅降级和透明化交互,为AI应用提供了高效可靠的上下文管理方案,展现了在有限资源下实现智能记忆的艺术。 阅读详情

相关推荐

kubernetes rook 安装 cephfs

ceph root kubernete osd uuid partuuid

天津托的博客 1543

.net core没有接口的服务注入

.net core中依赖注入的三种生命周期 要实现没有接口的依赖注入,首先当然是要了解依赖注入是什么了。网上资料还是很多的,这里不详细写了,具体写一下没有写接口,但又想实现依赖注入,怎么的具体实现。 首先要知道生命周期.net core提供了三种生命周期,大部分默认情况下基本上是第二种。 public enum ScopeEnum { // 整个应用程序生命周期中只有1个实例 Singleton, //...

ab31ab的博客 1243

ArcGIS 10.2.2自带Python 2.7.5环境配置全攻略:从pip安装到gdal、arcpy调用

本文详细指导如何在ArcGIS 10.2.2自带的Python 2.7.5环境中进行高效配置。核心内容包括安装pip包管理器、部署gdal等关键地理数据处理库,并确保arcpy模块的无缝调用。通过解决路径配置与PyCharm集成问题,帮助用户在此经典环境中构建稳定、兼容的地理信息自动化工作流,有效连接ArcGIS桌面功能与Python脚本编程。

moon9的博客 767

Spring IOC的基本概念,IoC容器的实现方式有哪些?

Spring IOC的基本概念,IoC容器的实现方式有哪些?

javamyfriend的博客 579

自制JS某课脚本

根据实际需求自制网页讯网刷题脚本,绕过人脸,弹窗,答题,实现自动化运行

2301_81243054的博客 3459

java实现mavlink协议解析与封包上传数据,实现mavlink数据交互,附带代码、详解

本篇文章介绍了使用java来进行mavlink协议解析,封包与解包的过程,并附带源码供大家使用。MAVLink(Micro Air Vehicle Link,微型空中飞行器链路通讯协议)是无人飞行器与地面站(Ground Control Station ,GCS)或云端服务器之间通讯,以及无人飞行器之间通讯最常用的协议。它已经在PX4、APM、PIXHAWK和Parrot AR.Drone飞控平台上进行了大量测试。

qq_30279827的博客 1449

IOC基础

组件必须自己使用容器提供的API来查找资源和协作对象,控制反转仅体现在那些回调方法上,容器调用这些回调方法,从而应用代码获取到资源。通常,我们实例化一个对象时,都是使用类的构造方法来new一个对象,这个过程是由我们自己来控制的,而控制反转就把new对象的工作交给了Spring容器。第三、具体到IOC框架产品(Spring)来讲,需要进行大量的配制工作,比较繁琐,对于一些小的项目而言,客观上也可能加大一些工作成本。ioc是把需要使用的对象提前创建好,放到spring的容器里面。......

Angel__wings的博客 2695

秦时明月6.2魔改版_从零到一部署指南_含安卓客户端配置与GM后台直通

本文详细介绍了秦时明月6.2魔改版的从零到一部署指南,包括环境准备、服务端安装、安卓客户端配置以及GM后台直通等关键步骤。特别针对安卓客户端配置和GM工具使用提供了实用技巧,帮助用户快速搭建并优化游戏服务器,确保稳定运行。

weixin_42522964的博客 315

如何实现一个springioc容器

如何实现一个springioc容器

efggfxfvhh的博客 1188

3.1使用Python读取excel的二维数据

要使用 Python 读取 Excel 的二维数据,可以使用 openpyxl 或 pandas 库。

kenshin1980的专栏 1032

PINN:当深度学习遇上朗伯-比尔定律

光谱数据处理中引入PINN的三种架构设计

qq_53529450的博客 154

ioc容器的实现方法有哪些_spring--ioc详细介绍

spring主要学习AOP和IOC概念IOC :inverse of control 控制反转或者依赖注入控制反转:从主动实例化对象,转换到被动获取对象(spring用的反射技术实现)依赖注入:需要什么对象,容器就注入什么对象ioc主要是用来实现对象装配的,比如controller调用service,controller中的service对象要来自容器,也就是容器要把service对象装配到con...

weixin_39792751的博客 725

【图像超分】论文复现:Pytorch实现ESPCN

论文精读:由于ESPCN没有公布源码,我们选择github上一个高star的项目来复现。准备数据集,以及数据预处理搭建网络模型设置参数并训练测试训练好的模型用训练好的模型测试自己的数据本文我们使用pycharm链接服务器训练。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1万+

ioc容器的实现方法有哪些_用简单代码实现IOC容器

本文作者:加耀 投稿手写IOC容器了解一下!相信每一个java程序员在面试经历中,都被面试官问到过AOP和IOC,用官方的话语来回答AOP和IOC,那就是切面编程和控制反转及依赖注入。具体什么是IOC呢,IOC(inversion of control)其含义是控制反转,即我们平时通过NEW出来的对象交由IOC来管理,当我们在代码中通过注入注解进行对象标记时,I...

weixin_39726971的博客 437

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

java容器代码示例_用java的spring实现一个简单的IOC容器示例代码

用java的spring实现一个简单的IOC容器示例代码发布时间:2020-09-20 20:39:08来源:脚本之家阅读:75作者:我叫了了要想深入的理解IOC的技术原理,没有什么能比的上我们自己实现它。这次我们一起实现一个简单IOC容器。让大家更容易理解Spring IOC的基本原理。这里会涉及到一些java反射的知识,如果有不了解的,可以自己去找些资料看看。注意在上一篇文章,我说,启动IOC...

weixin_34564735的博客 104

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

java容器应用实例_用java的spring实现一个简单的IOC容器示例代码

要想深入的理解IOC的技术原理,没有什么能比的上我们自己实现它。这次我们一起实现一个简单IOC容器。让大家更容易理解Spring IOC的基本原理。这里会涉及到一些java反射的知识,如果有不了解的,可以自己去找些资料看看。注意在上一篇文章,我说,启动IOC容器时,Spring会将xml文件里面配置的bean扫描并实例化,其实这种说法不太准确,所以我在这里更正一下,xml文件里面配置的非单利模式的...

weixin_36220492的博客 171

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

IOC容器的理解和原生代码实现简单容器

##IOC容器的理解和原生代码实现简单容器 IOC是什么 IOC :Inversion of Control,中文含义就是控制反转,IOC不是一种技术,而是一种思想,通过这种思想,我们可以设计出低耦合,灵活性高,扩展性高的程序。 1.控制正转 既然提到控制反转,那么就有控制正转。所谓的控制正转,就是我们传统的程序设计的方式:在对象内部通过new的方式创建对象,即我们主动去创建程序的依赖对象。下面我们通过简单的示例代码表现。 <?php interface Pay { function doPay

uniquenessss的博客 345

java的简单容器例子代码解析_用java的spring实现一个简单的IOC容器示例代码

要想深入的理解ioc的技术原理,没有什么能比的上我们自己实现它。这次我们一起实现一个简单ioc容器。让大家更容易理解spring ioc的基本原理。这里会涉及到一些java反射的知识,如果有不了解的,可以自己去找些资料看看。注意在上一篇文章,我说,启动ioc容器时,spring会将xml文件里面配置的bean扫描并实例化,其实这种说法不太准确,所以我在这里更正一下,xml文件里面配置的非单利模式的...

weixin_34338504的博客 115

Spring Framework(8)-两百行代码实现简单IoC容器

文章目录一.前言二.时序图三.代码实现 一.前言 Spring中有两大重要功能,一是AOP(面向切面编程),主要使用的是动态代理技术 Java基础(9)-反射与动态代理 二是IOC(控制反转)技术。控制反转即将对象的创建权交给Spring IOC容器来管理,开发者只需要注重顶层类的创建,不必理会底层类的各种依赖创建关系,而是将它们交由Spring IOC来创建。 本文将使用 反射技术 + dom4...

BoringError的博客 972

Java使用400行代码实现简单的Spring IOC容器(注意DOM4j工具类代码不做统计)

1、定义Bean结构封装类 /** * <p>Title: Bean.java</p> * <p>Description: </p> * <p>Copyright: Copyright (c) 2019</p> * <p>Company: www.nospring.com</p> *...

nosprings的博客 321

如何使用Java编写代码,实现一个简单的Spring IoC容器并实现Bean的创建和管理?

在BeanFactory类中实现getBean()方法,根据Bean名称获取Bean的定义对象,然后根据定义对象的信息实例化Bean对象并设置其属性。在BeanFactory类中实现读取Bean配置文件的方法,将Bean定义存储到Map中。实现一个简单的Bean配置文件解析器,将Bean配置文件转化为Bean的定义。创建一个BeanFactory类,用于管理Bean的创建和获取。实现BeanFactory接口,定义获取Bean的方法。定义一个Bean类,包括Bean的属性和方法。

wgq2020的博客 217

Spring 源码解读:手动实现IoC容器的基本概念与职责

本篇文章将带你从零开始手动实现一个自定义IoC容器,理解IoC的基本概念和工作原理,并与Spring源码进行对比。而IoC则通过将对象的创建和管理交给容器,从而实现了控制的反转。我们的自定义IoC容器实现虽然简单,但它帮助我们理解了Spring IoC容器的基本原理。在这一部分,我们将详细解读Spring中与我们自定义IoC容器功能对应的源码,帮助你理解Spring是如何实现这些功能的。为了更好地理解IoC容器的工作原理,我们将手动实现一个简单的IoC容器。来管理Bean的生命周期和依赖注入的。

捕风捉你 · 星桥API接入与大模型实战 1145

从0到1实现IOC

我们先来看看spring官网对IOC的定义:injectspattern.就不逐句翻译了,主要意思就是,IOC也被称为依赖注入,它是一个容器创建bean时将依赖对象注入的过程,而这些依赖对象在该bean中仅仅通过构造参数、工厂方法的参数或属性来指定。控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度,提高代码的可重用性和可维护性。

page_qiu的博客 1246

手把手教你实现TypeScript下的IoC容器

在此篇文章开始之前,先向大家简单介绍 IoC。什么是 IoC?以及为什么我们需要 IoC?以及本文核心,在 TypeScript 中实现一个简单的 IoC 容器? 目录[隐藏] IoC 定义 初识 Container 原理揭秘 @inject Container 小结 IoC 定义 我们看维基百科定义: 控制反转(Inversion of Control,缩写为 IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Depen.

嘿嘿 1959

手写简易IOC容器

手写简易IOC容器手写简易IOC容器1. IOC介绍2.简单代码实现IOC使用注解区别简介 手写简易IOC容器 1. IOC介绍 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: 谁控制谁,控制什么:传统Java SE程

adds001的博客 463

吊打面试官系列:深入理解Spring的IOC容器

管理的对象,使用的越多,创建的对象就越多,因此占用的资源也就会越多,如果循环创建对象,可能造成内存溢出。生态的时候,我们自己需要控制对象的创建和管理。容器统一管理,使用的时候通过注入的方式直接获取就好了。的思想很简单,但是里面的处理逻辑非常复杂,以兼容各种情况,这就是框架的魅力。管理的对象,使用更加灵活,不受框架的限制,可以用在任何地方。时代的到来,代替了我们对对象的管理。的对象太多了,导致现实的反噬,没有女朋友。对比非空参构造的对象,如果发生参数的修改,对于非。管理的对象,只需要修改实例化的地方即可。

static_coder的博客 645
上一篇: 系统盘为啥往往是C盘?软件为啥希望把自己装在C盘上?
下一篇: 如何理解Axis?
Java3y
Java3y 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄9年 1万+粉丝 509原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值