spring源码整合Mybatis

                                                                                                       ## 改造一

启动类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		context.register(AppConfig.class);
		context.refresh();
		UserService userService = (UserService) context.getBean("userService");
		userService.test();

Mapper层

public interface OrderMapper {
	@Select("select 'user'")
	String SelectById();
}

Service层

@Component
public class UserService {

	@Autowired
	private OrderMapper orderMapper;

	public void test(){
		System.out.println(orderMapper.SelectById());
	}

}
@Component
public class ZhqFactoryBean implements FactoryBean {
	@Override
	public Object getObject() throws Exception {
		//创建一个代理对象
		Object proxyInstance = Proxy.newProxyInstance(ZhqFactoryBean.class.getClassLoader(), new Class[]{OrderMapper.class}, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println(method);
				return null;
			}
		});
		return proxyInstance;
	}

	@Override
	public Class<?> getObjectType() {
		return OrderMapper.class;
	}
}

配置类

@ComponentScan("com.zhouyu")
public class AppConfig {}
  • 以上是简单mybatis整合spring的例子,主要就是想要让orderMapper成功注入到UserService 中,成功执行sql的查询嘛。
  • 由于orderMapper是一个接口,没法直接注入,只能功能动态代理的方式进行注入,== 后续可以cjlib动态代理==

问题

  1. 同样是单例Bean,ZhqFactoryBean和UserService的执行顺序?如果先执行UserService ,是不是就不能进行注入了,因为此时这个ZhqFactoryBean没有对OrderMapper执行代理逻辑?
  2. 现在只是注入一个Mapper对象,如果注入多个,怎么可以更好的设计?

改造二

应对第二个问题可以使用改造ZhqFactoryBean的构造方法,然后再注册BeanDefinition的时候进行传入当前类,就会把所需要的mapper即变成Beandefinition又会创建他的代理对象。

改造ZhqFactoryBean构造方法,使用他变成通用类。

public class ZhqFactoryBean implements FactoryBean {

	private Class aClass;

	public ZhqFactoryBean(Class aClass) {
		this.aClass = aClass;
	}

	@Override
	public Object getObject() throws Exception {
		//创建一个代理对象
		Object proxyInstance = Proxy.newProxyInstance(ZhqFactoryBean.class.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println(method);
				return null;
			}
		});
		return proxyInstance;
	}

	@Override
	public Class<?> getObjectType() {
		return aClass;
	}
}

1)启动类改造

AnnotationConfigApplicationContext registry = new AnnotationConfigApplicationContext();
		registry.register(AppConfig.class);
		AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
		beanDefinition1.setBeanClass(ZhqFactoryBean.class);

		AbstractBeanDefinition beanDefinition2 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		beanDefinition2.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
		beanDefinition2.setBeanClass(ZhqFactoryBean.class);

		registry.registerBeanDefinition("userMapper",beanDefinition1);
		registry.registerBeanDefinition("orderMapper",beanDefinition2);


		registry.refresh();

		UserService userService = (UserService) registry.getBean("userService");
		userService.test();

2)也可以使用BeanDefinitionRegistryPostProcessor

@Component
public class ZhqBeanPostProcessor  implements BeanDefinitionRegistryPostProcessor {
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(UserMapper.class);
		beanDefinition1.setBeanClass(ZhqFactoryBean.class);

		AbstractBeanDefinition beanDefinition2 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
		beanDefinition2.getConstructorArgumentValues().addGenericArgumentValue(OrderMapper.class);
		beanDefinition2.setBeanClass(ZhqFactoryBean.class);

		registry.registerBeanDefinition("userMapper",beanDefinition1);
		registry.registerBeanDefinition("orderMapper",beanDefinition2);
	}
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

	}
}

问题

  1. BeanDefinitionRegistryPostProcessor 是在Bean生命周期的什么时候执行的?
  2. 这样每次都需要定义一个BeanDefinition才能让mapper产生代理,怎么使用注解方法,一次性扫描包路径的所有mapper。

改造三

添加注解类用于扫描mapper包

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ZhqScan {

	String value();
}
//启动类添加
@ZhqScan("com.zhouyu.mapper")

注解中获取包路径,改造spring扫描

@Component
public class ZhqBeanPostProcessor  implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//扫描路径
		Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ZhqScan.class.getName());
		String path = (String) annotationAttributes.get("value");
		//如果这样还是使用的spring的扫描逻辑
		ZhqClassPathDefinitionScanner scanner = new ZhqClassPathDefinitionScanner(registry);

		scanner.addIncludeFilter(new TypeFilter() {
			@Override
			public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
				return true;
			}
		});
		scanner.scan(path);
	}
}

ZhqClassPathDefinitionScanner重载doScan,改造Beandefiniton使他通过代理

public class ZhqClassPathDefinitionScanner extends ClassPathBeanDefinitionScanner {

	public ZhqClassPathDefinitionScanner(BeanDefinitionRegistry registry) {
		super(registry);
	}

	@Override
	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);
		for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
			BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
			beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
			beanDefinition.setBeanClassName(ZhqFactoryBean.class.getName());
		}
		System.out.println(beanDefinitionHolders.size());
		return beanDefinitionHolders;
	}

	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return beanDefinition.getMetadata().isInterface();
	}
}

思路:

  • mybatis使用mapper接口进行数据库查询,首先得注册成为beandefinition中,但是mapper是一个接口,不能直接注册成beandefinition,那怎么办?需要用一个代理对象进行代理,改造一用到FactoryBean,生成BeanDefintion的同时,产生对mapper进行代理。
  • 对于怎么批量对mapper进行代理,改造二考虑到使用构造方法,通过创建BeanDefinition的同时调用构造方法直接产生了代理对象。但是对于怎么批量产生mapper就要看改造三了。
  • 改造三,使用注解将指定路径下的所有mapper都扫描到。但是手动写扫描太过负责,所以参考spring的扫描逻辑,他使用到的就是ClassPathBeanDefinitionScanner这个类实现的扫描功能,但是他又有只扫描接口,且带@Commopent注解的,所以创建一个新类继承他,并重写响应的方法,使他即使是接口的类也能扫描成为beanDefinition.
  • 但是光扫描出来成为deandefinition不太行啊,需要他对接成为代理对象,所以重写doscan方法,对扫描后的Beandefinition进行处理

问题:

  • 现在只是仿照mybatis进行操作,而且代理对象是咱们自己写的?

改造四

让mybatis进行代理

public class ZhqFactoryBean implements FactoryBean {
	private Class aClass;
	private SqlSession sqlSession;
	
	public ZhqFactoryBean(Class aClass) {
		this.aClass = aClass;
	}
	
    @Autowired
	public void setSession(SqlSessionFactory sqlSessionFactory) {
	    //使用sqlSessionFactory对sqlSession进行赋值
		sqlSessionFactory.getConfiguration().addMapper(aClass);
		this.sqlSession = sqlSessionFactory.openSession();
	}
	
	@Override
	public Object getObject() throws Exception {
	    //将接口让mybaits进行代理,但是sqlSession需要引入
		return sqlSession.getMapper(aClass);
	}
	@Override
	public Class<?> getObjectType() {
		return aClass;
	}
}

注入SqlSessionFactory,自定义注入

    @Bean
	public SqlSessionFactory sqlSessionFactory() throws IOException {
		InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
		return sqlSessionFactory;
	}

mybatis配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
  <environments default="development">
    <environment id="development">
      <!-- 使用jdbc事务管理 -->
      <transactionManager type="JDBC"/>
      <!-- 数据库连接池 -->
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url"
                  value="jdbc:mysql://***:3307/zhq?characterEncoding=utf-8&amp;useSSL=false"/>
        <property name="username" value="***"/>
        <property name="password" value="***"/>
      </dataSource>
    </environment>
  </environments>
</configuration>

参考图片
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值