自实现Spring IOC和AOP(下)

1.概述

上文实现的IOC和AOP功能很单一,且IOC和AOP两个模块没有整合到一起。IOC在加载bean过程中,AOP不能对bean织入通知。升级版IOC和AOP的功能:
1.根据xml配置文件加载相关bean
2.对BeanPostProcessor类型的bean提供支持
3.对BeanFactoryAware类型的bean提供支持
4.实现了基于JDK动态代理的AOP
5.整合了IOC和AOP,使得二者可很好的协同工作

2.IOC的实现

2.1.BeanFactory的生命流程

1.BeanFactory加载Bean配置文件,将读到的Bean配置封装成BeanDefinition对象
2.将封装好的BeanDefinition对象注册到BeanDefinition容器中
3.注册BeanPostProcessor相关实现类到BeanPostProcessor容器中
4.BeanFactory进入就绪状态
5.外部调用BeanFactory的getBean(String name)方法,BeanFactory着手实例化相应的bean
6.重复步骤3和4,直到程序退出,BeanFactory被销毁。

2.2.BeanDefinition及其他一些类的介绍

实现IOC会用到一些辅助类,包括BeanDefinition、BeanReferece、PropertyValues、PropertyValue,这些类与xml的解析紧密相关。先从BeanDefinition开始介绍。

BeanDefinition,从字面意思上翻译成中文就是"Bean的定义",即根据Bean配置信息生成的Bean详情对象。

在具体实现中,BeanDefinition和xml是怎么对应的呢?如图。在这里插入图片描述

上图中的ref对应的BeanReference对象。BeanReference对象保存的是bean配置中ref属性对应的值,在后续BeanFactory实例化bean时,会根据BeanReference保存的值去实例化bean依赖的其他bean。

接下来看看PropertyValue和PropertyValues这两个类。首先是PropertyValue,PropertyValue中有两个字段name和value,用于记录bean配置中的标签的属性值。然后是PropertyValues,PropertyValues从字面意思上来看,是PropertyValue复数形式,在功能上等同于List。那么为什么不直接使用List,而自己定义一个新类呢?答案是要获得一定的控制权,看下面的代码:

public class PropertyValues {

    private final List<PropertyValue> propertyValueList = new ArrayList<>();

    public void addPropertyValue(PropertyValue pv) {
        // 这里使用一个PropertyValues来封装,而没有简单使用list的原因是:可以对添加的pv元素,进行操作
        this.propertyValueList.add(pv);
    }

    public List<PropertyValue> getPropertyValueList() {
        return propertyValueList;
    }
}

2.3.xml的解析

BeanFactory初始化时,会根据传入的xml配置文件路径加载并解析配置文件。但是BeanFactory并不处理其他的工作,只管理容器中的bean。于是BeanFactory将加载和解析配置文件的任务委托给BeanDefinitionReader的实现类XmlBeanDefinitionReader去做。那么XmlBeanDefinitionReader具体是怎么做的呢?XmlBeanDefinitionReader做了如下几件事情:

1.将xml配置文件加载到内存中
2.获取根标签下所有的标签
3.遍历获取到的标签列表,并从标签中读取id,class属性
4.创建BeanDefinition对象,并将刚刚读取到的id,class属性值保存到对象中
5.遍历标签下的标签,从中读取属性值,并保持在BeanDefinition对象中
6.将< id, BeanDefintion>键值对缓存在Map中,留作后用
7.重复3、4、5、6步,直到解析结束

2.4.注册BeanPostProcessor

BeanPostProcessor接口是Spring对外扩展的接口之一,其主要用途提供一个机会,让开发人员能够插手bean的实例化过程。通过实现这个接口,我们就可在bean实例化时,对bean进行一些处理。比如,我们所熟悉的AOP就是在这里将切面逻辑织入相关bean中的。正是因为有了BeanPostProcessor接口作为桥梁,才使得AOP可以和IOC容器产生联系。

接下来说说BeanFactory是怎么注册BeanPostProcessor相关实现类的。

XmlBeanDefinitionReader在完成解析工作后,BeanFactory会将它解析得到的< id, BeanDefinition>键值对注册到自己的beanDefinitionMap中。BeanFactory注册好BeanDefinition后,就立即开始注册BeanPostProcessor相关实现类。过程如下:

1.根据BeanDefinition记录的信息,寻找所有实现了BeanPostProcessor接口的类。
2.实例化BeanPostProcessor接口的实现类
3.把实例化好的对象放入List中
4.重复2、3步,直至所有的实现类完成注册

2.5.getBean过程解析

在完成了xml的解析、BeanDefinition的注册以及BeanPostProcessor的注册过程后。BeanFactory初始化的工作就结束了,此时BeanFactory处于就绪状态,等待外部程序的调用。

外部程序一般都是通过调用BeanFactory的getBean(String name)方法来获取容器中的bean。BeanFactory具有延迟实例化bean的特性,也就是等外部程序需要的时候,才实例化相关的bean。这样做的好处是比较显而易见的,第一是提高了BeanFactory的初始化速度,第二是节省了内存资源。Bean的实例化过程:

在这里插入图片描述
上图是完整的Spring bean实例化过程图。而这个项目中简化的bean实例化的过程,如下:
在这里插入图片描述
简化后的实例化流程如下:
1.实例化bean对象,类似于new XXObject()
2.将配置文件中配置的属性填充到刚刚创建的bean对象中
3.检查bean对象是否实现了Aware一类的接口,如果实现了则把相应的依赖设置到bean对象中。目前仅对BeanFactoryAware接口实现类提供了支持
4.调用BeanPostProcessor前置处理方法,即postProcessBeforeInitialization(Object bean,String name)
5.调用BeanPostProcessor后置处理方法,即postProcessAfterInitializationn(Object bean,String name)
6.bean对象处于就绪状态,可以是用了

源码可以在gitee上下载阅读handwritten_spring

3.实现AOP

3.1.AOP原理

AOP是基于动态代理模式实现的,具体实现上可以基于JDK动态代理或者Cglib动态代理。其中JDK动态代理只能代理实现了接口的对象,而Cglib动态代理则无此限制。所以在为没有实现接口的对象生成代理时,只能使用cglib。在 handwritten_spring项目中,暂时只实现了基于JDK动态dialing对象生成器。

AOP的流程
1.AOP逻辑介入BeanFactory实例化bean的过程
2.根据Pointcut定义的匹配规则,判断当前正在实例化的bean是否符合规则
3.如果符合代理生成器将切面逻辑Advice织入bean相关方法中,并为目标bean生成代理对象
4.将生成的bean的代理对象返回给BeanFactory容器。到此,AOP逻辑执行结束。

3.2.基于JDK动态代理的AOP实现

在这个项目中,代理对象生成器的逻辑主要写在了JdkDynamicAopProxy类中,这个类的有两个方法,其中getProxy方法用于生成代理对象。invoke方法是InvocationHandler接口的具体实现,包含了将通知(Advice)织入相关方法中,看一下JdkDynamicAopProxy:

JDKDynamicAopProxy实现代码:

public abstract class AbstractAopProxy implements AopProxy{

    protected AdvisedSupport advised;

    public AbstractAopProxy(AdvisedSupport advised) {
        this.advised = advised;
    }
}

public class JdkDynamicAopProxy extends AbstractAopProxy implements InvocationHandler {

    public JdkDynamicAopProxy(AdvisedSupport advised) {
        super(advised);
    }


    /**
     * 为目标 bean 生成代理对象
     * @return bean 的代理对象
     * @author ZhouXuan
     * @date 2021/12/7 20:40
     */
    @Override
    public Object getProxy() {
        return Proxy.newProxyInstance(getClass().getClassLoader(), advised.getTargetSource().getInterfaces(), this);
    }

    /**
     * InvocationHandler 接口中的 invoke 方法具体实现,封装了具体的代理逻辑
     * @param proxy:
     * @param method:
     * @param args:
     * @return java.lang.Object 代理方法或原方法的返回值
     * @author ZhouXuan
     * @date 2021/12/7 20:48
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodMatcher methodMatcher = advised.getMethodMatcher();

        // 使用方法匹配器 methodMatcher 测试 bean 中原始方法 method 是否符合匹配规则
        if (methodMatcher != null && methodMatcher.matchers(method, advised.getTargetSource().getTargetClass())) {

            // 获取 Advice.MethodInterceptor 的父接口继承了 Advice
            MethodInterceptor methodInterceptor = advised.getMethodInterceptor();

            // 将 bean 的原始 method 封装成 MethodInvocation 实现类对象
            // 将生成的对象传给 Advice 实现类对象,执行通知逻辑
            return methodInterceptor.invoke(
                    new ReflectiveMethodInvocation(advised.getTargetSource().getTarget(), method, args)
            );
        } else {
            // 当前 method 不符合匹配规则,直接调用 bean 中的原始 method
            return method.invoke(advised.getTargetSource().getTarget(),args);
        }
    }
}

下面这个流程图对织入逻辑进行总结:
在这里插入图片描述
对JdkDynamicAopProxy进行简单的测试,测试代码及结果如下

测试类:

public class LogInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println(invocation.getMethod().getName() + " method start");
        Object obj = invocation.proceed();
        System.out.println(invocation.getMethod().getName() + " method end");
        return obj;
    }
}

public class JdkDynamicAopProxyTest {


    @Test
    public void getProxy() throws Exception {
        System.out.println("-----no proxy-----");
        HelloService helloService = new HelloServiceImpl();
        helloService.sayHelloWorld();

        System.out.println("--------proxy------");
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setMethodInterceptor(new LogInterceptor());

        TargetSource targetSource = new TargetSource(helloService, HelloServiceImpl.class, HelloServiceImpl.class.getInterfaces());
        advisedSupport.setTargetSource(targetSource);
        advisedSupport.setMethodMatcher((Method method, Class beanClass) -> true);

        helloService = (HelloService) new JdkDynamicAopProxy(advisedSupport).getProxy();
        helloService.sayHelloWorld();
    }
}

测试结果:
在这里插入图片描述

3.3.AOP与IOC协作

在 handwritten_spring中,AOP和IOC产生联系的具体实现类是AspectJAwareAdvisorAutoProxyCreator(下面简称AutoProxyCreator),这个类实现了BeanPostProcessor和BeanFactoryAware接口。BeanFactory在注册BeanPostProcessor接口相关实现类的阶段,会将其本身注入到AutoProxyCreator中,为后面AOP给bean生成代理对象做准备。BeanFactory初始化结束后,AOP与IOC桥梁类AutoProxyCreator也完成了实例化,并被缓存在BeanFactory中,静待BeanFactory实例化bean。当外部产生调用,BeanFactory开始实例化bean时。AutoProxyCreator也开始工作了,工作细节如下:

1.从BeanFactory查找实现了PointcutAdvisor接口的切面对象,切面对象中包含了实现Pointcut和Advice接口的对象。
2.使用Pointcut中的表达式对象匹配当前bean对象。如果匹配成功,进行下一步。否则终止逻辑,返回bean。
3.JdkDynamicAopProxy对象为匹配到的bean生成代理对象,并将代理对象返回给BeanFactory。

经过上面3步,AutoProxyCreator就把原来的bean替换为代理对象了。最后handwritten_spring AOP剩余的实现代码贴出来:

public class AspectJAwareAdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {

    private XmlBeanFactory xmlBeanFactory;


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        // 这里两个if判断很有必要,如果删除将会使程序进入死循环状态
        // 最终导致 StackOverflowError 错误发生
        if (bean instanceof AspectJExpressionPointcutAdvisor) {
            return bean;
        }
        if (bean instanceof MethodInterceptor) {
            return bean;
        }

        // 1. 从BeanFactory 查找 AspectJExpressionPointcutAdvisor 类型的对象
        List<AspectJExpressionPointcutAdvisor> advisors = xmlBeanFactory.getBeansForType(AspectJExpressionPointcut.class);
        for (AspectJExpressionPointcutAdvisor advisor : advisors) {

            // 2.使用 Pointcut 对象匹配当前 bean对象
            if (advisor.getPointcut().getClassFilter().matchers(bean.getClass())) {
                ProxyFactory advisedSupport = new ProxyFactory();
                advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
                advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());

                TargetSource targetSource = new TargetSource(bean, bean.getClass(), bean.getClass().getInterfaces());
                advisedSupport.setTargetSource(targetSource);

                // 3.生成代理对象,并返回
                return advisedSupport.getProxy();
            }
        }

        // 3.匹配失败,返回 bean
        return bean;
    }


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws Exception {
        xmlBeanFactory = (XmlBeanFactory) beanFactory;
    }
}

ProxyFactory实现代码:

public class ProxyFactory extends AdvisedSupport implements AopProxy{

    @Override
    public Object getProxy() {
        return createAopProxy().getProxy();
    }

    private AopProxy createAopProxy() {
        return new JdkDynamicAopProxy(this);
    }
}

测试类:

public class XmlBeanFactoryTest {

    @Test
    public void getBean() throws Exception {
        System.out.println("------AOP test---------");
        String location = getClass().getClassLoader().getResource("toy-spring.xml").getFile();
        XmlBeanFactory bf = new XmlBeanFactory(location);
        HelloService helloService = (HelloService)bf.getBean("helloService");
        helloService.sayHelloWorld();
    }
}

测试结果:
·在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值