Spring 框架之所以在众多Java开发框架中脱颖而出,其强大的扩展能力是关键因素之一。它提供了丰富的扩展点,使得第三方组件能够无缝集成,也催生了大量基于Spring的二次开发项目。本文旨在梳理Spring的核心扩展点,并通过一个手写Mybatis与Spring整合的简易示例,带你窥探其扩展机制的精髓。
Spring 加载容器上下文的三种方式
Spring 加载容器上下文主要有以下三种方式,其中最常用的是基于配置类的方式。
public class ApplicationDemo {
public static void main(String[] args) {
// 1. 基于配置类加载Spring上下文
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Student student = applicationContext.getBean(Student.class);
System.out.println(student);
// 2. 基于项目路径xml加载Spring上下文
ClassPathXmlApplicationContext applicationContextXml = new ClassPathXmlApplicationContext("spring.xml");
student = applicationContextXml.getBean(Student.class);
System.out.println(student);
// 3. 基于文件系统绝对路径xml加载Spring上下文
FileSystemXmlApplicationContext applicationContextFileXml = new FileSystemXmlApplicationContext("E://spring.xml");
student = applicationContextXml.getBean(Student.class);
System.out.println(student);
}
}
配置类 MyConfig 如下:
@Configuration
@ComponentScan({"com.itxs.pojo","com.itxs.extend"})
public class MyConfig {
}

其底层构造方法会依次执行注册和刷新:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
核心扩展点详解
掌握 Spring 的扩展点,是深入理解其框架设计和进行高阶 Java 开发的基础。下面我们逐一分析。
ApplicationContextInitializer
这是 Spring Boot 提供的扩展点,在 Spring 容器刷新之前初始化 ConfigurableApplicationContext 的回调接口。允许我们在容器完全初始化前执行自定义逻辑,例如激活特定配置或进行动态字节码注入。
实现类示例:
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("-----------------------MyApplicationContextInitializer initialize");
}
}
使用方式有三种:
-
Main函数添加:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(MyApplication.class);
springApplication.addInitializers(new MyApplicationContextInitializer());
springApplication.run(args);
}
}

-
配置文件配置 (application.yml):
context:
initializer:
classes: com.itxs.extend.MyApplicationContextInitializer
-
Spring Boot SPI扩展 (META-INF/spring.factories):
org.springframework.context.ApplicationContextInitializer=com.itxs.extend.MyApplicationContextInitializer

BeanDefinitionRegistryPostProcessor
它是 BeanFactoryPostProcessor 的子接口,作用时机更早。在所有的 Bean 定义信息即将被加载但未实例化时,postProcessBeanDefinitionRegistry 方法被执行。我们可以在此动态注册自己的 Bean 定义。

@Component
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
System.out.println("BeanDefinitionRegistryPostProcessor-postProcessBeanDefinitionRegistry");
System.out.println("BeanDefinitionCount:"+beanDefinitionRegistry.getBeanDefinitionCount());
String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println("beanDefinitionName:"+beanDefinitionName);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("BeanDefinitionRegistryPostProcessor-postProcessBeanFactory");
}
}

BeanFactoryPostProcessor
在 Spring 读取 Bean 定义信息之后,实例化 Bean 之前执行。用户可以修改已注册的 BeanDefinition 的元信息。
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor-postProcessBeanFactory");
// 将名为 “student” 的 Bean 的类型修改为 Teacher
BeanDefinition student = configurableListableBeanFactory.getBeanDefinition("student");
student.setBeanClassName(Teacher.class.getName());
}
}

BeanPostProcessor
该接口在 Bean 对象实例化、依赖注入完毕后,在初始化方法调用前后进行拦截。我们可以在此修改 Bean 属性或生成动态代理,Spring AOP 正是基于此实现。
postProcessBeforeInitialization: 初始化之前。
postProcessAfterInitialization: 初始化之后。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization beanName:"+beanName);
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization beanName:"+beanName);
// 如果 Bean 是 Student 类型,则返回 Teacher 类型(示例逻辑)
if (bean.getClass().isAssignableFrom(Student.class)){
return Teacher.class;
}
return null;
}
}


InstantiationAwareBeanPostProcessor
BeanPostProcessor 的子接口,扩展了实例化阶段和属性注入阶段的干预能力。
postProcessBeforeInstantiation: 实例化(new)之前。
postProcessAfterInstantiation: 实例化之后。
postProcessProperties: 属性注入时触发,@Autowired 等注解基于此实现。
@Component
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("postProcessBeforeInstantiation beanName" + beanName);
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInstantiation beanName" + beanName);
return false;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println("postProcessProperties beanName" + beanName);
return null;
}
}


SmartInstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor 的子接口,提供了更精细的控制。
predictBeanType: 预测 Bean 类型。
determineCandidateConstructors: 决定使用哪个构造函数。
getEarlyBeanReference: 用于解决循环依赖,提前暴露 Bean 引用。
@Component
public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor{
@Override
public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("predictBeanType beanName:"+beanName);
return null;
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
if (!beanClass.isAssignableFrom(Student.class)){
System.out.println("determineCandidateConstructors beanName:"+beanName);
}
return null;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
System.out.println("getEarlyBeanReference beanName:"+beanName);
return null;
}
}


通过 ApplicationContextAwareProcessor 触发的扩展点
ApplicationContextAwareProcessor 是一个 BeanPostProcessor,它在初始化前触发一系列 Aware 接口的回调,让 Bean 能感知到 Spring 容器的特定对象。
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationStartupAware) {
((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}

SmartInitializingSingleton
当 Spring 容器管理的所有非懒加载单例 Bean 都初始化完成后,会触发此接口的 afterSingletonsInstantiated 方法。
@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
@Override
public void afterSingletonsInstantiated() {
System.out.println("afterSingletonsInstantiated");
}
}
FactoryBean
用于定制复杂对象的创建逻辑。通过 getBean(“beanName”) 获取到的是 getObject() 方法返回的对象;要获取 FactoryBean 本身,需使用 getBean(“&beanName”)。
@Component
public class MyFactoryBean implements FactoryBean<Teacher> {
@Override
public Teacher getObject() throws Exception {
return new Teacher();
}
@Override
public Class<?> getObjectType() {
return Teacher.class;
}
}
public class ApplicationExtend {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Object student = applicationContext.getBean("student");
System.out.println("object:"+student);
System.out.println("factoryBeanReturn:"+applicationContext.getBean("myFactoryBean")); // 获取Teacher对象
System.out.println("factoryBeanSelf:"+applicationContext.getBean("&myFactoryBean")); // 获取FactoryBean本身
}
}


CommandLineRunner (Spring Boot)
Spring Boot 提供的扩展接口,项目启动完毕后自动执行 run 方法。多个实现可用 @Order 注解排序。
@Component
@Order(1)
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner run args:" +args);
}
}
@Component
@Order(0)
public class MyTwoCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyTwoCommandLineRunner run args:" +args);
}
}

ApplicationListener
用于监听 Spring 的内置或自定义事件。常见内置事件如:
ContextRefreshedEvent: 容器刷新完成。
ContextClosedEvent: 容器关闭。
@Configuration
public class MyApplicationListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println("MyApplicationListener onApplicationEvent");
}
}
BeanNameAware & BeanFactoryAware
BeanNameAware: 在初始化前,让 Bean 获知自己在容器中的名字。
BeanFactoryAware: 在实例化后、属性注入前,让 Bean 获知 BeanFactory 实例。
@Component
public class MyBeanNameAware implements BeanNameAware {
@Override
public void setBeanName(String s) {
System.out.println("setBeanName:"+s);
}
}
@Component
public class MyBeanFactoryAware implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryAware setBeanFactory:"+beanFactory.getBean("myBeanFactoryAware").getClass().getSimpleName());
}
}
初始化及销毁回调方法
InitializingBean#afterPropertiesSet: 属性设置完成后调用,在 postProcessAfterInitialization 之前。
DisposableBean#destroy: Bean 销毁时调用。
@PostConstruct: 标注的方法在 postProcessBeforeInitialization 之后,afterPropertiesSet 之前执行。
@PreDestroy: 标注的方法在容器关闭时执行。
@Repository
public class Student implements InitializingBean, DisposableBean {
@PostConstruct
public void init() throws Exception {
System.out.println("Student init");
}
@PreDestroy
public void close() throws Exception {
System.out.println("Student close");
}
@Override
public void destroy() throws Exception {
System.out.println("Student destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Student afterPropertiesSet");
}
}


手写 Mybatis-Spring 整合示例
理解了 Spring 的扩展点后,我们来看一个实际应用:模拟 Mybatis 如何与 Spring 整合。其源码思路主要利用了 FactoryBean 和 ImportBeanDefinitionRegistrar。
本示例仅为演示整合原理,不引入真实 Mybatis 依赖。
核心思路
- 利用 JDK 动态代理为 Mapper 接口生成代理对象。
- 借助
FactoryBean,使其 getObject() 方法返回代理对象,getObjectType() 方法返回 Mapper 接口类型。
- 通过
ImportBeanDefinitionRegistrar 配合自定义注解,扫描指定包下的 Mapper 接口,并将对应的 FactoryBean 定义动态注册到 Spring 容器中。
项目结构与实现
1. 项目依赖 (pom.xml)
仅需 Spring 上下文依赖。
<?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>com.itxs</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.9</version>
</dependency>
</dependencies>
</project>


2. 自定义扫描注解 (ItxsScan)
通过 @Import 导入注册器。
@Retention(RetentionPolicy.RUNTIME)
@Import({ItxsImportBeanDefinitionRegistrar.class})
public @interface ItxsScan {
String value() default "";
}
3. Spring 配置类 (AppConfig)
使用自定义的 @ItxsScan 注解指定 Mapper 接口的扫描路径。
@Configuration
@ComponentScan({"com.itxs"})
@ItxsScan("com.itxs.dao")
public class AppConfig {
}
4. Mapper 接口与自定义SQL注解
模拟 Mybatis 的 @Select 注解。
package com.itxs.dao;
import com.itxs.annotation.ItxsSelect;
import com.itxs.pojo.User;
public interface UserMapper {
@ItxsSelect("select * from user where user_id = #{userId}")
User selectByUserId(Integer userId);
}

5. Service 层
直接通过 @Autowired 注入 Mapper 接口,这也是整合后我们希望达到的效果。
@Component
public class UserService {
@Autowired
UserMapper userMapper;
@Autowired
OrderMapper orderMapper;
public void getUser(){
userMapper.selectByUserId(1);
orderMapper.selectByOrderId(1);
}
}

6. 核心:FactoryBean 实现 (ItxsFactoryBean)
负责创建 Mapper 接口的代理对象。当调用代理方法时,会打印出 SQL 信息(此处简化了真实的 数据库 操作)。
package com.itxs.utils;
import com.itxs.annotation.ItxsSelect;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ItxsFactoryBean implements FactoryBean {
private Class mapper;
public ItxsFactoryBean(Class mapper) {
this.mapper = mapper;
}
@Override
public Object getObject() throws Exception {
// 使用 JDK 动态代理
Object o = Proxy.newProxyInstance(ItxsFactoryBean.class.getClassLoader(), new Class[]{mapper}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())){
return method.invoke(this,args);
}
// 获取方法上的SQL注解
ItxsSelect annotation = method.getAnnotation(ItxsSelect.class);
System.out.println("调用方法名称是"+method.getName()+",sql语句为"+annotation.value());
// 此处可衔接真实的 Mybatis 执行流程
return null;
}
});
return o;
}
@Override
public Class<?> getObjectType() {
return mapper;
}
}

7. 核心:ImportBeanDefinitionRegistrar 实现 (ItxsImportBeanDefinitionRegistrar)
在 Spring 容器启动时被调用,扫描指定包,为每个 Mapper 接口创建 ItxsFactoryBean 的 BeanDefinition 并注册。
package com.itxs.utils;
import com.itxs.annotation.ItxsScan;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import java.io.File;
import java.net.URL;
import java.util.*;
public class ItxsImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, org.springframework.beans.factory.support.BeanNameGenerator importBeanNameGenerator) {
// 获取 @ItxsScan 注解的扫描路径
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ItxsScan.class.getName());
String mapperPath = annotationAttributes.get("value").toString();
List<Class> mappers = scan(mapperPath);
for (Class mapper : mappers) {
// 构建 ItxsFactoryBean 的 BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
beanDefinition.setBeanClass(ItxsFactoryBean.class);
// 设置构造参数:Mapper接口的Class对象
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(mapper);
// 注册BeanDefinition,Bean名称为类名首字母小写
registry.registerBeanDefinition(StringUtils.toLowerCaseFirstOne(mapper.getSimpleName()),beanDefinition);
}
}
// 扫描指定包路径下所有 .class 文件的工具方法(略)
private List<Class> scan(String path) { ... }
}

8. 主程序
启动 Spring 容器,并调用 Service 方法验证整合是否成功。
package com.itxs;
import com.itxs.config.AppConfig;
import com.itxs.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MybatisApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.getUser();
}
}

运行结果
执行主程序,成功通过 Service 调用了 Mapper 代理对象的方法,并打印出了对应的 SQL 语句,证明我们的简易整合框架工作正常。这个示例清晰地展示了如何利用 Spring 的扩展点(FactoryBean, ImportBeanDefinitionRegistrar)将第三方框架(如 Mybatis)的组件(Mapper 接口)纳入 Spring 容器的管理之中。

通过以上对 Spring 扩展点的系统梳理和一个完整的整合案例实践,我们可以深刻体会到 Spring 框架在设计上的高度可扩展性。这种设计哲学使得开发者能够灵活地介入 Bean 的生命周期,为构建复杂的企业级 应用架构 提供了坚实的基础。