《Java Spring框架》模拟Mybatis整合Spring

前言

为了更好的了解Spring的扩展,于是便去研究了MyBatis整合Spring的Jar代码。通过代码模拟写一个Mybatis整合Spring作为笔记留念。

代码

目录结构:

MapScan 

import com.my.postprocessor.MyImportBeanDefinitionRegistrar;
import org.springframework.context.annotation.Import;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@Import(MyImportBeanDefinitionRegistrar.class)
public @interface MapScan {
    String value() default "";
}

MyFactoryBean 

import com.my.mybatis.MySqlSession;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

/**
 * 猜想MyBatis为什么要通过FactoryBean来注入Spring,
 * 而不是直接扫描DAO接口生成Definition的时候直接将代理类也set进Definition中。
 */
@Component
public class MyFactoryBean implements FactoryBean {

    Class mapperInterface;

    @Override
    public Object getObject() throws Exception {
        MySqlSession mySqlSession = new MySqlSession();
        Object mapper = mySqlSession.getMapper(mapperInterface);
        return mapper;
    }

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

    public void setMapperInterface(Class mapperInterface) {
        this.mapperInterface = mapperInterface;
    }
}

ClassPathMapperScanner

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.filter.TypeFilter;

import java.util.Set;

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

    public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
        super(registry,false);
    }

    @Override
    public void addIncludeFilter(TypeFilter includeFilter) {
        super.addIncludeFilter(includeFilter);
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        return super.doScan(basePackages);
    }

    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
}

AccountInfoDao

import org.apache.ibatis.annotations.Select;

public interface AccountInfoDao {

    @Select("select * from taccount")
    public String getAccount();
}

AccountInfoDao1

import org.apache.ibatis.annotations.Select;

public interface AccountInfoDao1 {

    @Select("select * from taccount1")
    public String getAccount();
}

MySqlSession

import org.apache.ibatis.annotations.Select;

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

public class MySqlSession {

    class MyInvocationHandler implements InvocationHandler {
        @Override
        public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
                    // 获取方法上的注解
            Select annotation = method.getAnnotation(Select.class);

            System.out.println("打开数据库连接");

            System.out.println("执行SQL:" + annotation.value()[0]);

            System.out.println("关闭数据库连接");

            if(method.getName().equals("toString")){
                return "toString";
            }

            return "动态代理反馈";
        }
    }

    public Object getMapper(Class clazz){
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        Object object = Proxy.newProxyInstance(MySqlSession.class.getClassLoader(),
                new Class[]{clazz}, myInvocationHandler);
        return object;
    }
}

MyImportBeanDefinitionRegistrar

import com.my.annotation.MapScan;
import com.my.bean.MyFactoryBean;
import com.my.config.ClassPathMapperScanner;
import com.my.dao.AccountInfoDao;
import com.my.dao.AccountInfoDao1;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
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.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes annotations = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapScan.class.getName()));
        ClassPathMapperScanner classPathMapperScanner = new ClassPathMapperScanner(registry);
        classPathMapperScanner.addIncludeFilter((metadataReader,metadataReaderFactory) -> { return true; });
        Set<BeanDefinitionHolder> beanDefinitionHolders = classPathMapperScanner.doScan((String)annotations.get("value"));

        Iterator<BeanDefinitionHolder> it = beanDefinitionHolders.iterator();
        while (it.hasNext()) {
            BeanDefinitionHolder beanDefinitionHolder = it.next();
            GenericBeanDefinition definition = (GenericBeanDefinition)beanDefinitionHolder.getBeanDefinition();
            definition.getPropertyValues().addPropertyValue("mapperInterface",definition.getBeanClassName());
            definition.setBeanClass(MyFactoryBean.class);
        }
    }
}

 IndexService

import com.my.dao.AccountInfoDao;
import com.my.dao.AccountInfoDao1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class IndexService {

    @Autowired
    AccountInfoDao accountInfoDao;

    @Autowired
    AccountInfoDao1 accountInfoDao1;

    public void test(){
        System.out.println(accountInfoDao.getAccount());
        System.out.println("===============================");
        System.out.println(accountInfoDao1.getAccount());
    }
}

MyTest

import com.my.AppConfig;
import com.my.service.IndexService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ac
                = new AnnotationConfigApplicationContext(AppConfig.class);

        IndexService indexService = ac.getBean(IndexService.class);
        indexService.test();
    }
}

 AppConfig

import com.my.annotation.MapScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan("com.my")
@MapScan("com.my.dao")
public class AppConfig {
}

依赖包只需要一个即可:spring-context

运行结果:

总结

从别人的代码里面学习如何写优美的代码。

posted @ 2021-06-02 10:57  加速丨世界  阅读(197)  评论(0编辑  收藏  举报