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博客

相关推荐
喵爸的小作坊1 分钟前
StreamPanel:一个让 SSE 调试不再痛苦的 Chrome 插件
前端·后端·http
神奇小汤圆2 分钟前
字符串匹配算法
后端
无限大68 分钟前
为什么网站需要"域名"?——从 IP 地址到网址的演进
后端
树獭叔叔13 分钟前
LangGraph Memory 机制
后端·langchain·aigc
BullSmall14 分钟前
Tomcat11证书配置全指南
java·运维·tomcat
永不停歇的蜗牛16 分钟前
K8S之创建cm指令create和 apply的区别
java·容器·kubernetes
Java编程爱好者17 分钟前
OpenCVSharp:了解几种特征检测
后端
爱学习的小可爱卢21 分钟前
JavaEE进阶——SpringBoot统一功能处理全解析
java·spring boot·后端·java-ee
汤姆yu24 分钟前
基于springboot的二手物品交易系统的设计与实现
java·spring boot·后端
Java水解25 分钟前
基于Rust实现爬取 GitHub Trending 热门仓库
数据结构·后端