第四章、原理—手写Ioc

第四章、原理—手写Ioc

1、回顾反射

1、内容

1、反射机制是在运行状态中,对于任意一个类,都能知道这个类的属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

2、这种动态获取信息以及动态调用方法的功能就是java反射

3、简单来说,反射机制是指程序在运行时能够获取自身的信息。

4、要解刨一个类,必须要获取到该类的Class对象,而解刨一个类或用反射解决具体问题就是使用相关API

5、java.lang.Class

6、java.lang.reflect

7、Class是反射的根源

2、代码通过反射操作

1、获取class对象的多种获取方式

2、修改和设置class对象的构造方法

3、获取和修改对象的方法

1、注意

1、getDeclaredConstructors()方法:是用来获取所有private的构造方法

2、getConstructors()方法:是用来获取所有的public的构造方法

2、代码实现

1、Car.java

package com.atqt.reflect;

public class Car {
    private String name;
    private int age;
    private String color;

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

    public Car() {
    }

    public Car(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }

    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;
    }
}

2、TestCar.java

package com.atqt.reflect;

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

public class TestCar {
//    获取class对象多种方式
    @Test
    public void test01() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        类名.class
        Class<Car>clazz1 = Car.class;

//        对象.getClass()
        Class clazz2 = new Car().getClass();
//        Class.forName("全路径类名")
        Class clazz3 = Class.forName("com.atqt.reflect.Car");

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

//    获取构造方法
    @Test
    public void test02() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class clazz = Car.class;
////        获取所有的构造
//        Constructor[] constructors = clazz.getConstructors();
//        获取私有的构造和方法
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for(Constructor c :constructors){
            System.out.println("构造方法的名称:"+c.getName()+"参数的格式:"+c.getParameterCount());

        }
//        指定有参构造创建对象
////        1、构造公共的
//        Constructor c1 = clazz.getConstructor(String.class,int.class,String.class);
//        Car car1 = (Car) c1.newInstance("坦克",20,"绿色");
//        System.out.println(car1);
//        2、构造私有的
        Constructor c2 = clazz.getDeclaredConstructor(String.class,int.class,String.class);
        c2.setAccessible(true);
        Car car2 = (Car) c2.newInstance("坦克300",200,"绿色plus");
        System.out.println(car2);
    }
//  获取属性
    @Test
    public void test03() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class clazz = Car.class;
        Car car = (Car) clazz.getDeclaredConstructor().newInstance();
//        获取所有的public属性
        Field[] fields=clazz.getFields();
//        获取所有属性包含私有属性
        Field[] fields1 = clazz.getDeclaredFields();
        for (Field f :fields1){
//            在属性中设置一个值,在name设置一个值
            if(f.getName().equals("name")){
//                设置运行进行访问
                f.setAccessible(true);
                f.set(car,"好好好");
            }
            System.out.println(f.getName());
            System.out.println(car);
        }
    }
    //  获取方法
    @Test
    public void test04() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Car car = new Car("牧马人",10,"黑色");
        Class clazz = car.getClass();
//        操作public方法
        Method[] methods = clazz.getMethods();
        for (Method m :methods){
//            System.out.println(m.getName());
//            执行方法
            if (m.getName().equals("toString")){
                String invoke = (String) m.invoke(car);
                System.out.println(invoke);
            }
        }
//        操作private方法
        Method[] methods1 = clazz.getDeclaredMethods();
        for (Method m:methods1){
//            执行run方法
            if(m.getName().equals("run")){
                m.setAccessible(true);
                m.invoke(car);
            }
        }
    }
}

2、实现Spring的Ioc

image-20231016205716005

1、搭建子模块

1、搭建其他模块,就是创建其他项目

2、创建测试类Service、dao

1、UserService.java

package com.atqt.service;

public interface UserService {
    void add();
}

2、UserServiceImpl.java

package com.atqt.service.impl;

import com.atqt.anno.Bean;
import com.atqt.anno.Di;
import com.atqt.dao.UserDao;
import com.atqt.service.UserService;
@Bean
public class UserServiceImpl implements UserService {
    @Di
    private UserDao userDao;

    @Override
    public void add() {
        System.out.println("service......");
    }
}

3、UserDao.java

package com.atqt.dao;

public interface UserDao {

    void add();
}

4、UserDaoImpl.java

package com.atqt.dao.imp;

import com.atqt.anno.Bean;
import com.atqt.dao.UserDao;
@Bean
public class UserDaoImpl implements UserDao {

    @Override
    public void add() {
        System.out.println("dao ......");
    }
}

3、创建两个注解bean和Di

1、bean注解

package com.atqt.anno;

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 {
}

2、Di注解(依赖注入注解)

package com.atqt.anno;

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

//定义限制注解的类型
@Target({ElementType.FIELD, ElementType.TYPE})
//定义限制注解的使用情况
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

4、创建bean容器接口ApplicationContext定义方法,返回对象

1、创建ApplicationContext接口

package com.atqt.bean;

public interface ApplicationContext {

    Object getBean(Class clazz);

}

2、ApplicationContext方法的实现

package com.atqt.bean;

import com.atqt.anno.Bean;
import com.atqt.anno.Di;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class AnnotationApplicationContext implements ApplicationContext{

//    创建一个Map集合,放在bean对象
    private  Map<Class,Object> beanFactory = new HashMap<>();
//    成员根路径rootPath
    public static String rootPath;

//    返回创建的对象
    @Override
    public Object getBean(Class clazz) {
        return beanFactory.get(clazz);
    }

//    设置包的扫描规则
//    当前包及其子包,哪个类有@Bean注解,就把这个类通过反射实例化
//    创建有参数的构造,传递包的路径
    public  AnnotationApplicationContext(String basePackage)   {
//    public static void ann(String basePackage){
//com.atqt
//        把点替换为\
        String packagePath = basePackage.replaceAll("\\.","\\\\");

//        获取包的绝对路径
        try {
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
//            遍历得到路径
            while(urls.hasMoreElements()){
                URL url = urls.nextElement();
//                因为转义了,所以要转回去
                String filePath = URLDecoder.decode(url.getFile(),"utf-8");
//                System.out.println(filePath);
//                获取包前面路径部分,字符串截取
                rootPath =filePath.substring(0,filePath.length()-packagePath.length());

//                已经得到路径了,开始进行包的扫描
                loadBean(new File(filePath));

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

//        属性注入
        loadDi();

    }

    //    包扫描的过程,方法,bean实例化
    private  void loadBean(File file) throws Exception {
//        1、判断是否是个文件夹
        if(file.isDirectory()){
//        2、获取文件夹中的所有内容(类、文件夹等)
            File[] childFiles = file.listFiles();
//        3、判断文件夹内容为null,直接返回,(没有类)
            if(childFiles==null || childFiles.length==0){
                return;
            }
//        4、如果文件夹里面不为空,遍历文件夹所有的内容
            for(File child :childFiles){
//            4.1、遍历得到每个File对象,继续判断,如果还是文件夹,我们就递归
                if(child.isDirectory()){
                    //递归
                    loadBean(child);
                }else {
//            4.2、遍历得到的File对象不是文件夹,是文件

//            4.3、得到包路径+类名称部分-字符串截取
                    String pathWithClass =child.getAbsolutePath().substring(rootPath.length()-1);
//            4.4、当前文件的类型是否是.class文件
                    if(pathWithClass.contains(".class")){

        //            4.5、如果是.class类型,把路径的\替换成. 把.class去掉
        //                com.atqt.service.UserServiceImpl
                        String allName = pathWithClass.replaceAll("\\\\",".").replace(".class","");
        //            4.6、使用返回判断类上面是否有 @Bean注解,如果有就进行实例化过程
                        //4.6.1、获取类的class对象
                        Class<?> clazz = Class.forName(allName);
                        //4.6.2、判断不是接口
                        if(!clazz.isInterface()){
                            //4.6.3、判断类上面是否有注解@Bean
                            Bean annotation = clazz.getAnnotation(Bean.class);
                            if(annotation !=null){
                                //4.6.4、实例化
                                Object instance =clazz.getConstructor().newInstance();
                            //            4.7、把对象实例化后,放到map集合中
                                //4.7.1、判断当前类如果有接口,让接口class作为map的key
                                if(clazz.getInterfaces().length>0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                }else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 属性的注入
     */
    private void loadDi() {
//        1、实例化对象在beanFactory的map集合中
//        1、遍历beanFactory的map集合
        Set<Map.Entry<Class,Object>> entries = beanFactory.entrySet();
        for (Map.Entry<Class,Object> entry:entries){
//        2、获取map集合中的每个对象,把每个对象中的属性获取到
            Object obj = entry.getValue();
            //获取Class对象
            Class clazz = obj.getClass();
            //获取每个对象属性
            Field[] declaredFields = clazz.getDeclaredFields();
//        3、遍历得到每个对象属性数组,得到每个属性
            for(Field field:declaredFields){
//        4、判断属性上面是否有@Di注解
                Di annotatio = field.getAnnotation(Di.class);
                if(annotatio!=null){
                    //如果有私有属性,可以设置值
                    field.setAccessible(true);
//        5、如果有@DI注解,把对象进行设置(注入)
                    try {
                        field.set(obj,beanFactory.get(field.getType()));
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

}

5、进行测试

1、进行注释,并且实现测试类TestUser.java

package com.atqt;

import com.atqt.bean.AnnotationApplicationContext;
import com.atqt.bean.ApplicationContext;
import com.atqt.service.UserService;
import org.junit.jupiter.api.Test;

public class TestUser {

    public static void main(String[] args) {
        ApplicationContext context = new AnnotationApplicationContext("com.atqt");
        UserService userService = (UserService) context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇