IOC principles and framework for achieving IOC custom functions (a)

IOC Introduction

IOC stands for Inversion of Control (Inversion of Control), also known as dependency injection, in fact, these two concepts are the same, inversion of control is the academic term, obscure, for ease of understanding, then the introduction of dependency injection to facilitate understanding.

Why use ioc

When not in use ioc, coupling large projects, often when updating the program, is not directly delete the original function, to keep the original part, it is necessary to use a new class to replace the old class, but this way All related classes need to be modified, this is very troublesome, and to forget a change, it will error, the program can not run, can be achieved while the ioc class and class decoupling, the decoupling layers, reaching only need to change one, it can update the entire project, easy to maintain expansion.

How to achieve ioc

To achieve the purpose of decoupling, and the need to use the interface to inject the object reflector

1. Create two classes and a test class

//User类
public class User {
    private UserDAO userDAO;
     public UserDAO getUserDAO() {
        return userDAO;
    }
}

//UserDAO类
public class UserDAO {
}
//测试类
public class Demo {
    public static void main(String[] args) {
        User user = new User();
        user.getUserDAO();
    }
}

Now I want to create a new UserDAO1 instead UserDAO, then it must be modified in accordance with the previous User and Demo, the relationship if there are other needs like these two classes, then change more, which use the reflection to inject First let UserDAO and UserDAO1 implement IUserDAO, and let the properties of the User class becomes IUserDAO

public class User {
    private IUserDAO userDAO;

    public IUserDAO getUserDAO() {
        return userDAO;
    }

In the class with the reflective demo injection userDAO1

 public static void main(String[] args) throws Exception {
        User user = new User();
        //如果要更换UserDAO类,只需要添加一个新的类让他实现IuserDAO,这里修改一下就可以了
        IUserDAO userDAO = new UserDAO1();
        //通过反射取得UserDAO的属性并把要更改的类的实例注入
        Class<? extends User> aClass = user.getClass();
        Field field = aClass.getDeclaredField("userDAO");
        field.setAccessible(false);
        field.set(user,userDAO);
    }

To this, and certainly will wonder, I do not like too much trouble to go to new objects one by one, a line of code to get, but if a big project, like a lot of it, one by one to find it? To find a leak in case, that bug. And this only write once, after more class, more projects can use this one on it.

2. There are many methods to inject even attribute it?

Then use the annotation, first customize a comment MyField, to two properties, gave a default value

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyField {
     Class name() default Class.class;
     String value() default "";
}

Then add annotations on the properties to be injected, and the value obtained by adding the required injection

public class User {
    
    @MyField(name = UserDAO1.class)
    public IUserDAO userDAO;
    
    @MyField(value = "张三")
    public String name;
    
    
    public IUserDAO getUserDAO() {
        return userDAO;
    }
}

Implanting the elements of the user in the demo

public static void main(String[] args) throws Exception {
        User user = new User();
        //遍历获取user中的所有元素
        Field[] declaredFields = user.getClass().getDeclaredFields();
        for (Field field:declaredFields){
            MyField annotation = field.getAnnotation(MyField.class);
            //判断元素上面是否有MyFiled注解
            if (annotation != null){
                //如果有就获取name和value的值
                Class userDAO1 = annotation.name();
                String name = annotation.value();
                //如果name的值不为空,且不等于默认值,就说明需要注入,直接注入
                if (userDAO1!=null && !userDAO1.equals(Class.class)){
                    field.set(user,userDAO1.newInstance());
                }
                //如果value值不为空且不等于默认值,就说明需要注入,直接注入
                if(name!=null && !name.equals("")){
                    field.set(user,name);
                }
            }
        }
        System.out.println(user);
    }

The output is User{userDAO=com.qf.demo.UserDAO1@511d50c0, name = 'Joe Smith'}
instructions injection is successful, and now we want to change UserDAO found that if you want to modify only the value of the notes, but strictly speaking notes not java code, it would have meant not need to change the java code to achieve the purpose of decoupling.

3. To inject not only the User class, there are many types of it?

In fact, the principle is the same, I have added a custom annotation SpringComponent on the need to inject all the classes, and then traverse the entire folder, access to all the java files, the class will be annotated directly to SpringComponent written above inject methods

public static void main(String[] args) throws Exception {
        Injection injection = new Injection();
        injection.init("com.qf.demo");

    }

    public void init(String packageName) throws Exception {
        String basePath="D:\\program\\idea\\day52_demo\\src\\main\\java\\";
        //将传入的"com.qf.demo"转换成"com\qf\demo"
        String replace = packageName.replace(".", "/");
        File file = new File(basePath + "\\"+replace);
        //遍历该文件夹下所有文件
        File[] files = file.listFiles();
        for (File f:files){
            String name = f.getName();
            if(f.isFile()){
                //只有java文件才是哦们需要的,如果是文件就判断是不是以".java"结尾的
                if (name.endsWith(".java")){
                    //如果是就以“.”将文件名分割成"demo"和"java"两部分
                    String[] split = name.split("\\.");
                    String className = split[0];
                    Class<?> aClass = Class.forName(packageName +"." +className);
                  //判断类上是否有SpringComponent注解,如果有就调用inject方法,将类对象传入
                    SpringComponent annotation = aClass.getAnnotation(SpringComponent.class);
                    if (annotation != null){
                        inject(aClass);
                    }
                }
            }else{
                //如果不是文件就是文件夹,就使用递归循环该方法
                String childPath=packageName+"."+name;
                init(childPath);
            }
        }

    }

    public <T>void inject(Class<T> clazz) throws Exception {
        //创建传入类对象的实例对象
        T t = clazz.newInstance();
        Field[] declaredFields = clazz.getClass().getDeclaredFields();
        for (Field field:declaredFields){
            MyField annotation = field.getAnnotation(MyField.class);
            //判断元素上面是否有MyFiled注解
            if (annotation != null){
                //如果有就获取name和value的值
                Class aClass = annotation.name();
                String name = annotation.value();
                //如果name的值不为空,且不等于默认值,就说明需要注入,直接注入
                if (aClass!=null && !aClass.equals(Class.class)){
                    field.set(t,aClass.newInstance());
                }
                //如果value值不为空且不等于默认值,就说明需要注入,直接注入
                if(name!=null && !name.equals("")){
                    field.set(t,name);
                }
            }
        }
    }

end

Now though the ioc has achieved the most basic functions, but there are many shortcomings, such as:
1. Each time you create a new project it is necessary to change the address basePath
words 2. I want to take elements within the object or to take a new target, and former lacks distinction
3. If a is an element of B, and B and the first implementation, then B of a is empty
...

Released two original articles · won praise 0 · Views 110

Guess you like

Origin blog.csdn.net/weixin_46007540/article/details/103499554