spring6 - IOCコンテナの簡易バージョンの実装

Spring フレームワークの IOC が Java リフレクション メカニズムに基づいて実装されていることは誰もが知っていますが、まず Java リフレクションについて確認してみましょう。

1. Java リフレクションを確認する

Javaリフレクション メカニズムでは、実行状態では、どのクラスについても、このクラスのすべてのプロパティとメソッドを知ることができます。また、どのオブジェクトについても、そのメソッドやプロパティのいずれかを呼び出すことができます。この動的な情報の取得とオブジェクトの動的な呼び出しは、メソッド機能はJava言語の反射メカニズムと呼ばれます。簡単に言えば、リフレクション メカニズムとは、プログラムが実行中に独自の情報を取得できることを意味します。

クラスを分析するには、まずクラスの Class オブジェクトを取得する必要があります。クラスを分析したり、リフレクションを使用して特定の問題を解決するには、関連する API** (1) java.lang.Class (2) java.lang.reflect** を使用することになるため、Class オブジェクトがリフレクションのソースになります

カスタムクラス

package com.atguigu.reflect;

public class Car {
    
    

    //属性
    private String name;
    private int age;
    private String color;

    //无参数构造
    public Car() {
    
    
    }

    //有参数构造
    public Car(String name, int age, String color) {
    
    
        this.name = name;
        this.age = age;
        this.color = color;
    }

    //普通方法
    private void run() {
    
    
        System.out.println("私有方法-run.....");
    }

    //get和set方法
    public String getName() {
    
    
        return name;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }
    public int getAge() {
    
    
        return age;
    }
    public void setAge(int age) {
    
    
        this.age = age;
    }
    public String getColor() {
    
    
        return color;
    }
    public void setColor(String color) {
    
    
        this.color = color;
    }

    @Override
    public String toString() {
    
    
        return "Car{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}

テストクラスを書く

package com.atguigu.reflect;

import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestCar {
    
    

    //1、获取Class对象多种方式
    @Test
    public void test01() throws Exception {
    
    
        //1 类名.class
        Class clazz1 = Car.class;

        //2 对象.getClass()
        Class clazz2 = new Car().getClass();

        //3 Class.forName("全路径")
        Class clazz3 = Class.forName("com.atguigu.reflect.Car");

        //实例化
        Car car = (Car)clazz3.getConstructor().newInstance();
        System.out.println(car);
    }

    //2、获取构造方法
    @Test
    public void test02() throws Exception {
    
    
        Class clazz = Car.class;
        //获取所有构造
        // getConstructors()获取所有public的构造方法
//        Constructor[] constructors = clazz.getConstructors();
        // getDeclaredConstructors()获取所有的构造方法public  private
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor c:constructors) {
    
    
            System.out.println("方法名称:"+c.getName()+" 参数个数:"+c.getParameterCount());
        }

        //指定有参数构造创建对象
        //1 构造public
//        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
//        Car car1 = (Car)c1.newInstance("夏利", 10, "红色");
//        System.out.println(car1);
        
        //2 构造private
        Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        c2.setAccessible(true);
        Car car2 = (Car)c2.newInstance("捷达", 15, "白色");
        System.out.println(car2);
    }

    //3、获取属性
    @Test
    public void test03() throws Exception {
    
    
        Class clazz = Car.class;
        Car car = (Car)clazz.getDeclaredConstructor().newInstance();
        //获取所有public属性
        //Field[] fields = clazz.getFields();
        //获取所有属性(包含私有属性)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field:fields) {
    
    
            if(field.getName().equals("name")) {
    
    
                //设置允许访问
                field.setAccessible(true);
                field.set(car,"五菱宏光");
                System.out.println(car);
            }
            System.out.println(field.getName());
        }
    }

    //4、获取方法
    @Test
    public void test04() throws Exception {
    
    
        Car car = new Car("奔驰",10,"黑色");
        Class clazz = car.getClass();
        //1 public方法
        Method[] methods = clazz.getMethods();
        for (Method m1:methods) {
    
    
            //System.out.println(m1.getName());
            //执行方法 toString
            if(m1.getName().equals("toString")) {
    
    
                String invoke = (String)m1.invoke(car);
                //System.out.println("toString执行了:"+invoke);
            }
        }

        //2 private方法
        Method[] methodsAll = clazz.getDeclaredMethods();
        for (Method m:methodsAll) {
    
    
            //执行方法 run
            if(m.getName().equals("run")) {
    
    
                m.setAccessible(true);
                m.invoke(car);
            }
        }
    }
}

2. Spring の IoC を実装する

IoC (制御の反転) と DI (依存性注入) が Spring の中核であることはわかっていますが、そのようなコードを自分で手書きするにはどうすればよいでしょうか? 以下に、Spring フレームワークのコア部分をステップごとに書き出していきます。

①サブモジュールの構築

モジュールの構築: guigu-spring、構築方法は他の Spring サブモジュールと同じです

②テストに必要な豆を準備する

依存関係を追加する

<dependencies>
    <!--junit5测试-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.3.1</version>
    </dependency>
</dependencies>

UserDaoインターフェースを作成する

package com.atguigu.spring6.test.dao;

public interface UserDao {
    
    

    public void print();
}

UserDaoImpl実装を作成する

package com.atguigu.spring6.test.dao.impl;

import com.atguigu.spring.dao.UserDao;

public class UserDaoImpl implements UserDao {
    
    

    @Override
    public void print() {
    
    
        System.out.println("Dao层执行结束");
    }
}

UserServiceインターフェースの作成

package com.atguigu.spring6.test.service;

public interface UserService {
    
    

    public void out();
}

UserServiceImpl実装クラスを作成する

package com.atguigu.spring.test.service.impl;

import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.service.UserService;

@Bean
public class UserServiceImpl implements UserService {
    
    

//    private UserDao userDao;

    @Override
    public void out() {
    
    
        //userDao.print();
        System.out.println("Service层执行结束");
    }
}

③定義アノテーション

Bean をロードし、アノテーションを通じて依存関係の注入を実装します。

Bean アノテーション

package com.atguigu.spring.core.annotation;

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

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

依存関係注入のアノテーション

package com.atguigu.spring.core.annotation;

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

@Target({
    
    ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
    
    
}

注: 上記の 2 つのアノテーションには、好きな名前を付けることができます。

④Beanコンテナインターフェースの定義

package com.atguigu.spring.core;

public interface ApplicationContext {
    
    

    Object getBean(Class clazz);
}

⑤書き込みアノテーションBeanコンテナインターフェース実装

AnnotationApplicationContext はアノテーションに基づいて Bean をスキャンします

package com.atguigu.spring.core;

import java.util.HashMap;

public class AnnotationApplicationContext implements ApplicationContext {
    
    

    //存储bean的容器
    private HashMap<Class, Object> beanFactory = new HashMap<>();

    @Override
    public Object getBean(Class clazz) {
    
    
        return beanFactory.get(clazz);
    }

    /**
     * 根据包扫描加载bean
     * @param basePackage
     */
    public AnnotationApplicationContext(String basePackage) {
    
    
        
    }
}

⑥スキャンビーンロジックを書く

コンストラクター メソッドを通じてパッケージのベース パスを渡し、@Bean アノテーションが付けられた Java オブジェクトをスキャンします。完全なコードは次のとおりです。

package com.atguigu.spring.core;

import com.atguigu.spring.core.annotation.Bean;

import java.io.File;
import java.util.HashMap;

public class AnnotationApplicationContext implements ApplicationContext {
    
    

    //存储bean的容器
    private HashMap<Class, Object> beanFactory = new HashMap<>();
    private static String rootPath;

    @Override
    public Object getBean(Class clazz) {
    
    
        return beanFactory.get(clazz);
    }

    /**
     * 根据包扫描加载bean
     * @param basePackage
     */
    public AnnotationApplicationContext(String basePackage) {
    
    
       try {
    
    
            String packageDirName = basePackage.replaceAll("\\.", "\\\\");
            Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
    
    
                URL url = dirs.nextElement();
                String filePath = URLDecoder.decode(url.getFile(),"utf-8");
                rootPath = filePath.substring(0, filePath.length()-packageDirName.length());
                loadBean(new File(filePath));
            }

        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
    }

    private  void loadBean(File fileParent) {
    
    
        if (fileParent.isDirectory()) {
    
    
            File[] childrenFiles = fileParent.listFiles();
            if(childrenFiles == null || childrenFiles.length == 0){
    
    
                return;
            }
            for (File child : childrenFiles) {
    
    
                if (child.isDirectory()) {
    
    
                    //如果是个文件夹就继续调用该方法,使用了递归
                    loadBean(child);
                } else {
    
    
                    //通过文件路径转变成全类名,第一步把绝对路径部分去掉
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //选中class文件
                    if (pathWithClass.contains(".class")) {
    
    
                        //    com.xinzhi.dao.UserDao
                        //去掉.class后缀,并且把 \ 替换成 .
                        String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        try {
    
    
                            Class<?> aClass = Class.forName(fullName);
                            //把非接口的类实例化放在map中
                            if(!aClass.isInterface()){
    
    
                                Bean annotation = aClass.getAnnotation(Bean.class);
                                if(annotation != null){
    
    
                                    Object instance = aClass.newInstance();
                                    //判断一下有没有接口
                                    if(aClass.getInterfaces().length > 0) {
    
    
                                        //如果有接口把接口的class当成key,实例对象当成value
                                        System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass.getInterfaces()[0], instance);
                                    }else{
    
    
                                        //如果有接口把自己的class当成key,实例对象当成value
                                        System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass, instance);
                                    }
                                }
                            }
                        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

}

⑦Javaクラス識別Beanアノテーション

@Bean
public class UserServiceImpl implements UserService
@Bean
public class UserDaoImpl implements UserDao 

⑧テストBeanのロード

package com.atguigu.spring;

import com.atguigu.spring.core.AnnotationApplicationContext;
import com.atguigu.spring.core.ApplicationContext;
import com.atguigu.spring.test.service.UserService;
import org.junit.jupiter.api.Test;

public class SpringIocTest {
    
    

    @Test
    public void testIoc() {
    
    
        ApplicationContext applicationContext = new AnnotationApplicationContext("com.atguigu.spring.test");
        UserService userService = (UserService)applicationContext.getBean(UserService.class);
        userService.out();
        System.out.println("run success");
    }
}

コンソール印刷テスト

⑨依存性注入

userDao.print(); が正常に呼び出される限り、注入は成功します。

package com.atguigu.spring.test.service.impl;

import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;
import com.atguigu.spring.dao.UserDao;
import com.atguigu.spring.service.UserService;

@Bean
public class UserServiceImpl implements UserService {
    
    

    @Di
    private UserDao userDao;

    @Override
    public void out() {
    
    
        userDao.print();
        System.out.println("Service层执行结束");
    }
}

ステップ 8 を実行します。現在の userDao が空のオブジェクトであることを示すエラーが報告されます。

⑩ 依存性注入の実装

package com.atguigu.spring.core;

import com.atguigu.spring.core.annotation.Bean;
import com.atguigu.spring.core.annotation.Di;

import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class AnnotationApplicationContext implements ApplicationContext {
    
    

    //存储bean的容器
    private HashMap<Class, Object> beanFactory = new HashMap<>();
    private static String rootPath;

    @Override
    public Object getBean(Class clazz) {
    
    
        return beanFactory.get(clazz);
    }

    /**
     * 根据包扫描加载bean
     * @param basePackage
     */
    public AnnotationApplicationContext(String basePackage) {
    
    
        try {
    
    
            String packageDirName = basePackage.replaceAll("\\.", "\\\\");
            Enumeration<URL> dirs =Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
    
    
                URL url = dirs.nextElement();
                String filePath = URLDecoder.decode(url.getFile(),"utf-8");
                rootPath = filePath.substring(0, filePath.length()-packageDirName.length());
                loadBean(new File(filePath));
            }

        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
        
        //依赖注入
        loadDi();
    }
    
    private  void loadBean(File fileParent) {
    
    
        if (fileParent.isDirectory()) {
    
    
            File[] childrenFiles = fileParent.listFiles();
            if(childrenFiles == null || childrenFiles.length == 0){
    
    
                return;
            }
            for (File child : childrenFiles) {
    
    
                if (child.isDirectory()) {
    
    
                    //如果是个文件夹就继续调用该方法,使用了递归
                    loadBean(child);
                } else {
    
    
                    //通过文件路径转变成全类名,第一步把绝对路径部分去掉
                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //选中class文件
                    if (pathWithClass.contains(".class")) {
    
    
                        //    com.xinzhi.dao.UserDao
                        //去掉.class后缀,并且把 \ 替换成 .
                        String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");
                        try {
    
    
                            Class<?> aClass = Class.forName(fullName);
                            //把非接口的类实例化放在map中
                            if(!aClass.isInterface()){
    
    
                                Bean annotation = aClass.getAnnotation(Bean.class);
                                if(annotation != null){
    
    
                                    Object instance = aClass.newInstance();
                                    //判断一下有没有接口
                                    if(aClass.getInterfaces().length > 0) {
    
    
                                        //如果有接口把接口的class当成key,实例对象当成value
                                        System.out.println("正在加载【"+ aClass.getInterfaces()[0] +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass.getInterfaces()[0], instance);
                                    }else{
    
    
                                        //如果有接口把自己的class当成key,实例对象当成value
                                        System.out.println("正在加载【"+ aClass.getName() +"】,实例对象是:" + instance.getClass().getName());
                                        beanFactory.put(aClass, instance);
                                    }
                                }
                            }
                        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private void loadDi() {
    
    
        for(Map.Entry<Class,Object> entry : beanFactory.entrySet()){
    
    
            //就是咱们放在容器的对象
            Object obj = entry.getValue();
            Class<?> aClass = obj.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field field : declaredFields){
    
    
                Di annotation = field.getAnnotation(Di.class);
                if( annotation != null ){
    
    
                    field.setAccessible(true);
                    try {
    
    
                        System.out.println("正在给【"+obj.getClass().getName()+"】属性【" + field.getName() + "】注入值【"+ beanFactory.get(field.getType()).getClass().getName() +"】");
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

実行ステップ 8: 実行は成功し、依存関係の注入は成功します。

おすすめ

転載: blog.csdn.net/m0_62946761/article/details/133470213