spring[7]-使用ImportBeanDefinitionRegistrar、JDK代理、FactoryBean模拟mybatis原理

简介

  • 使用ImportBeanDefinitionRegistrar、JDK代理、FactoryBean模拟mybatis原理

知识点

  • ImportBeanDefinitionRegistrar
  • JDK代理
  • FactoryBean
  • @interface
  • mybatis的极简单的知识

在这里插入图片描述

代码如下 :

  • UserDao.java : 使用mybatis的mapper
  • ProxyTest.java : 测试类
  • MyImportBeanDefinition.java : 使用 ImportBeanDefinitionRegistrar 注册bean
  • MyFactoryBean.java : 实现 FactoryBean ,自定义对象的创建并交由spring管理
  • MySession.java : 代理类,模拟mybatis的sqlSession
  • MyMapperInvocationHandler.java : 代理实现类,使用JDK代理,实现接口的代理操作
  • MyMapperScan.java : 自定义配置类,启动类增加注解,模拟mybatis的@MapperScan()
  • AppConfig.java : 项目配置类

调用链

  • ProxyTest(启动引用配置类)
  • AppConfig(增加注解)
  • MyMapperScan(注解方式被调用)
  • MyImportBeanDefinition(注册beandefinition)
  • MyFactoryBean(创建对象并交给spring管理)
  • MySession(创建对象)
  • MyMapperInvocationHandler(通过JDK代理创建代理类)
  • UserDao(对此接口进行JDK代理)

spring源码环境搭建

源码下载,代码很少,浪费积分,建议跟着写一下。

UserDao

package com.proxy;

import org.apache.ibatis.annotations.Select;

import java.util.List;
import java.util.Map;

/**
 * 模拟Mybatis的接口
 */
public interface UserDao {

	/**
	 * 仅有接口,无实现类
	 * 此处仅以@Select做为案例
	 */
	@Select("select * from user")
	List<Map<Object, Object>> query();

}

MySession

package com.proxy;

import java.lang.reflect.Proxy;

/**
 * 代理类,模拟mybatis的sqlSession
 * sqlSession.getMapper(),其实是返回了代理类
 */
public class MySession {

	/**
	 * 调用此方法,可以实现代理类,使用JDK代理
	 */
	public static Object queryMapper(Class clazz) {
		return Proxy.newProxyInstance(MySession.class.getClassLoader(), new Class[]{clazz}, new MyMapperInvocationHandler());
	}

}

MyMapperInvocationHandler

package com.proxy;

import org.apache.ibatis.annotations.Select;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 代理实现类,使用JDK代理,实现接口的代理操作
 */
public class MyMapperInvocationHandler implements InvocationHandler {

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		/**
		 * 获取注解,此处仅以select做案例
		 */
		Select selectAnnotation = method.getAnnotation(Select.class);
		if (selectAnnotation != null) {
			/**
			 * 获取注解里面的值,此处不做实际连库操作
			 */
			String sql = selectAnnotation.value()[0];
			System.err.println(" exec sql : " + sql);
		}
		if ("toString".equals(method.getName())) {
			return proxy;
		}
		/**
		 * 此处直接返回null,实际应返回具体值
		 */
		return null;
	}

}

MyImportBeanDefinition

package com.proxy;

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;

/**
 * 使用 ImportBeanDefinitionRegistrar 注册bean
 */
public class MyImportBeanDefinition implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		// 生成 BeanDefinitionBuilder
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyFactoryBean.class);
		// 获取 AbstractBeanDefinition
		AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
		// 给 MyFactoryBean 设置类,这里可以做成扫描包,方便起见,这里暂将类写死
		// 		可以参考 我另一篇博客 https://blog.csdn.net/qq_17522211/article/details/116748646
		// 		spring[1]-使用ImportBeanDefinitionRegistrar自定义注册bean(基于源码)
		beanDefinition.getPropertyValues().addPropertyValue("mapperInterface", "com.proxy.UserDao");
		// 获取 beanClassName
		String beanClassName = beanDefinition.getBeanClassName();
		// 注册 BeanDefinition,让spring来管理
		registry.registerBeanDefinition(beanClassName, beanDefinition);
	}

}

MyFactoryBean

package com.proxy;

import org.springframework.beans.factory.FactoryBean;

/**
 * 实现 FactoryBean ,自定义对象的创建,并交由spring管理
 */
public class MyFactoryBean implements FactoryBean {
	Class mapperInterface;

	/**
	 * 返回类的对象 : 对接口做代理后的对象
	 */
	@Override
	public Object getObject() throws Exception {
		return MySession.queryMapper(mapperInterface);
	}

	/**
	 * 返回类型
	 */
	@Override
	public Class<?> getObjectType() {
		return mapperInterface;
	}

	/**
	 * 设置类,ImportBeanDefinitionRegistrar 注册时
	 * 通过FactoryBean生成代理,需要将代理的接口类传过来
	 */
	public void setMapperInterface(Class mapperInterface) {
		this.mapperInterface = mapperInterface;
	}

}

MyMapperScan

package com.proxy;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

/**
 * 自定义配置类,启动类增加注解,模拟mybatis的@MapperScan()
 * MyMapperScan(basePackages = "com.proxy") 即可扫描包下的类
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Import(MyImportBeanDefinition.class)
public @interface MyMapperScan {

	/**
	 * 需要扫描的包
	 */
	String[] basePackages() default {};

}

AppConfig

package com;

import com.beandefinition.MyMapperAutoConfig;
import com.proxy.MyMapperScan;
import org.springframework.context.annotation.ComponentScan;

/**
 * 模拟mybatis的@MapperScan()
 */
@MyMapperScan(basePackages = "com.proxy")
public class AppConfig {

}

ProxyTest

package com.proxy;

import com.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ProxyTest {

	public static void main(String[] args) {
		/**
		 * JDK动态代理测试
		 */
		UserDao userDao = (UserDao) MySession.queryMapper(UserDao.class);
		userDao.query();

		/**
		 * spring测试,可以将类通过spring来管理
		 * AppConfig 添加注解 @Import(MyImportBeanDefinition.class)
		 */
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
		ac.getBean(UserDao.class).query();
	}

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小安灬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值