实现Spring底层机制(二)

发布于:2024-04-28 ⋅ 阅读:(29) ⋅ 点赞:(0)

阶段2—封装bean定义信息到Map

1.代码框架图

image-20240221183904450

2.代码实现

1.文件目录

image-20240221195548820

2.新增注解Scope存储单例或多例信息Scope.java
package com.sun.spring.annotation;

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

/**
 * @author 孙显圣
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default ""; //通过value来决定是singleton,prototype
}

3.修改MonsterService.java指定多例注解
package com.sun.spring.component;

import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.Scope;

/**
 * @author 孙显圣
 * @version 1.0
 */

@Scope(value = "prototype") //指定是多例的
@Component(value = "monsterService")//自定义注解,自动反射创建bean对象,如果指定了value则id为value否则为首字母小写
public class MonsterService {
}

4.新增bean定义对象存储bean定义信息BeanDefinition.java
package com.sun.spring.ioc;

/**
 * 记录bean定义的信息{1.scope 表示单例还是多例 2.clazz 表示bean的Class对象,方便多例的时候创建实例}
 *
 * @author 孙显圣
 * @version 1.0
 */
public class BeanDefinition {
    private String scope; //单例或多例
    private Class clazz; //Class对象

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

5.修改pom.xml增加依赖
    <!--工具类,使类名首字母小写-->
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>
6.修改容器实现bean定义信息扫描SunSpringApplicationContext.java
package com.sun.spring.ioc;

import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.ComponentScan;
import com.sun.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;


import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 类似于Spring原生的ioc容器
 *
 * @author 孙显圣
 * @version 1.0
 */
public class SunSpringApplicationContext {
    //传进来一个配置类的Class对象
    private Class configClass;
    //bean定义字段
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //bean对象字段
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    //构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class configClass) throws ClassNotFoundException {
        //bean定义扫描
        this.beanDefinitionScan(configClass);

    }
    public void beanDefinitionScan(Class configClass) throws ClassNotFoundException {
        this.configClass = configClass;
        //一、获取要扫描的包
        //1.首先反射获取类的注解信息
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.通过注解来获取要扫描的包的路径
        String path = componentScan.value();
        System.out.println("要扫描的包=" + path);

        //二、得到要扫描包的.class文件对象,从而得到全路径进行反射
        //1.获取类加载器
        ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();
        //2.获取要扫描包的真实路径,默认刚开始在根目录下
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        //3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型
        File file = new File(resource.getFile());
        //4.遍历该文件夹下的所有.class文件对象
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                //反射注入容器
                //1.获取所有文件的绝对路径
                String absolutePath = f.getAbsolutePath();
                //只处理class文件
                if (absolutePath.endsWith(".class")) {
                    //2.分割出类名
                    String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf("."));
                    //3.得到全路径
                    String fullPath = path.replace("/", ".") + "." + className;
                    //4.判断是否需要注入容器,查看有没有自定义的注解Component
                    Class<?> aClass = classLoader.loadClass(fullPath);

                    //如果该类使用了注解Component则说明是一个spring bean
                    if (aClass.isAnnotationPresent(Component.class)) {
                        System.out.println("这是一个Spring bean=" + aClass);
                        //将Bean的信息封装到BeanDefinition对象中并放入到beanDefinitionMap
                        BeanDefinition beanDefinition = new BeanDefinition();
                        //获取scope注解信息
                        Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                        //只要scopeAnnotation是空的或者他不是空的但是值是空串,则返回singleon,否则就返回value
                        String scope = scopeAnnotation == null || scopeAnnotation.value().equals("") ?
                                "singleton" : scopeAnnotation.value();

                        //封装信息到bean定义字段中
                        beanDefinition.setScope(scope);
                        beanDefinition.setClazz(aClass);

                        //获取Component注解的value值作为beanName
                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        //只要component注解的值是空串就返回类名首字母小写,否则返回这个注解的值
                        String beanName = componentAnnotation.value().equals("") ?
                                StringUtils.uncapitalize(className) : componentAnnotation.value();

                        //封装到beanDefinitionMap中
                        beanDefinitionMap.put(beanName, beanDefinition);


                    } else {
                        System.out.println("这不是一个Spring bean=" + aClass);
                    }

                }
            }

            //遍历输出beanDefinitionMap
            System.out.println("遍历输出beanDefinitionMap");
            for (Map.Entry<String, BeanDefinition> beanDefinitionMap : beanDefinitionMap.entrySet()) {
                System.out.println(beanDefinitionMap.getKey() + " " + beanDefinitionMap.getValue());
            }
        }

    }

    //返回容器中的对象
    public Object getBean(String name) {
        return null;
    }

}

7.结果展示

image-20240221200049728

3.该阶段完成的任务

  • 新增注解Scope来存储单例/多例信息
  • 新增bean定义对象,存储bean定义信息Scope和Class对象
  • 扫描所有组件的信息,将bean定义信息以beanName - bean定义对象的形式存储到Map中

阶段3—初始化bean单例池&实现依赖注入

1.初始化bean单例池

1.代码框架图

image-20240221201306781

2.代码实现
1.修改SunSpringApplicationContext.java增加两个方法
    //根据bean定义对象来创建bean对象
    private Object createBean(BeanDefinition beanDefinition){
        //获取Class对象,反射创建实例
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //如果成功则返回这个对象
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

    }

    //返回容器中的对象
    public Object getBean(String name) {
        //如果是单例的,则从单例池中获取,如果是多例的则直接创建一个实例返回,名字没在bean定义中则抛出异常
        if (beanDefinitionMap.containsKey(name)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            //判断是否是单例
            if ("singleton".equals(beanDefinition.getScope())) {
                //从单例池中获取对象
                return singletonObjects.get(name);
            } else {
                //直接创建对象
                return createBean(beanDefinition);
            }
        } else {
            //name不在bean定义中则抛出异常
            throw new NullPointerException("没有该bean");
        }
    }
2.修改SunSpringApplicationContext.java在构造方法初始化单例池
    //构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class configClass) throws ClassNotFoundException {
        //bean定义扫描,并将信息封装到beanDefinitionMap中
        this.beanDefinitionScan(configClass);

        //初始化单例池
        //1.获取所有bean的名字
        Enumeration<String> keys = beanDefinitionMap.keys();
        //2.遍历名字
        while (keys.hasMoreElements()) {
            String beanName = keys.nextElement();
            //3.是单例类型的就创建bean对象并放到单例池中
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanDefinition);
                //4.放到单例池
                singletonObjects.put(beanName, bean);
            }
        }
        System.out.println("singletonObjects: " + singletonObjects);

    }
3.启动类AppMain.java
package com.sun.spring;

import com.sun.spring.ioc.SunSpringApplicationContext;
import com.sun.spring.ioc.SunSpringConfig;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        SunSpringApplicationContext ioc = new SunSpringApplicationContext(SunSpringConfig.class);
        Object bean01 = ioc.getBean("monsterDao111"); //单例对象
        Object bean02 = ioc.getBean("monsterDao111"); //单例对象
        System.out.println("单例对象1:" + bean01);
        System.out.println("单例对象2:" +bean02);
        //多例对象
        Object bean1 = ioc.getBean("monsterService");
        Object bean2 = ioc.getBean("monsterService");
        System.out.println("多例对象1:" + bean1);
        System.out.println("多例对象1:" + bean2);

    }
}

4.结果

image-20240221212306705

3.该阶段完成的任务
  • 新增createBean的方法,根据bean定义对象创建bean对象
  • 新增初始化单例池,在封装完bean定义信息之后,扫描bean定义信息,如果是单例对象则创建bean对象放到单例池中
  • 新增getBean方法,如果是单例的对象,则从单例池中查找,否则直接创建对象

2.实现依赖注入

1.代码实现
1.增加注解Autowired用来表示自动装配
package com.sun.spring.annotation;

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

/**
 * @author 孙显圣
 * @version 1.0
 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {

}

2.修改MonsterDao.java
package com.sun.spring.component;

import com.sun.spring.annotation.Component;

/**
 * @author 孙显圣
 * @version 1.0
 */
//scope默认是单例的
@Component(value = "monsterDao")
public class MonsterDao {
    public void hi() {
        System.out.println("MonsterDao say hi!");
    }

}

3.修改MonsterService.java
package com.sun.spring.component;

import com.sun.spring.annotation.Autowired;
import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.Scope;

/**
 * @author 孙显圣
 * @version 1.0
 */

@Scope(value = "prototype") //指定是多例的
@Component(value = "monsterService")//自定义注解,自动反射创建bean对象,如果指定了value则id为value否则为首字母小写
public class MonsterService {
    //自定义注解,按照名字进行依赖注入
    @Autowired
    private MonsterDao monsterDao;

    public void m1() {
        monsterDao.hi();
    }
}

4.修改SunSpringApplicationContext.java的createBean,添加依赖注入逻辑
    //根据bean定义对象来创建bean对象
    private Object createBean(BeanDefinition beanDefinition){
        //获取Class对象,反射创建实例
        Class clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //进行依赖注入
            //1.遍历所有字段
            for (Field field : clazz.getDeclaredFields()) {
                //2.判断字段中是否有autowired注解
                if (field.isAnnotationPresent(Autowired.class)) {
                    //3.依赖注入
                    String name = field.getName();
                    Object bean = getBean(name);
                    //反射爆破
                    field.setAccessible(true);
                    field.set(instance, bean);
                }
            }

            //如果成功则返回这个对象
            return instance;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }

    }
5.启动类AppMain.java
package com.sxs.spring;

import com.sxs.spring.aop.SmartAnimalable;
import com.sxs.spring.component.UserAction;
import com.sxs.spring.component.UserDao;
import com.sxs.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        UserAction action1 = ioc.getBean(UserAction.class);
        UserAction action2 = ioc.getBean(UserAction.class);
        System.out.println("action1=" + action1);
        System.out.println("action2=" + action2);
        UserService service = ioc.getBean(UserService.class);
        System.out.println("service=" + service);
        service.m1();
        UserDao userDao = ioc.getBean(UserDao.class);
        System.out.println("userDao=" + userDao);

        //aop测试
        SmartAnimalable bean = ioc.getBean(SmartAnimalable.class);
        bean.getSum(1,2);
        bean.getSub(2,1);
    }

}

6.结果

image-20240222103029837

3.总结IOC

1.单例bean的创建和依赖注入
  • 获取Spring容器对象,读取配置文件,得到要扫描的包
  • 扫描指定的包,将bean定义信息放到map中
  • 初始化单例池,如果是单例的则直接反射创建bean对象并放到单例池中
  • 依赖注入,扫描单例池中实例的所有字段,如果有自动装配注解则进行依赖注入
  • 初始化bean对象
  • getBean的时候从单例池中获取bean对象
  • 销毁bean
2.多例bean的特殊处理
  • getBean的时候反射创建bean对象,依赖注入,初始化bean,然后再得到bean对象
  • 销毁bean

网站公告

今日签到

点亮在社区的每一天
去签到