Spring源码深度解析(四)——模拟mybatis和原理分析
说到Mybatis就顺理成章的想起了@Mapper,@MapperScan,@Select,@Update等等注解,下面就来模拟一下它的过程。
用到的知识点:自定义注解,FactoryBean,Spring的构造方法装配,ImportBeanDefinitionRegistrar,@Import,JDK动态代理
需要去模拟mybatis需要想清楚的是目标是什么?
- 目标就是将抽象接口进行注入值,也就是将我们实现的daoImpl注入到dao
- 将@Select,@Update中的东西拿出来
第一个目标怎么去实现,很明显这是需要代理的,因为dao和daoImpl根本就不是同一个类,因此我们就顺理成章的想到了使用FactoryBean构造一个自定义返回的Bean,然后里面通过动态代理完成对daoImol的改变(改变其内部的BeanDefinition)
第二个目标很简单,那就是得到类,然后得到类里方法,并对方法上面的注解的值进行解析
下面就来具体的实现一下
创建IndexDao和IndexDaoImpl
package mybatis.dao;
/*
* @Author Wrial
* @Date Created in 14:39 2020/2/25
* @Description
*/
import mybatis.anno.Select;
import java.util.List;
import java.util.Map;
public interface IndexDao {
@Select("select * from student")
List<Map<Integer,String>> list(String string);
}
package mybatis.dao;
/*
* @Author Wrial
* @Date Created in 20:01 2020/2/25
* @Description
*/
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class IndexDaoImpl implements IndexDao {
@Override
public List<Map<Integer, String>> list(String string) {
System.out.println("IndexDaoImpl");
return null;
}
}
自定义@Select注解
package mybatis.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* @Author Wrial
* @Date Created in 20:17 2020/2/25
* @Description
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Select {
String value();
}
创建IndexService,并@AutowireIndexDao
package mybatis.config;
/*
* @Author Wrial
* @Date Created in 14:45 2020/2/25
* @Description
*/
import mybatis.anno.Select;
import mybatis.dao.IndexDao;
import org.springframework.beans.factory.FactoryBean;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyFactoryBean implements FactoryBean<Object>, InvocationHandler {
// 保证通用性,根据传入的class(接口)进行代理
Class<?> clazz;
// 提供一个构造方法,会在MyImportDefinitionRegistrar进行构造填充
public MyFactoryBean(Class<?> clazz){
this.clazz = clazz;
}
@Override
public Object getObject(){
Class<?>[] classes = new Class<?>[]{clazz};
IndexDao object = (IndexDao)Proxy.newProxyInstance(clazz.getClassLoader(),classes,this);
// 产生的是一个代理对象(经过ImportDefinitionRegistrar对BD处理过)
return object;
}
@Override
public Class<?> getObjectType() {
return clazz;
}
/**
* 动态代理需要实现的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy————————————————————————————————————————");
// 拿到方法
Method method1 = proxy.getClass().getInterfaces()[0].getMethod(method.getName(), String.class);
// 拿出我们自定义的Select注解并打印
Select select = method1.getDeclaredAnnotation(Select.class);
System.out.println(select.value());
return null;
}
}
这个是我们流程实现的核心步骤,它可以对BeanDefinition进行修改,也可以给它的构造方法中添加值,这块下面会结合构造方法构造Bean的源码中解疑惑
package mybatis.config;
/*
* @Author Wrial
* @Date Created in 14:57 2020/2/25
* @Description
*/
import lifecycle.Test;
import mybatis.dao.IndexDao;
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;
public class MyImportDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
//修改FactoryBean的定义
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(IndexDao.class);
GenericBeanDefinition beanDefinition = (GenericBeanDefinition) beanDefinitionBuilder.getBeanDefinition();
System.out.println(beanDefinition.getBeanClassName());
//传入beanDefinition.getBeanClassName()
// 给构造方法中添加一个参数
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("mybatis.dao.IndexDao");
beanDefinition.setBeanClass(MyFactoryBean.class);
registry.registerBeanDefinition("indexDao",beanDefinition);
}
}
接下来是编写配置类
package mybatis.config;
/*
* @Author Wrial
* @Date Created in 14:42 2020/2/25
* @Description
*/
import mybatis.anno.MyMapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@ComponentScan("mybatis")
//可以将下面放在注解里
//@Import(MyImportDefinitionRegistrar.class)
@MyMapperScan
public class MyConfig {
}
自定义MapperScan注解
package mybatis.anno;
/*
* @Author Wrial
* @Date Created in 14:34 2020/2/25
* @Description 自定义的MapperScan
*/
import mybatis.config.MyImportDefinitionRegistrar;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportDefinitionRegistrar.class)
public @interface MyMapperScan {
}
编写测试类
package mybatis.test;
/*
* @Author Wrial
* @Date Created in 14:32 2020/2/25
* @Description 模拟Mybatis
*/
import mybatis.config.MyConfig;
import mybatis.dao.IndexDao;
import mybatis.dao.IndexDaoImpl;
import mybatis.service.IndexService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
// 加载自定义的配置类
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
/* IndexDao dao = (IndexDao) applicationContext.getBean("indexDao");
dao.list();*/
applicationContext.getBean(IndexService.class).printIndexDao();
}
}
结果展示:
这样很简单的就实现了Mybatis的@MapperScan和抽象类型的动态注入和值的解析!
下面就结合Spring源码来说一说添加的这个ConstructorValue有什么用,怎么去用。
简单的先说一下流程,Spring在构造Bean的时候回将无参的和有参数的分开,在有参数的会对参数进行解析,然后是值的解析,如果内部有@Resource,@Autowire就会通过循环CommonBeanPostProcessor和InstantiationAwareBeanPostProcessor后置处理器进行处理。
下面先从这段源码开始看起
// 为Bean创建实例
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 检查这个类的访问权限 方法是不是public
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// factoryMethod 可以在xml中配置某个自定义方法,注册的Bean就是方法返回的Bean,而不是当前被factoryMethod修饰的Bean
// 如果是FactoryMethod 就讲那个方法的返回值放入map
// 比如在@Bean修饰的方法是静态方法的时候,就会给设置factoryMethod为当前方法 就相当于给Bean配置了一个FactoryMethod
// 如果@Bean修飾的方法不是静态的,那就是uniqueFactoryMethod,是唯一的(不会每次都去new),static不是唯一的
// 为什么bean不直接放进去?很显然如果直接放那就没有@Bean这一说法了
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut(快捷方式) when re-creating the same bean...
// 当重新创建相同的Bean的时候就不用去做一系列的推断(不用推断去用那种方式去构造这个Bean)
// 比如多次构造一个原型对象bean就可通过这个shortcut 此处resolved和autowireNecessary会在第一次进行设置
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 如果是通过构造方法/factoryMethod进行解析的 那就对上面的变量进行设置
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
// 如果已经被解析
if (resolved) {
//如果是必须自动装配的
if (autowireNecessary) {
// 就返回自动装配的BeanWrapper(通过构造方法)
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 否则就此方法进行初始化
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
// 由后置处理器决定返回那些构造方法(如果是无参构造方法就为null)
// 默认调用的无参构造方法 也就是说@Component一个类,有两个构造方法
// 一个是有参的一个是无参的,在扫描实例化后会默认调用无参的
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 如果是有参构造 或者是自动装配模式是CONSTRUCTOR(默认是no但是采用的是byType的技术,一共有四种)
// Spring自动装配模型!=自动装配的技术 no==byType的技术
// 也就是@Autowire 默认mode是no 但是使用byType进行自动装配
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// 无参构造也就是普通处理类型
return instantiateBean(beanName, mbd);
}
进入autowireConstructor(beanName, mbd, ctors, args)方法
// 构造方法自动装配 explicitArgs:用过getBean以编程方式传入的参数 其实就是我们设置的那个值
// Class是无法进行自动装配的,必须是对象
protected BeanWrapper autowireConstructor(
String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}
进入new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs)
// 使用构造方法自动装配返回一个BeanWrapper
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
@Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
// 实例化一个BeanWrapperImpl 实现BeanWrapper
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
// 用到的构造方法
Constructor<?> constructorToUse = null;
// 需要的参数
ArgumentsHolder argsHolderToUse = null;
// argsToUse有两种办法进行设置 一种通过BeanDefinition 另一种就是XML
Object[] argsToUse = null;
if (explicitArgs != null) {
argsToUse = explicitArgs;
} else {
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
// 获取已经解析的构造方法或者是factoryMethod 下次就不需要去解析了,直接用哪个值就行了
// 一般情况不会有,除非是多个构造方法的
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached constructor...
// 如果有缓存到的构造器 然后拿到缓存的参数
argsToUse = mbd.resolvedConstructorArguments;
// 如果没有,那就preparedConstructorArguments
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
// 如果argsToResolve等于空说明有缓存就不用执行此处 不为空然后在这进行解析参数
if (argsToResolve != null) {
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve, true);
}
}
// 如果constructorToUse或argsToUse为空 说明没有缓存 下面就进行解析构造方法和参数
if (constructorToUse == null || argsToUse == null) {
// Take specified constructors, if any.
// chosenCtors就是在判断是有参的时候传进来的
Constructor<?>[] candidates = chosenCtors;
// 构造方法不为空的话(一般都不为空,不然不会进入这里代码)
if (candidates == null) {
Class<?> beanClass = mbd.getBeanClass();
try {
// 判断是否允许非public方法构造,如果允许就拿所有,不允许就拿公有
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
} catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
}
// 针对于一个构造器,explicitArgs(通过getBean以编程方式传入的参数)为null并且在构造器判断参数是否被定义(是否有用的参数)
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];
if (uniqueCandidate.getParameterCount() == 0) {
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
// Need to resolve the constructor.
// 判断构造方法是否为null 或AutowireMode是否为构造方法自动装配 满足其一就为true
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
//最小参数个数
int minNrOfArgs;
//通过getBean传进的参数(就说名是具体的,参数个数固定)的个数赋值给minNrOfAges
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
} else {
// ConstructorArgumentValues是一个存储构造方法值的一个数据结构
// 先去拿已有的
// 比如:在模仿mybatis的时候回在bdm中setConstructorArgumentValues(“xxxx”)
// 就是这个时候取出来的
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
// 在new一个新的 最后应该会被合并
resolvedValues = new ConstructorArgumentValues();
// 确定构造方法参数的数量并返回 就是上面至少需要setConstructorArgumentValues的个数
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
// 对构造方法进行排序
/**
* 排序的序列如下 (按照访问权限+参数个数)
* public 最多参数的
* public 依次到一个参数的
* protected 多个参数的
* protected 依次到一个参数的
*/
AutowireUtils.sortConstructors(candidates);
// 最小类型的差异权重 差异变量 很重要
int minTypeDiffWeight = Integer.MAX_VALUE;
// 存有歧义的构造方法
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Constructor<?> candidate : candidates) {
Class<?>[] paramTypes = candidate.getParameterTypes();
// 如果参数个数
if (constructorToUse != null && argsToUse != null && argsToUse.length > paramTypes.length) {
// Already found greedy constructor that can be satisfied ->
// do not look any further, there are only less greedy constructors left.
break;
}
// 如果参数列表的长度小于最小参数长度(那就直接跳过当前这个Constructor<?> candidate)
if (paramTypes.length < minNrOfArgs) {
continue;
}
ArgumentsHolder argsHolder;
if (resolvedValues != null) {
try {
// 判断是否加了ConstructorProperties注解 如果加了就把值取出来
// 如@ConstructorProperties(value={“xxx”,“xxxx”})
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
// 获取此构造方法的参数列表
paramNames = pnd.getParameterNames(candidate);
}
}
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
} catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
}
// Swallow and try next constructor.
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue;
}
} else {
// Explicit arguments given -> arguments length must match exactly.
if (paramTypes.length != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
// 根据差异值来选择最匹配的构造方法
/*
第一遍一定会进,因为minTypeDiffWeight初始化为最大值
*/
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
} else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
if (constructorToUse == null) {
if (causes != null) {
UnsatisfiedDependencyException ex = causes.removeLast();
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
throw ex;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Could not resolve matching constructor " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
// 如果差异值相同,就是有歧义的构造方法,就抛出异常
// 为什么不在上面循环中找到差异值相同就直接抛出异常呢?因为还不能确定它就是最优的选择,不过不是最优选择,就算相同(重复)也没有关系
} else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Ambiguous constructor matches found in bean '" + beanName + "' " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
ambiguousConstructors);
}
if (explicitArgs == null && argsHolderToUse != null) {
// 将构造方法,参数,是否被解析的标志缓存起来
argsHolderToUse.storeCache(mbd, constructorToUse);
}
}
Assert.state(argsToUse != null, "Unresolved constructor arguments");
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
return bw;
}
打开调试
可以定位到indexDao,可以发现它的构造方法是我们FactoryBean的构造方法,并且是一个有参的构造方法
可以看到执行到这个位置的时候会发现它会进行构造方法解析,并且可以看到类型是IndexDao
至此,我们就很清晰它是怎么讲我们添加的构造方法添加进去的,然后是怎么实例化Bean的
实例化Bean是一个很复杂的事情,会在另外一篇博客中细说!
上一篇: java利用mybatis拦截器统计sql执行时间示例
下一篇: 简单理解关于分类模型的评估