Spring底层之后置处理器机制的实现

1、认识后置处理器

1.1. 后置处理器的作用

Spring的后置处理器是干什么用的呢?帮各位回顾一下,后置处理器能够对Spring容器初始化后的bean对象进行修改,比如Aop在执行后置处理器的After方法之后就将bean对象变成了代理对象,从而实现切面编程。由此可想而知,后置处理器还是相当重要的。

请读者朋友记住2点:

  1. 后置处理器是用来修改bean对象的
  2. 后置处理器和Aop有着千丝万缕的关系

1.2. 后置处理器代码演示

为了了解后置处理器,我们下面创建一个工程,注意这个和我们实现Spring底层核心机制的工程不是一个。

  • 创建Maven工程spring-learning-zhl
  • 创建包:com.zhl.spring
  • 在pom.xml文件中添加如下配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring-learning-zhl</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

</project>
  • 在com.zhl.spring.component包下创建UserDao类

package com.zhl.spring.component;

import org.springframework.stereotype.Repository;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/11 16:34
 * @Desc:
 **/
@Repository
public class UserDao {
    public void hi() {
        System.out.println("UserDao hi() 被调用...");
    }
}

  • 在com.zhl.spring.entry包下创建House类

package com.zhl.spring.entry;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/11 16:28
 * @Desc: 实体类House
 **/
public class House {
    private String name;

    public House() {
        System.out.println("House() 构造器...");
    }

    public House(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        System.out.println("House() Setter方法被调用...");
        this.name = name;
    }


    @Override
    public String toString() {
        return "House{" +
                "name='" + name + '\'' +
                '}';
    }

    /**
     * 初始化方法
     * 1. 根据公司业务逻辑来写
     * 2. 名字可以更改
     */
    public void init() {
        System.out.println("House init()..");
    }

    /**
     * 销毁方法
     * 1. 根据公司业务逻辑来写
     * 2. 名字可以更改
     */
    public void  destroy(){
        System.out.println("House destroy()..");
    }
}

  • 在com.zhl.spring.process包下创建MyBeanPostProcessor类
/**
 * Author: zhl
 * Date:   2023/4/11 16:32
 * Desc:
 */

package com.zhl.spring.process;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/11 16:32
 * @Desc: 后置处理器
 **/
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * 在 bean 初始化之前完成某些任务
     *
     * @param bean     ioc容器返回的bean对象,如果这里被替换会修改,则返回的bean对象也会被修改
     * @param beanName ioc容器配置的bean的名称
     * @return 返回的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization 被调用" + beanName + " bean=" + bean.getClass());
        return bean;
    }

    /**
     * 在 bean初始化之后完成某些任务
     *
     * @param bean    ioc容器返回的bean对象,如果这里被替换会修改,则返回的bean对象也会被修改
     * @param beanName ioc容器配置的bean的名称
     * @return 返回的bean对象
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization被调用 " + beanName + " bean=" + bean.getClass());
        return bean;
    }
}

  • 在resource目录下创建beans.xml
  • 对beans.xml做如下配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置包扫描-->
    <context:component-scan base-package="com.zhl.spring.component"/>
    <context:component-scan base-package="com.zhl.spring.entry"/>

    <!--配置后置处理器-->
    <bean id="myBeanPostProcessor" class="com.zhl.spring.process.MyBeanPostProcessor"/>

    <bean id="house" class="com.zhl.spring.entry.House" init-method="init" destroy-method="destroy">
        <property name="name" value="杭州豪宅"/>
    </bean>
</beans>

这里主要就是配置了包扫描,使spring能够扫描到component和entry这两个包下的bean,另外还配置了House和后置处理器,其实后置处理器也是Spring容器中的bean,UserDao我们使用了注解方式,House这里也使用了xml配置的方式。

  • com.zhl.spring包下编写测试类MainApp
package com.zhl.spring;

import com.zhl.spring.entry.House;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/11 16:35
 * @Desc: 测试类
 **/
public class MainApp {
    // 执行顺序为:构造方法->Setter方法->Before方法->初始化方法->After方法->获取到bean对象执行的方法->销毁方法
    @Test
    public void BeanPostProcessorTest(){
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        House house = ioc.getBean(House.class);
        System.out.println("豪宅名字="+house.getName());

        // 关闭容器
        ((ConfigurableApplicationContext) ioc).close();
    }
}

  • 执行结果
postProcessBeforeInitialization 被调用userDao bean=class com.zhl.spring.component.UserDao
postProcessAfterInitialization被调用 userDao bean=class com.zhl.spring.component.UserDao
House() 构造器...
House() Setter方法被调用...
postProcessBeforeInitialization 被调用house bean=class com.zhl.spring.entry.House
House init()..
postProcessAfterInitialization被调用 house bean=class com.zhl.spring.entry.House
豪宅名字=杭州豪宅
House destroy()..

Process finished with exit code 0

这里我们看到UserDao和House都执行了Before和After方法,另外我们需要注意的是后置处理器Before方法和After方法的执行顺序,这个可以从House的执行结果中看出:构造方法->Setter方法->Before方法->初始化方法->After方法->获取到bean对象执行的方法->销毁方法,请读者记住此执行顺序,很重要!

2、Spring后置处理器的实现

下面的代码是基于上篇文章<<Spring底层之多例模式及依赖注入机制的实现>>实现的,如果小伙伴们没有查阅上篇文章,下面会有点难度哦~

2.1. 定义初始化接口

从上面的代码中我们知道后置处理器的before和after方法是在初始化方法前后执行的,所以我们就需要确定一个初始化方法,这里我们参考原生Spring规范来定义这个接口。

那为什么要把这个初始化方法定义成接口呢?因为Spring容器在创建好bean对象之后,要根据这个bean对象是否实现了初始化接口来判断是否执行初始化方法,用bean对象 instanceof 初始化接口 做出判断,如果bean对象是初始化接口的子类,那么就执行初始化方法,否则就不执行。这其实就是Java基础接口编程的实际应用。

  • 原生Spring初始化方法接口
package org.springframework.beans.factory;

/**
 * Interface to be implemented by beans that need to react once all their properties
 * have been set by a {@link BeanFactory}: e.g. to perform custom initialization,
 * or merely to check that all mandatory properties have been set.
 *
 * <p>An alternative to implementing {@code InitializingBean} is specifying a custom
 * init method, for example in an XML bean definition. For a list of all bean
 * lifecycle methods, see the {@link BeanFactory BeanFactory javadocs}.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see DisposableBean
 * @see org.springframework.beans.factory.config.BeanDefinition#getPropertyValues()
 * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getInitMethodName()
 */
public interface InitializingBean {

	/**
	 * Invoked by the containing {@code BeanFactory} after it has set all bean properties
	 * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
	 * <p>This method allows the bean instance to perform validation of its overall
	 * configuration and final initialization when all bean properties have been set.
	 * @throws Exception in the event of misconfiguration (such as failure to set an
	 * essential property) or if initialization fails for any other reason
	 */
	void afterPropertiesSet() throws Exception;

}

  • 自定义初始化接口

在com.zhl.spring.process包下创建InitializingBean接口

package com.zhl.spring.processe;

/**
 * TODO
 * @Author zhl
 * @Date 2023/4/11 16:09
 * @Desc: 在该接口定义初始化方法
 **/
public interface InitializingBean {

    // 该方法在setter方法之后执行
    void afterPropertiesSet() ;
}

2.2. MonsterService实现初始化接口


package com.zhl.spring.component;


import com.zhl.spring.annotation.Autowired;
import com.zhl.spring.annotation.Component;
import com.zhl.spring.annotation.Scope;
import com.zhl.spring.processe.InitializingBean;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/1 16:31
 * @Desc:
 **/

@Component(value = "service01")
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {

    @Autowired
    private MonsterDao monsterDao;

    public void sayHello(){
        monsterDao.sayHi();
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("MonsterService 进行初始化, 具体业务由程序员来搞定....");
    }
}

2.3. 修改类ZhlSpringApplicationContext

我们主要修改的是 在bean对象创建成功之后,判断bean对象是否是初始化接口的子类,如果是我们就执行初始化方法。修改的地方是在createBean() 方法中,修改后为:

 public Object createBean(BeanDefinition beanDefinition) {
        // 1. 获取class对象
        Class aClass = beanDefinition.getaClass();
        Object instance = null;
        try {
            instance = aClass.newInstance();
            // 2. 如果存在依赖注入,遍历所有字段,完成注入
            for (Field declaredField : aClass.getDeclaredFields()) {
                // 3. 判断当前字段是否标有Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    // 4. 获取字段名
                    String beanName = declaredField.getName();
                    // 5. 根据字段名获取bean对象
                    Object bean = getBean(beanName);
                    // 6. 完成组装
                    declaredField.setAccessible(true);
                    declaredField.set(instance, bean);
                }
            }

            System.out.println("=====创建好实例====" + instance);

            if (instance instanceof InitializingBean) {
               try {
                  ((InitializingBean)instance).afterPropertiesSet();
               } catch (Exception e) {
                  throw new RuntimeException(e);
               }
            }

        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return instance;
    }

2.4. 执行测试类,查看MonsterService是否执行初始化方法


package com.zhl.spring;


import com.zhl.spring.component.MonsterDao;
import com.zhl.spring.component.MonsterService;
import com.zhl.spring.ioc.ZhlSpringApplicationContext;
import com.zhl.spring.ioc.ZhlSpringConfig;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/1 16:38
 * @Desc: 测试类
 **/
public class AppMain {
    public static void main(String[] args) {
        ZhlSpringApplicationContext ioc = new ZhlSpringApplicationContext(ZhlSpringConfig.class);
        MonsterService monsterService = (MonsterService) ioc.getBean("service01");
        monsterService.sayHello();
        System.out.println("ok~~~");
    }
}

执行结果:

=====创建好实例====com.zhl.spring.component.MonsterDao@7440e464
=====创建好实例====com.zhl.spring.component.MonsterService@2626b418
MonsterService 进行初始化, 具体业务由程序员来搞定....
hello~~,sir
ok~~~

Process finished with exit code 0

可以看到,MonsterService初始化方法执行了,MonsterDao没有执行初始化方法,因为MonsterDao没有实现初始化接口,这说明我们的代码是正确的,那下面我们的重点就是后置处理器了。

2.5. 定义后置处理器接口

这里我们还是参看原生Spring规范

  • 原生Spring后置处理器接口
package org.springframework.beans.factory.config;

import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;

/**
 * Factory hook that allows for custom modification of new bean instances &mdash;
 * for example, checking for marker interfaces or wrapping beans with proxies.
 *
 * <p>Typically, post-processors that populate beans via marker interfaces
 * or the like will implement {@link #postProcessBeforeInitialization},
 * while post-processors that wrap beans with proxies will normally
 * implement {@link #postProcessAfterInitialization}.
 *
 * <h3>Registration</h3>
 * <p>An {@code ApplicationContext} can autodetect {@code BeanPostProcessor} beans
 * in its bean definitions and apply those post-processors to any beans subsequently
 * created. A plain {@code BeanFactory} allows for programmatic registration of
 * post-processors, applying them to all beans created through the bean factory.
 *
 * <h3>Ordering</h3>
 * <p>{@code BeanPostProcessor} beans that are autodetected in an
 * {@code ApplicationContext} will be ordered according to
 * {@link org.springframework.core.PriorityOrdered} and
 * {@link org.springframework.core.Ordered} semantics. In contrast,
 * {@code BeanPostProcessor} beans that are registered programmatically with a
 * {@code BeanFactory} will be applied in the order of registration; any ordering
 * semantics expressed through implementing the
 * {@code PriorityOrdered} or {@code Ordered} interface will be ignored for
 * programmatically registered post-processors. Furthermore, the
 * {@link org.springframework.core.annotation.Order @Order} annotation is not
 * taken into account for {@code BeanPostProcessor} beans.
 *
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 10.10.2003
 * @see InstantiationAwareBeanPostProcessor
 * @see DestructionAwareBeanPostProcessor
 * @see ConfigurableBeanFactory#addBeanPostProcessor
 * @see BeanFactoryPostProcessor
 */
public interface BeanPostProcessor {

	/**
	 * Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 */
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
	 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
	 * post-processor can decide whether to apply to either the FactoryBean or created
	 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
	 * <p>This callback will also be invoked after a short-circuiting triggered by a
	 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
	 * in contrast to all other {@code BeanPostProcessor} callbacks.
	 * <p>The default implementation returns the given {@code bean} as-is.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.beans.factory.FactoryBean
	 */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

这里有2个方法,我们借鉴过来

  • 自定义后置处理器接口

在com.zhl.spring.processor包下创建BeanPostProcessor


package com.zhl.spring.processor;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/12 17:41
 * @Desc:
 **/
public interface BeanPostProcessor {


    /**
     * postProcessBeforeInitialization在初始化之前调用
     * @param bean bean
     * @param beanName  beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName)  {
        return bean;
    }


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

2.6. 实现后置处理器接口

我们在com.zhl.spring.component包下创建一个类ZhlBeanPostProcessor,实现后置处理器接口。

package com.zhl.spring.component;

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

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/12 17:51
 * @Desc: 后置处理器 注意:后置处理器本身也是容器中的一个bean
 **/
@Component
public class ZhlBeanPostProcessor implements BeanPostProcessor {
    /**
     * 该方法时在 bean 创建好后,进行初始化前调用
     * @param bean bean 创建好的bean 对象
     * @param beanName  创建好的bean 的名字
     * @return
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        //这里程序员来决定业务逻辑,spring 只是提供处理机制
        System.out.println("postProcessBeforeInitialization 被调用 " + beanName + " bean= " + bean.getClass());
        return bean;
    }

    /**
     * 该方法时在bean 创建好后,初始化完成后调用
     * @param bean : 创建好的bean 对象
     * @param beanName : 创建好的bean 的名字
     * @return
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        //这里程序员来决定业务逻辑,spring 只是提供处理机制
        System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
        return bean;
    }


}

2.7. 再次修改ZhlSpringApplicationContext

现在我们的初始化方法可以打印出来,后置处理器接口和类也都已经创建好了,前置工作我们已经准备完毕。下面我们就唠唠后置处理器的before和after方法在初始化方法前后执行应该怎么去实现?其实很简单,那我们只需要在初始化方法前后拿到了实现后置处理器接口的实例化对象,然后用对象去调before或after方法不就行了吗?那各位可以试想一下,我们应该在哪里获取到这些对象呢?

答案就是通过全路径获取到包下所有Class对象的时候,此时对Class对象进行判断,如果该Class对象的实例是BeanPostProcessor的子类,那么我们就可以将该Class对象进行实例化,然后将对象放入List中,那最后只需要在初始化方法之前遍历对象,调用before方法,在初始化方法之后遍历,调用after方法即可。

注意:后置处理器是有多个的,所以我们可以将其放入List中

  • 首先我们应该创建List集合,用来接收BeanPostProcessor子类的实例化对象
    private final List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
  • 然后我们得把实现了后置处理器接口的实例化对象放入到集合中

主要添加的代码是

        // TODO 判断aClass对象是不是BeanPostProcessor的子类或相同类型
        if (BeanPostProcessor.class.isAssignableFrom(aClass)){
            // 如果是BeanPostProcessor的子类,那么就将其实例化,放入到List中
            BeanPostProcessor beanPostProcessor = (BeanPostProcessor)aClass.newInstance();
            beanPostProcessorList.add(beanPostProcessor);
        }

该代码在整个方法中的位置是:

 public void BeanDefinitionByScan(Class configClass) {
        this.configClass = configClass;
        // 1. 获取注解的value值
        // 1.1 首先获取到注解对象
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String value = componentScan.value(); // value=com.zhl.spring.component
        // 2. 通过类加载器拿到资源路径
        ClassLoader classLoader = ZhlSpringApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(value.replace(".", "/"));
        String resourcePath = resource.getPath(); // /C:/App/develop/spring-learning/spring5-zhl/target/classes/com/zhl/spring/component
        // 3. 通过资源路径拿到每个bean的绝对路径
        File file = new File(resourcePath);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File document : files) {
                String absolutePath = document.getAbsolutePath(); // C:\App\develop\spring-learning\spring5-zhl\target\classes\com\zhl\spring\component\MonsterDao.class
                // 4. 通过绝对路径拿到每个bean的类名
                String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf(".class"));
                // 5. 将包路径和类名进行拼接得到全类名,为下面反射处理准备
                String fullClassName = value + "." + className; // com.zhl.spring.component.MonsterDao
                try {
                    // 6. 将包路径下所有的bean进行反射
                    Class<?> aClass = classLoader.loadClass(fullClassName);


                    // TODO 判断aClass对象是不是BeanPostProcessor的子类或相同类型
                    if (BeanPostProcessor.class.isAssignableFrom(aClass)){
                        // 如果是BeanPostProcessor的子类,那么就将其实例化,放入到List中
                        BeanPostProcessor beanPostProcessor = (BeanPostProcessor)aClass.newInstance();
                        beanPostProcessorList.add(beanPostProcessor);
                    }



                    // 7. 根据bean的class对象判断bean上是否有Component注解,
                    // 在这里我们只举例component注解,其余Service Repository注解原理和Component一样
                    if (aClass.isAnnotationPresent(Component.class)) {
                        // 8. 如果有Component注解,我们要取出它的value值当作容器的key
                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();
                        // 9. 如果Component注解没有传入值,那么将类名小写当做key
                        if ("".equals(beanName)) {
                            beanName = StringUtils.uncapitalize(className);
                        }
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setaClass(aClass);
                        // 10. 判断bean上是否注有Scope注解
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                            String scopeValue = scopeAnnotation.value();
                            if ("".equals(scopeValue)) {
                                scopeValue = "singleton";
                            }
                            if ("singleton".equals(scopeValue) || "prototype".equals(scopeValue)) {
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                new Throwable("注解Scope属性值错误~~");
                            }
                        } else { // 如果没有标注Scope注解,默认创建单例对象
                            beanDefinition.setScope("singleton");
                        }

                        // 11. 将bean信息放入beanDefinitionMap中
                        beanDefinitionMap.put(beanName, beanDefinition);
                        // 12. 如果Scope="singleton", 那么我们实例化对象,放入单例池中
                        if (beanDefinition.getScope().equals("singleton")) {
                            singletonObjects.put(beanName, createBean(beanDefinition));
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

  • 最后我们在初始化前后遍历List集合,然后调用before/after方法

但是我们在调before方法的时候,需要传入beanName,所以我们要在createBean方法上多加一个参数。加上beanName参数后,注意修改一下其他地方用到的createBean方法。
在这里插入图片描述

所以完整的createBean方法是

public Object createBean(BeanDefinition beanDefinition,String beanName) {
        // 1. 获取class对象
        Class aClass = beanDefinition.getaClass();
        Object instance = null;
        try {
            instance = aClass.newInstance();
            // 2. 如果存在依赖注入,遍历所有字段,完成注入
            for (Field declaredField : aClass.getDeclaredFields()) {
                // 3. 判断当前字段是否标有Autowired注解
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    // 4. 获取字段名
                    String name = declaredField.getName();
                    // 5. 根据字段名获取bean对象
                    Object bean = getBean(name);
                    // 6. 完成组装
                    declaredField.setAccessible(true);
                    declaredField.set(instance, bean);
                }
            }

            System.out.println("=====创建好实例====" + instance);

            // 调用before方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }

            // 初始化方法
            if (instance instanceof InitializingBean) {
                try {
                    ((InitializingBean) instance).afterPropertiesSet();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            // 调用after方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }


        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return instance;
    }

2.8. 执行测试类AppMain

package com.zhl.spring;


import com.zhl.spring.component.MonsterDao;
import com.zhl.spring.component.MonsterService;
import com.zhl.spring.ioc.ZhlSpringApplicationContext;
import com.zhl.spring.ioc.ZhlSpringConfig;

/**
 * TODO
 *
 * @Author zhl
 * @Date 2023/4/1 16:38
 * @Desc: 测试类
 **/
    public class AppMain {
    public static void main(String[] args) {
        ZhlSpringApplicationContext ioc = new ZhlSpringApplicationContext(ZhlSpringConfig.class);
        MonsterService monsterService = (MonsterService) ioc.getBean("service01");
        monsterService.sayHello();
        System.out.println("ok~~~");
    }
}

测试结果

=====创建好实例====com.zhl.spring.component.MonsterDao@7440e464
=====创建好实例====com.zhl.spring.component.ZhlBeanPostProcessor@27c170f0
postProcessBeforeInitialization 被调用 zhlBeanPostProcessor bean= class com.zhl.spring.component.ZhlBeanPostProcessor
postProcessAfterInitialization 被调用 zhlBeanPostProcessor bean= class com.zhl.spring.component.ZhlBeanPostProcessor
=====创建好实例====com.zhl.spring.component.MonsterService@76ed5528
postProcessBeforeInitialization 被调用 service01 bean= class com.zhl.spring.component.MonsterService
MonsterService 进行初始化, 具体业务由程序员来搞定....
postProcessAfterInitialization 被调用 service01 bean= class com.zhl.spring.component.MonsterService
hello~~,sir
ok~~~

可以看到,实现了后置处理器接口的MonsterService ,确实在初始化方法前后调用了before和after方法,说明我们后置处理器的实现是OK的。

认识后置处理器代码 spring-learning-zhl
链接:https://pan.baidu.com/s/14MovOujDN-ZyaDsjz2DTIw?pwd=yf0h
提取码:yf0h

Spring核心机制底层实现代码 spring5-zhl
链接:https://pan.baidu.com/s/1I1Eno-4p4aSMppSefU0FSg?pwd=o2ur
提取码:o2ur

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值