Java框架学习之Spring—手写容器IOC

4、原理-手写IOC

4.1 Java反射机制

反射是指 对于任意一个类 能知道这个类的属性和方法 对于任意一个对象 都能调用它任意的方法和属性 动态获取信息以及动态调用对象方法的功能成为反射

要想解剖一个类 首先要获取该类的Class对象 剖析一个类用反射解决问题就是使用相关的API

public class TestCar {
    
    //1、获取Class对象
    @Test
    public void test01() throws Exception {

        //1、类名.class
        Class<Car> clazz1 = Car.class;
        //2、对象.getClass()
        Class clazz2 = new Car().getClass();
        //3、Class.forName("全路径")
        Class clszz3 = Class.forName("com.szy.reflect.Car");

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

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

        //指定有参数的构造创建对象
        //1、构造public
        Constructor<Car> c1 = clazz.getConstructor(String.class, int.class, String.class);
        Car car1 = c1.newInstance("夏利", 10, "红色");
        System.out.println(car1);
    }

    //3、用反射获取属性
    @Test
    public void test03()throws Exception{
        Class<Car> clazz = Car.class;
        Car car = clazz.getDeclaredConstructor().newInstance();
        //Field[] fields = clazz.getFields();
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals("name")){
                //设置运行访问
                declaredField.setAccessible(true);
                declaredField.set(car,"五菱宏光");
            }
            System.out.println(declaredField.getName());
            System.out.println(car);
        }
    }

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

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


}

4.2实现Spring的IOC

自己实现IOC和DI

实现过程:

1、创建子模块

2、创建测试类 service dao

3、创建两个注解(自己写)@Bean @Di

4、自己常见一个bean容器的接口ApplicationContext 定义方法 返回对象

5、实现bean容器的接口

(1) 返回对象

(2) 根据包规则加载bean

扫描com.szy包以及其子包下的所有类 开类上面是否有我们创建的注解@Bean 如果有把这个类通过反射实例化

4.2.1 搭建子模块

4.2.1.1 自定义注解
//Bean注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {
}
//Di注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}
4.2.1.2 DAO层
//UserDao.interface
public interface UserDao {
    public void run();
}
//UserDaoImpl.class
@Bean
public class UserDaoImpl implements UserDao {
    @Override
    public void run() {
        System.out.println("dao........");
    }
}
4.2.1.3 Service层
public interface UserService {
    public void add();
}
@Bean
public class UserServiceImpl implements UserService {

    @Di
    private UserDao userDao;

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

4.2.2 手写注入类

//接口
public interface ApplicationContext {

    Object getBean(Class clazz);
}
//实现类
public class AnnotationApplicationContext implements ApplicationContext{

    //创建map集合用于放入bean对象
    private Map<Class,Object> beanFactory = new HashMap();
    private static String rootPath;

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

    //设置扫描规则
    //当前包及其子类 哪个类有@Bean注解 把这个类通过反射实例化
    //创建有参数构造 传入


    public AnnotationApplicationContext(String basePackage) {
        try {
            //1、把.替换成斜杠
            String packagePath = basePackage.replaceAll("\\.", "\\\\");
            //2、获取包的绝对路径
            Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packagePath);
            while (urls.hasMoreElements()){
                URL url = urls.nextElement();
                String filepath = URLDecoder.decode(url.getFile(), "utf8");

                //获取包前面路径部分
                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[] childrenFiles = file.listFiles();
            //3、判断文件夹里面为空 直接返回
            if (childrenFiles==null || childrenFiles.length==0){
                return;
            }
            //4、如果文件夹不为空 遍历文件夹所有内容
            for (File child : childrenFiles) {
                //4.1 遍历得到每一个File对象 继续判断 如果还是一个文件夹继续递归遍历
                if (child.isDirectory()){
                    loadBean(child);
                }else {
                    //4.2 遍历得到不是一个文件夹是文件
                    //4.3 得到包路径+类名称部分-字符串提取过程

                    String pathWithClass = child.getAbsolutePath().substring(rootPath.length() - 1);
                    //4.4 判断当前文件类型是否为.class
                    if (pathWithClass.contains(".class")){
                        //4.5 如果是.class 把路径\替换成. 把.class去掉
                        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 annotation = (Bean) clazz.getAnnotation(Bean.class);
                            if (annotation!=null){
                                //4.6.4 实例化
                                Object instance = clazz.getConstructor().newInstance();
                                //4.7 把对象实例化后放入map集合中去
                                //4.7.1 该类有接口让接口作为key 没有接口自己的class就是key
                                if (clazz.getInterfaces().length>0){
                                    beanFactory.put(clazz.getInterfaces()[0],instance);
                                }else {
                                    beanFactory.put(clazz,instance);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

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

    }


}
//测试类
public class TestUser {
    public static void main(String[] args) {

        ApplicationContext context  = new AnnotationApplicationContext("com.szy");
        UserService userService = (UserService) context.getBean(UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

结果

com.szy.service.Impl.UserServiceImpl@77459877
service....
dao........

Process finished with exit code 0
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值