通过idea debugger分析mybatis的Bean创建过程

本文深入探讨SpringBoot环境下MyBatis-Plus的@Mapper注解处理流程,包括如何通过动态代理创建代理对象,以及如何在SpringBoot中集成并扫描带有@Mapper的接口。同时,解析了在Bean初始化过程中,SpringBoot如何应用BeanPostProcessor来处理Mapper相关的Bean,实现如缓存、异常处理等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下面的代码为例

@Mapper  //使用了mybatis
@Repository  //当出现异常时, 统一使用spring封装的异常
@CacheConfig(cacheNames = "city")  //使用了缓存
public interface CityRepository extends BaseMapper<City> {  //使用了mybatis-plush

    @Cacheable(key = "#id",unless = "#result == null ")
    default City findById(String id){
        return selectById(id);
    }

	default City myFind(String id){
		//嵌套调用, 此时findById上的注解不会生效.
		return findById(id);
	}
}
@Configuration
public class TestApplication{
    @Autowired
    CityRepository cityRepository ;
}

上例中, 给接口CityRepository添加了一堆的注解, 从而实现了一堆的功能, 我好奇spring boot是怎么实现的.

配置spring-boot为debug模式

配置pom.xml

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.version}</version>
 
                <configuration>
                    <mainClass>com.xx.Application</mainClass>
                    <jvmArguments>
                         -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
                    </jvmArguments>
                </configuration>
            </plugin>

然后在idea中配置一个remote

JDK的动态代理

JDK动态代理示例

public class Main {
	private final static Logger log = LogManager.getLogger(Main.class);
	
	public static interface MyInterface{
		void run();
	}
	
	public static class MyInvoker<T> implements InvocationHandler, Serializable{
		@Override
		public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
			log.info("invoke");
			return null;
		}
	}

	public static void main(String[] args) {
		log.info("main");
		MyInvoker<MyInterface> invoker = new MyInvoker<MyInterface>();
		MyInterface iface = (MyInterface) Proxy.newProxyInstance(Main.class.getClassLoader(), 
							new Class[]{MyInterface.class}, invoker );
		iface.run();
	}
}

上面的代码中, 声明了一个接口MyInterface, 但是并没有定义一个继承它的class, 而是通过java8中的Proxy动态的创建了一个对象,这个对象可以直接强转为MyInterface.
spring boot也是采用类似的方式

mybatis-plus怎样处理@Mapper?

将mybatis-plus加到依赖中, spring boot配置会执行MybatisPlusAutoConfiguration, 通过ClassPathMapperScanner来扫描包含相关注解的接口

MapperScan.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)  //通过它来扫描带有@Mapper的注解
@Repeatable(MapperScans.class)
public @interface MapperScan {
...
}

MapperScannerRegistrar.java

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    /** 扫描到的带有@Mapper的interface的集合 */
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
      LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      //配置sql相关的参数, 读者自己去看代码
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }

Mapper相关的Bean工厂MapperFactoryBean

MapperFactoryBean.java

@Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
      	//处理interface上定义的各种SQL
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

spring-boot对mapper bean的处理

当构造出bean之后, spring boot会bean再做一次处理.

AbstractAutowireCapableBeanFactory.java

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

本文中CityRepository例子中, 使用了Cacheable, Repository, 这两个注解分别对应着两个Processor, 应用到代理中

AbstractAdvisingBeanPostProcessor.java

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		if (this.advisor == null || bean instanceof AopInfrastructureBean) {
			// Ignore AOP infrastructure such as scoped proxies.
			return bean;
		}

		if (bean instanceof Advised) {
			Advised advised = (Advised) bean;
			if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
				// Add our local Advisor to the existing proxy's Advisor chain...
				if (this.beforeExistingAdvisors) {
					advised.addAdvisor(0, this.advisor);
				}
				else {
					advised.addAdvisor(this.advisor);
				}
				return bean;
			}
		}

		if (isEligible(bean, beanName)) {
			ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
			if (!proxyFactory.isProxyTargetClass()) {
				evaluateProxyInterfaces(bean.getClass(), proxyFactory);
			}
			proxyFactory.addAdvisor(this.advisor);
			customizeProxyFactory(proxyFactory);
			return proxyFactory.getProxy(getProxyClassLoader());
		}

		// No proxy needed.
		return bean;
	}

AbstractAutowireCapableBeanFactory.java

	@Override
	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

最终生成的bean, 是一条很复杂的代理JdkDynamicAopProxy, 需要进行process的方法存放在它的AdvisedSupport advised成员中.
比如查找数据后, 需要将结果缓存到cache中, 会调用CacheInterceptor

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值