实现Spring底层机制(三)

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

阶段4—实现BeanPostProcessor机制

1.文件目录

image-20240222142242669

2.初始化方法实现

1.编写初始化接口InitializingBean.java
package com.sun.spring.processor;

/**
 * 该接口有一个方法afterPropertiesSet是在依赖注入之后调用,即初始化方法
 * @author 孙显圣
 * @version 1.0
 */
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

2.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;
import com.sun.spring.processor.InitializingBean;

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

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

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

    //初始化方法
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterService 的初始化方法被调用!");
    }
}

3.容器中的createBean方法增加初始化逻辑,判断对象类型是否是InitializingBean的子类型,如果是,则转换成初始化接口类型执行初始化方法

3.后置处理器实现

1.编写后置处理器接口BeanPostProcessor.java
package com.sun.spring.processor;


/**
 * @author 孙显圣
 * @version 1.0
 */
public interface BeanPostProcessor {
    //这两个方法会对Spring容器的所有bean生效,切面编程
    //bean初始化方法前调用
    default Object postProcessBeforeInitialization(Object bean, String beanName){
        return bean;
    }

    //bean初始化方法后调用
    default Object postProcessAfterInitialization(Object bean, String beanName){
        return bean;
    }
}

2.在组件文件夹下编写后置处理器实现类SunBeanPostProcessor.java会被容器扫描
package com.sun.spring.component;

import com.sun.spring.annotation.Component;
import com.sun.spring.processor.BeanPostProcessor;

/**
 * 后置处理器,实现了自定义的后置处理器的接口
 *
 * @author 孙显圣
 * @version 1.0
 */
//反射创建bean对象
@Component
public class SunBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("\n后置处理器postProcessBeforeInitialization被调用 bean类型=" + bean.getClass() + "bean名字=" + beanName);
        if (bean instanceof Car) {
            System.out.println("后置处理器发现这个类型是Car类型");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器postProcessAfterInitialization被调用 bean类型=" + bean.getClass() + "bean名字=" + beanName);
        return bean;
    }
}

3.容器类完整代码SunSpringApplicationContext.java
package com.sun.spring.ioc;

import com.sun.spring.annotation.Autowired;
import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.ComponentScan;
import com.sun.spring.annotation.Scope;
import com.sun.spring.processor.BeanPostProcessor;
import com.sun.spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
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<>();
    //定义一个属性ArrayList来存放后置处理器
    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    //构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        //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, beanName);
                //4.放到单例池
                singletonObjects.put(beanName, bean);
            }
        }

    }

    public void beanDefinitionScan(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        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();
            System.out.println("==================================扫描所有组件==================================");
            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)) {


                        //后置处理器逻辑
                        //为了方便,将后置处理器放到ArrayList,原生的Spring容器中,对后置处理器还是放到的singletonObjects
                        //判断当前的class对象是否实现了后置处理器的接口
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            //如果实现了后置处理器的接口则转换成后置处理器类型
                            BeanPostProcessor beanPostProcessor = (BeanPostProcessor) aClass.newInstance();
                            //添加到ArrayList中
                            beanPostProcessors.add(beanPostProcessor);
                            //后面我们并没有用到单例池中的后置处理器,所以不需要将这个bean定义的信息放到map
                            continue;
                        }

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

                }
            }

        }

    }

    //根据bean定义对象来创建bean对象
    private Object createBean(BeanDefinition beanDefinition, String beanName) {
        //获取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);
                    System.out.println(instance + "依赖注入完毕!");
                }
            }


            //初始化方法逻辑
            //判断是否要执行Bean的初始化方法
            //存储instance
            Object result = instance;
            //查看这个对象是否实现了InitializingBean接口,instanceof判断是否是某个类型或者某个类型的子类型
            if (instance instanceof InitializingBean) {
                //在初始化方法之前调用后置处理器的before方法,可以对容器的bean进行修改
                for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                    instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
                }
                //向上转型为接口类型并调用初始化方法
                ((InitializingBean) instance).afterPropertiesSet();
                //在初始化方法之后调用后置处理器的before方法,可以对容器的bean进行修改
                for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                    instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
                }
            }
            //如果返回的instence是null,则返回之前存贮的结果
            if (instance == null) {
                return result;
            }

            //如果成功则返回这个对象
            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);
        } catch (Exception 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, name);
            }
        } else {
            //name不在bean定义中则抛出异常
            throw new NullPointerException("没有该bean");
        }
    }


}

4.结果展示

image-20240222143316755

4.该阶段完成的任务

  • 实现初始化方法
    • 编写初始化接口,里面有一个default的初始化方法
    • 编写初始化实现类,重写这个初始化方法
    • 在容器的依赖注入后添加初始化逻辑,当这个对象的类型是初始化接口的子类型的时候,就将其转换成接口类型并且调用初始化方法
  • 后置处理器实现
    • 编写一个后置处理器接口,里面有两个方法一个在初始化前调用,一个在初始化后调用
    • 在组件文件夹下编写后置处理器实现类
    • 在容器中beanDefinitionScan方法的扫描组件时添加后置处理器逻辑,如果该组件实现了后置处理器接口,则转换成后置处理器类型,并且放到ArrayList中方便查找
    • 在容器中的createBean方法增加后置处理器逻辑,在初始化方法之前和之后遍历存储后置处理器的ArrayList并分别执行方法

阶段5—实现AOP机制&Spring底层机制总结

1.实现AOP机制

1.原理分析

image-20240222151512245

2.代码实现
1.文件目录

image-20240222164154334

2.编写接口SmartAnimalable.java
package com.sun.spring.component;

/**
 * @author 孙显圣
 * @version 1.0
 */
public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}

2.编写实现类SmartDog.java
package com.sun.spring.component;

import com.sun.spring.annotation.Component;
import com.sun.spring.processor.InitializingBean;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Component(value = "smartDog")
public class SmartDog implements SmartAnimalable, InitializingBean {
    public float getSum(float i, float j) {
        float res = i + j;
        System.out.println("SmartDog-getSum=" + res);
        return res;
    }

    public float getSub(float i, float j) {
        float res = i - j;
        System.out.println("SmartDog-getSub=" + res);
        return res;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("smartDog 被初始化!");
    }
}

3.编写切面类SmartAnimalAspect.java
package com.sun.spring.component;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class SmartAnimalAspect {
    public static void showBeginLog() {
        System.out.println("前置通知");
    }
    public static void showSuccessLog() {
        System.out.println("返回通知");
    }
}

4.修改SunBeanPostProcessor.java对SmartDog的getSum方法进行动态代理
package com.sun.spring.component;

import com.sun.spring.annotation.Component;
import com.sun.spring.processor.BeanPostProcessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 后置处理器,实现了自定义的后置处理器的接口
 *
 * @author 孙显圣
 * @version 1.0
 */
//反射创建bean对象
@Component
public class SunBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("\n后置处理器postProcessBeforeInitialization被调用 bean类型=" + bean.getClass() + "bean名字=" + beanName);
        if (bean instanceof Car) {
            System.out.println("后置处理器发现这个类型是Car类型");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器postProcessAfterInitialization被调用 bean类型=" + bean.getClass() + "bean名字=" + beanName);
        //实现AOP,返回代理对象
        if ("smartDog".equals(beanName)) {
            //使用jdk的动态代理返回代理对象
            /**
             * ClassLoader loader,
             * Class<?>[] interfaces,
             * InvocationHandler h
             */
            //1.要代理的对象的类加载器
            ClassLoader classLoader = bean.getClass().getClassLoader();

            //2.要代理对象的接口信息

            Class<?>[] interfaces = bean.getClass().getInterfaces();

            //3.代理对象执行的方法,在代理对象执行所有的方法时都会调用
            InvocationHandler invocationHandler = new InvocationHandler() {
                /**
                 * 代理对象执行的方法,在代理对象执行所有的方法时都会调用
                 *
                 * @param proxy 动态代理对象
                 *
                 * @param method 动态代理对象调用的方法
                 *
                 * @param args 方法的参数
                 *
                 * @return 返回method返回的结果
                 * @throws Throwable
                 */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    //判断是不是getSum方法,如果是,则进行AOP处理
                    Object result = null;
                    if ("getSum".equals(method.getName())) {
                        //前置通知
                        SmartAnimalAspect.showBeginLog();
                        result = method.invoke(bean, args);
                        //返回通知
                        SmartAnimalAspect.showSuccessLog();
                    } else {
                        //如果不是getSum方法则正常执行原来的方法
                        result = method.invoke(bean, args);
                    }
                    return result;
                }
            };
            //返回针对接口的代理对象(可以使用接口类型接收)
            return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        }
        return bean;
    }
}

5.启动类
package com.sun.spring;

import com.sun.spring.component.MonsterService;
import com.sun.spring.component.SmartAnimalable;
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("monsterDao");

        //测试依赖注入
        MonsterService bean = (MonsterService) ioc.getBean("monsterService");
        System.out.println("==================================测试依赖注入调用方法==================================");
        bean.m1();

        //演示AOP
        System.out.println("==================================测试AOP==================================");
        SmartAnimalable proxy = (SmartAnimalable) ioc.getBean("smartDog");
        proxy.getSub(4,1);
        proxy.getSum(2,4);
    }
}

6.结果展示

image-20240222164835898

3.该阶段完成的任务
  • 编写接口,编写实现类并将实例交给Spring容器管理,编写切面类
  • 后置处理器中进行动态代理操作,这里是对SmartDog类型的getSum方法进行动态代理
    • 首先判断beanName是不是smartDog
    • 如果是则进行动态代理,invoke方法中先判断当前执行的方法是不是getSum
    • 如果是getSum则插入前置通知和返回通知,如果不是则正常执行这个方法

2.Spring底层机制总结

1.bean的生命周期
  • 反射创建bean对象
  • 依赖注入
  • 初始化bean
  • getBean
  • 销毁bean
2.Spring容器的执行流程
  • 获取容器对象
  • 读取配置文件,得到要扫描的包
  • 扫描包下的组件,将bean定义信息放到Map中
  • 如果是单例
    • 反射创建bean对象,放到单例池中
    • 依赖注入(调用getBean)
    • 初始化bean
      • 初始化容器前调用后置处理器的before方法
      • 初始化容器之后调用后置处理器的after方法
  • 如果是多例
    • 在这个阶段什么都不做
  • getBean阶段
    • 如果是单例
      • 从单例池中获取bean对象
    • 如果是多例
      • 反射创建bean对象
      • 依赖注入(调用getBean)
      • 初始化bean
        • 初始化容器前调用后置处理器的before方法
        • 初始化容器之后调用后置处理器的after方法
      • 得到bean对象
  • 销毁bean
3.动态代理和AOP的区别
  • 动态代理:针对的是某个对象所有方法
  • AOP:针对的是某些类型所有对象具体方法
4.关于后置处理器 + 动态代理的理解
  • 后置处理器作用于Spring容器中所有类型,所有对象的初始化方法
  • getBean得到的就是后置处理器返回的bean
  • 当进入到后置处理器的时候
    • 获取切面类的所有信息
    • 将切入表达式与当前bean对象进行对比,只要类型匹配就进行动态代理
    • 使用动态代理进一步筛选要代理的方法,并根据不同的通知执行不同的操作,返回动态代理对象
    • 如果是不需要代理的方法,就不进行额外操作
5.AOP的使用方式
  • 编写接口,接口对象(注解)(用于展示AOP的效果,因为没有对象)
  • 编写切面类(注解),切面对象(注解)
  • 通知 + 切点
  • 配置beans.xnl
    • 普通注解扫描
    • 开启AOP注解
  • 具体使用
    • 获取针对接口类型的代理对象(通过id或者接口类型获取)
    • 使用代理对象调用接口的方法
  • 四种通知
    • 前置通知
    • 返回通知
    • 异常通知
    • 后置通知