spring整合mybatis---Mapperscan

Spring+mybatis框架说明

众所周知:mybatis整合spring,大家使用这个框架的时候都知道mapper层都是接口类型的,而接口类型是不会实现功能的,但是mybatis整合spring之后却能实现,证明确实存在一个实现方法,但我们看不见。实际上在整合的时候mybatis使用了代理把接口转换成了一个类并注入到了容器当中。
首先考虑一下有哪些方式:
1、我们要把一个接口使用代理成为一个类。那么就需要使用动态代理。
2、将类注册到容器当中,而且是手动注入进去的。那么我们需要获取到bd的注册器(BeanDefinitionRegistry)才能够去注册。
那么需要哪些扩展来获取到呢?
1、beanPostProsessor :这是bean的实例化过程用到的,此时的bd已经固定好了,没有办法去改变。
2、BeanFactoryPostProcessor:里面提供了一个beanFactory,由于没有注册器,不能添加bd。
3、BeanDefinitionRegistryPostProcessor:增加了一个接口。接口提供了bd注册器,可以动态的向bd中添加bean。
4、ImportSelector:这个是根据提供的字符来构建bd,如果使用这个接口,字符串到bd的过程无法参与,那么我们动态代理的类必须在项目当中,不然我们提供的字符就不能构建出bd。
5、ImportBeanDefinitionRegistrar:里面提供了注册器,可以动态的向bd中添加bean。
仔细思考就第三和第五种符合要求。我们以第5种为例(因为MapperScan用的这种)。
先建立一个mapper:

package test.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Select;

import test.dao.User;

public interface UserMapper {

	@Select("select * from user")
	List<User> getAll();
}

首先按照一贯的想法:

package test.util;

import java.lang.reflect.Method;

import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import test.mapper.UserMapper;

public class MyImport implements ImportBeanDefinitionRegistrar,InvocationHandler {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//得到代理对象
		UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {UserMapper.class}, this);
		System.out.println("userMapper="+userMapper.getAll());
		//构建bd
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(userMapper.getClass());
		//得到bd
		GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
		//注册bd
		registry.registerBeanDefinition("userMapper", beanDefinition);
	}

	@Override
	public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
		System.out.println("代理");
		return null;
	}

}

建立一个测试类:

package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import test.mapper.UserMapper;

public class App {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
		UserMapper bean = (UserMapper) ac.getBean("userMapper");
		bean.getAll();
	}
}

配置类:

package test;
import test.util.MyProxy;

@MyProxy
public class Appconfig {

}

我们看MyImport这个类。
先运行,结果如下:

14:17:13.885 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@311d617d
14:17:13.904 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
代理
userMapper=null
14:17:14.018 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
14:17:14.021 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
14:17:14.022 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
14:17:14.024 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
14:17:14.033 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appconfig'
14:17:14.039 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userMapper'
14:17:14.044 [main] DEBUG org.springframework.core.LocalVariableTableParameterNameDiscoverer - Cannot find '.class' file for class [class org.springframework.cglib.proxy.Proxy$ProxyImpl$$EnhancerByCGLIB$$5ecfa6bd] - unable to determine constructor/method parameter names
14:17:14.054 [main] WARN org.springframework.context.annotation.AnnotationConfigApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userMapper': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cglib.proxy.InvocationHandler' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userMapper': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cglib.proxy.InvocationHandler' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:228)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1358)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1204)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:89)
	at test.App.main(App.java:11)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.cglib.proxy.InvocationHandler' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
	... 14 more

可以发现代理的对象无法注册到容器当中。这也很好理解,虽然我们将接口代理成了一个对象,但是它并不符合spring生产bean的流程,因此在注册的时候就不满足bean的条件,导致没有办法注册。
那么怎么解决呢?
我们现在需要的是一个完整的bean,需要经过spring的流程,这里自然而然就想到了FactoryBean了,因为它最大的特殊点在于它能够生产一个bean对象出来。
同时因为我们为了动态的构造,需要提供构造方法去手动传一个class进去。
改造后如下:

package test.util;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import test.mapper.UserMapper;

public class MyImport implements ImportBeanDefinitionRegistrar {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		//得到代理对象
		//UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] {UserMapper.class}, this);
		//System.out.println("userMapper="+userMapper.getAll());
		//构建bd
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserMapper.class);
		//得到bd
		GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
		//设置构造方法
		beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClass());
		//设置bd
		beanDefinition.setBeanClass(MyFactoryBean.class);
		//注册bd
		registry.registerBeanDefinition("userMapper", beanDefinition);
	}

}

package test.util;

import java.lang.reflect.Method;

import org.apache.ibatis.annotations.Select;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import test.mapper.UserMapper;

public class MyFactoryBean implements FactoryBean,InvocationHandler{

	Class clazz;
	public MyFactoryBean(Class clazz) {
		this.clazz=clazz;
	}
	@Override
	public Object getObject() throws Exception {
		System.out.println("getObject");
		Object object = Proxy.newProxyInstance(this.getClass().getClassLoader(),
				new Class[] {clazz}, this);
		return object;
	}

	@Override
	public Class getObjectType() {
		return clazz;
	}

	@Override
	public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
		//arg0:代理对象
		//arg1:对象上的方法
		//arg2:参数
		System.out.println("代理");
		Method method = arg0.getClass().getInterfaces()[0].getMethod(arg1.getName());
		Select select = method.getDeclaredAnnotation(Select.class);
		System.out.println("select="+select.value()[0]);
		
		return null;
	}

}

最后我们模拟拿到注解的值。具体操作可以自行模拟了,因为到了这一步,我们已经把接口做成了对象,再想想以前做的项目,自动注入应该就能懂了。
BeanDefinitionRegistryPostProcessor也能实现,只要上面的做出来了这个就很简单了。这里就不写怎么实现出来的了。
结果展示:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值