Spring后处理器-BeanPostProcessor

Spring后处理器-BeanPostProcessor

  • **Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过bean的初始化过程((该后处理器的执行时机)),**例如:属性的填充、初始化方法init的执行等,其中有一个对外拓展的点BeanPostProcessor,我们称之为bean后处理器。与上文bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor(即在配置文件中对其进行配置),会在流程节点上被Spring自动调用。

  • BeanPostProcessor接口代码如下
    *

    java 复制代码
    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by FernFlower decompiler)
    //
    
    package org.springframework.beans.factory.config;
    
    import org.springframework.beans.BeansException;
    import org.springframework.lang.Nullable;
    
    public interface BeanPostProcessor {
        @Nullable
        default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        @Nullable
        default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }
  • 创建实现该接口(BeanPsotProcessor)的类,要在配置文件中进行管理

    • 快捷键 ctrl + insert 重写接口方法
    java 复制代码
    package com.example.PostProcessor;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println(beanName + ":postProcessBeforeInitialization");
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println(beanName + ":postProcessAfterInitialization");
            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
  • 测试类代码

    java 复制代码
    package com.example.Test;
    
    
    import com.example.Service.Impl.UserServiceImpl;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    public class TestApplicationContext {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            System.out.println(context.getBean(UserServiceImpl.class));
        }
    }
  • 运行结果如下

    • 展示了该后处理器的执行时机


Before和After执行时机

  • 在Bean实例化过程可以配置相关对于bean对象的操作方法,具体间往期文章:Bean的配置- CSDN搜索

  • 注册为bean的类
    *

    java 复制代码
    package com.example.Service.Impl;
    
    import com.example.DAO.UserDAO;
    import com.example.Service.UserService;
    import org.springframework.beans.factory.InitializingBean;
    
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    
    public class UserServiceImpl implements UserService, InitializingBean {
        // todo 无参构造方法
        public UserServiceImpl() {
            System.out.println("UserServiceImpl实例化");
        }
    
        // todo 自定义初始化方法
        public void init() {
            System.out.println("自定义初始化方法init()");
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("属性设置之后执行afterPropertiesSet()");
        }
    
    }
  • 实现bean后处理器的类
    *

    java 复制代码
    package com.example.PostProcessor;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    public class MyBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println(beanName + ":postProcessBeforeInitialization");
            return bean;
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println(beanName + ":postProcessAfterInitialization");
            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
  • 配置文件

    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:util="http://www.springframework.org/schema/util"
           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/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <bean class="com.example.PostProcessor.MyBeanPostProcessor"></bean>
        <bean id="userService" class="com.example.Service.Impl.UserServiceImpl" init-method="init">
    </beans>
  • 测试类

    java 复制代码
    package com.example.Test;
    
    
    import com.example.Service.Impl.UserServiceImpl;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    public class TestApplicationContext {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            System.out.println(context.getBean(UserServiceImpl.class));
        }
    }
  • 运行结果

小结

  • 从上述运行结果来看,首先完成bean对象的创建,然后执行后处理器中的before方法,然后执行属性设置之后的方法,然后执行自定义的初始化方法,最后执行后处理器的after方法

案例

  • 对Bean方法进行执行时间日志增强

  • 要求

    • Bean的方法执行之前控制台打印当前时间
    • Bean的方法执行之后控制台打印当前时间
  • 分析

    • 对方法进行增强主要就是代理设计模式和包装设计模式
    • 由于Bean方法不确定,所以使用动态代理在运行期间执行增强操作
    • 在Bean实例创建完毕之后,进入到单例之前,使用Proxy真实的目标bean
  • 具体代码如下

    • 代理类(实现了bean后处理接口)
    java 复制代码
    package com.example.PostProcessor;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    import java.util.Date;
    
    public class TimeLogBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            // todo 使用动态代理对目标bean进行增强,返回proxy对象,进而存储到单例池singletonObjects中
            Object beanProxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), (InvocationHandler) (proxy, method, args) -> {
                // 输出开始时间
                System.out.println("方法" + method.getName() + "开始执行时间" + new Date());
                // 执行目标方法
                Object rs = method.invoke(bean, args);
                // 输出结束时间
                System.out.println("方法" + method.getName() + "结束执行时间" + new Date());
                return rs;
            });
            return beanProxy;// 将增强的bean存入单例池中
        }
    }
    • bean对象对应的类

    java 复制代码
    package com.example.Service.Impl;
    
    import com.example.Service.UserService;
    
    
    public class UserServiceImpl implements UserService {
        public void show() {
            System.out.println("show......");
        }
    
    }
    • 接口类

    java 复制代码
    package com.example.Service;
    
    public interface UserService {
        public void show();
    }
    • 配置文件

    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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean class="com.example.PostProcessor.TimeLogBeanPostProcessor"></bean>
        <bean id="userService" class="com.example.Service.Impl.UserServiceImpl">
        </bean>
    
    
    </beans>
    • 测试类

    java 复制代码
    package com.example.Test;
    
    
    import com.example.Service.UserService;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestApplicationContext {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
            UserService UserServiceBean = (UserService) context.getBean(UserService.class);
            UserServiceBean.show();
        }
    }
    • 运行结果

Bean实例化基本流程图



  • 首先通过Reader读取配置文件,解析bean标签,然后将每个bean标签变成beanDefinition对象存储到beanDefinitionMap中,然后经过所有的BeanFactoryPostProcessor(bean工厂后处理器),再从Map中取出每个beanDefiniton对象,通过反射变成Object对象,创建完对象后,经过beanPoatProcessor中的before方法和after方法(之间还存在bean中的init方法,后面会讲述),最终存入单例池中。

在学习本文章内容的时候,我也去补充了一下,基础知识,便于更加程序运行原理,具体文章如下

Java高级-代理(proxy)_熵240的博客-CSDN博客
Java高级-注解_熵240的博客-CSDN博客
反射的作用、应用场景_熵240的博客-CSDN博客
Java高级-反射_熵240的博客-CSDN博客
Lambda表达式_熵240的博客-CSDN博客

相关推荐
郑祎亦11 分钟前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
不是二师兄的八戒11 分钟前
本地 PHP 和 Java 开发环境 Docker 化与配置开机自启
java·docker·php
爱编程的小生23 分钟前
Easyexcel(2-文件读取)
java·excel
本当迷ya24 分钟前
💖2025年不会Stream流被同事排挤了┭┮﹏┭┮(强烈建议实操)
后端·程序员
带多刺的玫瑰40 分钟前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
计算机毕设指导61 小时前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study1 小时前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言
Chris _data1 小时前
二叉树oj题解析
java·数据结构
牙牙7051 小时前
Centos7安装Jenkins脚本一键部署
java·servlet·jenkins
paopaokaka_luck2 小时前
[371]基于springboot的高校实习管理系统
java·spring boot·后端