手写Spring-第八章-感受波动!用Aware接口来实现感知

前言

感知,是一个很玄乎的词。其实我们可以这样理解。正常情况下,我们的bean,是不应该意识到Spring框架的存在的,它虽然被Spring所管理,但是它本身意识不到管理者的存在。从初始化,到销毁,在它自己看来,其实都是一个正常的java对象应该有的流程。只不过调用者是Spring而已。那么总有些时候,我们需要让bean【感知到】Spring框架的存在,体现在代码中,就是获取到属于Spring框架的一部分,比如bean工厂:是谁生产出自己的。ApplicationContext:自己属于哪个上下文。或者更加单纯的beanName:自己在Spring中叫什么名字。一旦我们的需求需要这些信息的时候,我们的bean就需要通过一些手段,来获取到它们。这就是Aware接口的作用。

但其实,Aware接口本身设计出来是用于Spring框架本身的,其实它不太提倡由用户去实现这些接口,因为这样的程序会难以避免的和Spring框架耦合在一起,我们知道高耦合的程序是不好的。但我们有些时候难免会遇到一些情况,需要我们这样处理。又或者像现在这样,当我们去研究Spring本身的时候,也绕不开这个点。

工程结构

├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─akitsuki
│  │  │          └─springframework
│  │  │              ├─beans
│  │  │              │  ├─exception
│  │  │              │  │      BeanException.java
│  │  │              │  │  
│  │  │              │  └─factory
│  │  │              │      │  Aware.java
│  │  │              │      │  BeanClassLoaderAware.java
│  │  │              │      │  BeanFactory.java
│  │  │              │      │  BeanFactoryAware.java
│  │  │              │      │  BeanNameAware.java
│  │  │              │      │  ConfigurableListableBeanFactory.java
│  │  │              │      │  DisposableBean.java
│  │  │              │      │  HierarchicalBeanFactory.java
│  │  │              │      │  InitializingBean.java
│  │  │              │      │  ListableBeanFactory.java
│  │  │              │      │  
│  │  │              │      ├─config
│  │  │              │      │      AutowireCapableBeanFactory.java
│  │  │              │      │      BeanDefinition.java
│  │  │              │      │      BeanFactoryPostProcessor.java
│  │  │              │      │      BeanPostProcessor.java
│  │  │              │      │      BeanReference.java
│  │  │              │      │      ConfigurableBeanFactory.java
│  │  │              │      │      DefaultSingletonBeanRegistry.java
│  │  │              │      │      PropertyValue.java
│  │  │              │      │      PropertyValues.java
│  │  │              │      │      SingletonBeanRegistry.java
│  │  │              │      │  
│  │  │              │      ├─support
│  │  │              │      │      AbstractAutowireCapableBeanFactory.java
│  │  │              │      │      AbstractBeanDefinitionReader.java
│  │  │              │      │      AbstractBeanFactory.java
│  │  │              │      │      BeanDefinitionReader.java
│  │  │              │      │      BeanDefinitionRegistry.java
│  │  │              │      │      CglibSubclassingInstantiationStrategy.java
│  │  │              │      │      DefaultListableBeanFactory.java
│  │  │              │      │      DisposableBeanAdapter.java
│  │  │              │      │      InstantiationStrategy.java
│  │  │              │      │      SimpleInstantiationStrategy.java
│  │  │              │      │  
│  │  │              │      └─xml
│  │  │              │              XmlBeanDefinitionReader.java
│  │  │              │        
│  │  │              ├─context
│  │  │              │  │  ApplicationContext.java
│  │  │              │  │  ApplicationContextAware.java
│  │  │              │  │  ConfigurableApplicationContext.java
│  │  │              │  │  
│  │  │              │  └─support
│  │  │              │          AbstractApplicationContext.java
│  │  │              │          AbstractRefreshableApplicationContext.java
│  │  │              │          AbstractXmlApplicationContext.java
│  │  │              │          ApplicationContextAwareProcessor.java
│  │  │              │          ClasspathXmlApplicationContext.java
│  │  │              │    
│  │  │              ├─core
│  │  │              │  └─io
│  │  │              │          ClasspathResource.java
│  │  │              │          DefaultResourceLoader.java
│  │  │              │          FileSystemResource.java
│  │  │              │          Resource.java
│  │  │              │          ResourceLoader.java
│  │  │              │          UrlResource.java
│  │  │              │    
│  │  │              └─util
│  │  │                      ClassUtils.java
│  │  │                
│  │  └─resources
│  └─test
│      ├─java
│      │  └─com
│      │      └─akitsuki
│      │          └─springframework
│      │              └─test
│      │                  │  ApiTest.java
│      │                  │  
│      │                  └─bean
│      │                          UserDao.java
│      │                          UserService.java
│      │                    
│      └─resources
│              spring.xml

虽然看起来很多,但实际上这一章的内容相当轻松。大部分内容都是前面积累下来的,新增的内容并没有多少。抱着轻松的心情来开始这一章吧。

我什么也不做,就想给你盖个戳

既然我们要让bean感知到Spring的存在,那么我们需要一个标记,来告诉Spring:这个bean,需要感知到你。那么对于Spring来说,这个标记,就是Aware接口。

package com.akitsuki.springframework.beans.factory;

/**
 * 标记接口,本身没有内容,仅用于提供标记功能
 * 实现这个接口及其子接口,可以被Spring容器【感知】
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/24 14:11
 */
public interface Aware {
}

空空如也的接口,没有继承,也没有自己的方法。但有了它,Spring就可以通过 instanceof操作,来知道,这个bean是否需要进行感知,从而进行统一的处理。这就像是被盖上了一个印章一样。

来,坐在这儿,你想盖哪种戳?

我们虽然说,感知Spring。但我们不可能把庞大的Spring,一股脑的全部给Bean。来,你要的Spring给你了,拿去玩吧。我们肯定是要按需分配。你告诉我,你想要哪块儿,我再把你需要的部分给你。于是,就有了Aware的各种子接口。在这里,我们介绍实现其中的四种:BeanFactory感知接口、BeanClassLoader感知接口、BeanName感知接口、ApplicationContext感知接口。

package com.akitsuki.springframework.beans.factory;

import com.akitsuki.springframework.beans.exception.BeanException;

/**
 * 实现此接口,即可感知到所属的BeanFactory
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/24 14:12
 */
public interface BeanFactoryAware extends Aware {
    /**
     * 设置所属的BeanFactory
     *
     * @param beanFactory beanFactory
     * @throws BeanException 防止出现初始化错误
     */
    void setBeanFactory(BeanFactory beanFactory) throws BeanException;
}

首先是我们的BeanFactory感知接口,提供了一个setBeanFactory方法,很好理解,bean所需要的BeanFactory,就会通过这个方法来传递给它。

package com.akitsuki.springframework.beans.factory;

/**
 * 实现此接口,即可感知到所使用的BeanClassLoader
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/24 14:14
 */
public interface BeanClassLoaderAware extends Aware {

    /**
     * 设置所使用的classLoader
     *
     * @param classLoader classLoader
     */
    void setBeanClassLoader(ClassLoader classLoader);
}

与上面类似,这里传递的是ClassLoader。

package com.akitsuki.springframework.beans.factory;

/**
 * 实现此接口,即可感知到当前bean的name
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/24 14:17
 */
public interface BeanNameAware extends Aware {

    /**
     * 设置beanName
     *
     * @param beanName beanName
     */
    void setBeanName(String beanName);
}

嗯,beanName,我们的老朋友

package com.akitsuki.springframework.context;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.Aware;

/**
 * 实现此接口,即可感知到所属的ApplicationContext
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/24 14:19
 */
public interface ApplicationContextAware extends Aware {

    /**
     * 设置ApplicationContext
     *
     * @param applicationContext applicationContext
     * @throws BeanException applicationContext中的方法可能会抛出此异常
     */
    void setApplicationContext(ApplicationContext applicationContext) throws BeanException;
}

最后出场的是我们的重量级选手,ApplicationContext。

可以看到,这些接口的方法都差不多,都是将bean需要的内容,传递给它。那么我们来思考一下,这些东西,要在什么时候传递给bean呢?很自然的想法就是,在创建它的时候。但是这个时候我们就犯了难:在创建bean的时候,我们是拿不到ApplicationContext的>_<!这个时候,beanFactory、classLoader、beanName,我们都可以获取到,唯独ApplicationContext拿不到。这要怎么办?往前想想,前面我们实现的一个功能,可以帮助我们实现这一个需求。嗯…想到了吗?现在来揭晓答案:bean后置处理器!我们可以在ApplicationContext的刷新方法中,将自身作为参数传入处理器中,再将处理器注册到beanFactory中。最后,在beanFactory创建bean的时候,就可以在执行bean处理器的时候操作ApplicationContext了,真的是很好玩的一个设计。

后置处理,解决大问题

来,我们看看这个后置处理器要怎么设计

package com.akitsuki.springframework.context.support;

import com.akitsuki.springframework.beans.exception.BeanException;
import com.akitsuki.springframework.beans.factory.config.BeanPostProcessor;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.context.ApplicationContextAware;
import lombok.AllArgsConstructor;

/**
 * ApplicationContext感知包装处理器,由于ApplicationContext的创建在Bean创建之后
 * 所以不能在创建bean的时候就拿到,就需要后置处理器,在bean初始化之前来将ApplicationContext进行注入
 *
 * @author ziling.wang@hand-china.com
 * @date 2022/11/24 14:28
 */
@AllArgsConstructor
public class ApplicationContextAwareProcessor implements BeanPostProcessor {

    private ApplicationContext applicationContext;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeanException {
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(applicationContext);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeanException {
        return bean;
    }
}

嗯嗯,非常简单易懂的设计,判断bean实现了 ApplicationContextAware接口,就对其进行强转,再调用 setApplicationContext方法,将applicationContext设置进去即可。顺带一提,这里用lombok的 @AllArgsConstructor注解,省略了有参构造方法的编写,实际上这里应该是要传入进来 ApplicationContext来对属性进行初始化的,需要注意。

接下来是 refresh方法的改造,方法位于 AbstractApplicationContext中。

@Override
    public void refresh() throws BeanException {
        //创建beanFactory,加载beanDefinition
        refreshBeanFactory();
        //获取beanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //添加ApplicationContextAwareProcessor
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        //在bean实例化之前,执行BeanFactoryPostProcessor
        invokeBeanFactoryPostProcessors(beanFactory);
        //注册BeanPostProcessor
        registerBeanPostProcessors(beanFactory);
        //提前实例化单例bean对象
        beanFactory.preInstantiateSingletons();
    }

可以看到,方法中我们增加了一步:向beanFactory中添加了后置处理器。这样就可以保证在bean初始化的时候,执行到这个处理器了。由此也可以看出,Spring设计的这些功能,并不仅仅是用来给用户使用的,Spring内部本身也大量的用到了这些设计。

你要这个是吗?好,给你

那么接口我们也定义好了,难题也解决了。接下来就该正式的去实现这些方法了。我们刚才也说到,应该在创建bean的时候,将这些感知对象传递进去,那么我们就来改造一下创建bean的过程。让我看看!AbstractAutowireCapableBeanFactory


    /**
     * 初始化bean
     *
     * @param beanName       bean名称
     * @param bean           待初始化bean
     * @param beanDefinition bean定义
     * @return bean
     */
    private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
        //执行Aware感知操作
        invokeAwareMethods(beanName, bean);
        //执行beanPostProcessor before处理
        Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        //执行bean初始化内容
        try {
            invokeInitMethod(beanName, wrappedBean, beanDefinition);
        } catch (Exception e) {
            throw new BeanException("执行bean初始化方法失败,bean名称:" + beanName);
        }
        //执行beanPostProcessor after处理
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        return wrappedBean;
    }


    /**
     * 执行bean注册的感知接口相应操作
     *
     * @param beanName
     * @param bean
     */
    private void invokeAwareMethods(String beanName, Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(this);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getClassLoader());
            }
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
        }
    }

可以看到,我们把操作放在了初始化中,在执行初始化前置处理之前,我们先执行了Aware感知的相关操作。操作的内容和前面对ApplicationContext的类似,我们就不多说了。invokeAwareMethods方法执行完毕后,紧接着就会到bean初始化前后置处理器的执行。在那里又会对ApplicationContext进行处理。

这里有一点点地方需要注意的是,可以看到对BeanClassLoader的处理,这里调用了getClassLoader方法。但实际上前面我们并没有这个方法。这个方法是这次新加的,加在了 AbstractBeanFactory中,内容也很简单。

private ClassLoader classLoader = ClassUtils.getDefaultClassLoader();  


public ClassLoader getClassLoader() {
    return this.classLoader;
}

我全都要!快给我!

好了,这次我们的测试环节来的格外的快,因为这次内容还是相对来说比较简单的(虽然不像第一章那样简单)。来看看我们bean的变化吧

package com.akitsuki.springframework.test.bean;

import com.akitsuki.springframework.beans.factory.*;
import com.akitsuki.springframework.context.ApplicationContext;
import com.akitsuki.springframework.context.ApplicationContextAware;
import lombok.Getter;
import lombok.Setter;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/8 14:42
 */
@Getter
@Setter
public class UserService implements InitializingBean, DisposableBean,
        BeanFactoryAware, BeanClassLoaderAware, BeanNameAware, ApplicationContextAware {

    private BeanFactory beanFactory;

    private ClassLoader beanClassLoader;

    private String beanName;

    private ApplicationContext applicationContext;

    private String dummyString;

    private int dummyInt;

    private UserDao userDao;

    public void queryUserInfo(Long id) {
        System.out.println("dummyString:" + dummyString);
        System.out.println("dummyInt:" + dummyInt);
        String userName = userDao.queryUserName(id);
        if (null == userName) {
            System.out.println("用户未找到>_<");
        } else {
            System.out.println("用户名:" + userDao.queryUserName(id));
        }
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("userService的destroy执行了");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("userService的afterPropertiesSet执行了");
    }
}

嗯,真的是我全都要。实现了我们上面完成的那四个接口,这也导致了我们的UserService实现的接口数量达到了可怕的6个,承受着这个年纪不该承受的重量。至于这里看起来没有实现接口方法的原因,是因为这些接口都是set方法,而这里通过lombok的@Setter注解,刚好为这些属性提供了setter方法,所以就没有显式的去写了。

接下来是我们的正式测试类

package com.akitsuki.springframework.test;

import com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;

/**
 * @author ziling.wang@hand-china.com
 * @date 2022/11/15 13:58
 */
public class ApiTest {

    @Test
    public void test() {
        //初始化BeanFactory
        ClasspathXmlApplicationContext context = new ClasspathXmlApplicationContext("classpath:spring.xml");
        context.registerShutdownHook();

        //获取bean,测试
        UserService userService = context.getBean("userService", UserService.class);
        userService.queryUserInfo(1L);
        userService.queryUserInfo(4L);
        userService.queryUserInfo(114L);
        System.out.println("beanFactory:" + userService.getBeanFactory());
        System.out.println("beanClassLoader:" + userService.getBeanClassLoader());
        System.out.println("beanName:" + userService.getBeanName());
        System.out.println("applicationContext:" + userService.getApplicationContext());
    }
}

测试结果

执行UserDao的initMethod
userService的afterPropertiesSet执行了
dummyString:dummy
dummyInt:114514
用户名:akitsuki
dummyString:dummy
dummyInt:114514
用户名:hanazawa
dummyString:dummy
dummyInt:114514
用户未找到>_<
beanFactory:com.akitsuki.springframework.beans.factory.support.DefaultListableBeanFactory@3551a94
beanClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
beanName:userService
applicationContext:com.akitsuki.springframework.context.support.ClasspathXmlApplicationContext@531be3c5
执行UserDao的destroyMethod
userService的destroy执行了

Process finished with exit code 0

可以看到,我们新加的那些Spring的部分,都已经被注入进来了。这也就意味着,我们的bean成功的感知到了Spring的部分。终于,这次练习也圆满结束了,值得鼓励。

相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring,这里对应的代码是mini-spring-08

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值