MyBatis整合Spring原理介绍与手写实现(整合了之前实现的Sping、Mybatis、SpringMVC)

2 篇文章 0 订阅
2 篇文章 0 订阅

 

一,MyBatis整合Spring原理介绍:

1,在mybatis-spring的源码中,有一个把spring和mybatis结合起来的类:MapperScannerConfigurer

这个类实现了Spirng的BeanDefinitionRegistryPostProcessor接口,这是利用了spring的一个扩展点,Spring在初始化容器的扫描注册BeanDefinition之后,会调用所有实现了BeanDefinitionRegistryPostProcessor接口的类的postProcessBeanDefinitionRegistry方法,给第三方或者程序员一次修改BeanDefinition集合的机会。那么mybatis就是在这里把Mapper接口的定义注册到Spring中,交由Spring来管理的。

2,但是有一个问题,就是Mapper都是接口,接口是不能直接new的,这里就需要另外一个类:MapperFactoryBean

这个类实现了Spring的FactoryBean接口,说明这个一个工厂bean。在把Mapper接口信息封装为BeanDefinition注册到Spring之后,又修改了这些由Mybatis注册的BeanDefinition,把原来的Mapper接口的Class替换成了MapperFactoryBean这个工厂bean的Class,并标识这个一个工厂bean。这样Spring在通过BeanDefinition实例化的时候就会实例化出一个MapperFactoryBean对象,然后由MapperFactoryBean来创建Mapper实例。

补充说明:Spring的获取bean的时候,如果这是一个工厂bean,则会调用工厂bean的getObject()方法来获取由工厂bean生成的bean实例。

而在MapperFactoryBean这个工厂bean中,getObject()方法里面调用的就是mybatis提供的获取mapper对象的方法,源码如下:

    public T getObject() throws Exception {
        return this.getSqlSession().getMapper(this.mapperInterface);
    }

总结:一句话描述就是:mybatis利用Spring的后置处理器扩展点注册了MapperFactoryBean,MapperFactoryBean可以生产mapper对象。

二,代码实现逻辑

基于之前写的springIOC容器和mybatis代码来实现整合。

实现逻辑:

1,SpringIOC容器提供一个BeanDefinitionRegistryPostProcessor接口,然后找出实现了这个接口的类,调用其中的postProcessBeanDefinitionRegistry()方法。

2,mybatis中创建一个类MapperBeanDefinitionRegistryPostProcessor实现BeanDefinitionRegistryPostProcessor接口,将mapper接口注册到Spring的BeanDefinition集合中,并修改class为工厂bean。

3,修改之前Spring的代码,在创建bean的时候判断是否为工厂bean,如果是,则创建工厂bena之后调用getObject方法返回bean对象。

 

三,写代码实现

1,基于之前的Spring和SpringMVC整合的项目,新建一个用于实现mybatis的模块,把之前写的mybatis简单实现的代码放到这个模块中继续开发。

2,实现Spring中BeanDefinitionRegistryPostProcessor接口扩展点的功能:

2.1 创建BeanDefinitionRegistryPostProcessor这个接口:

package com.salulu.minisp.context.support;

import com.salulu.minisp.context.implement.AnnotatedBeanDefinitionReader;

public interface BeanDefinitionRegistryPostProcessor{

	void postProcessBeanDefinitionRegistry(AnnotatedBeanDefinitionReader registry);

}

2.2 修改之前的AnnotatedBeanDefinitionReader类

在扫描包完成之后,找出实现了BeanDefinitionRegistryPostProcessor接口的类,然后执行类中的postProcessBeanDefinitionRegistry()方法。

/**
     * 进行包扫描,并封装beanDefinition对象放入BeanFactory的bdMap中
     */
    public void doScanner(){
        String basePackage = findScanPath();
        // 扫描指定包的Class文件
        List<Class> classList = new ArrayList<>();
        scanClassFile(basePackage,classList);
        // 过滤需要由SpingIOC容器管理的class
        List<Class> filterClassList = filterBeanDefinition(classList, new Class[]{Component.class, Controller.class, Service.class});
        // 注册到BeanDefinitionMap中
        registryBeanDefinition(filterClassList);
        System.out.println("包扫描完成");
        // 找出BeanDefinitionRegistry的后置处理器放到一个list集合中
        findBeanDefinitionRegistryPostProcessorImpl();
        // 执行实现了BeanDefinitionRegistryPostProcessor接口的类
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(this,beanFactoryPostProcessors);
    }

2.2.1找出BeanDefinitionRegistry的后置处理器放到一个list集合中

/**
     * 找出BeanDefinitionRegistry的后置处理器放到一个list集合中
     */
    private void findBeanDefinitionRegistryPostProcessorImpl() {
        // 获取所有的.class
        List<File> classFileList = getAllClassFile(rootPath);
        // 根据.class文件得到Class对象
        List<Class> classList = getClassListByFile(classFileList);
        // 遍厉Class对象,找出实现了BeanDefinitionRegistryPostProcessor接口的后置处理器类,并把这个类实例化,然后放入一个list集合中
        findBeanDefinitionRegistryPostProcessorImpl(classList);
    }


//获取所有的.class
    private List<File> getAllClassFile(String path){
        List<File> classFileList = new ArrayList<>();
        File rootFile = new File(path);
        for (File file : rootFile.listFiles()) {
            if(file.isDirectory()){
                List<File> allClassFile = getAllClassFile(file.getAbsolutePath());
                classFileList.addAll(allClassFile);
            }else {
                if(file.getName().endsWith(".class")){
                    classFileList.add(file);
                }
            }
        }
        return classFileList;
    }



    // 将.class文件转为Class对象
    private List<Class> getClassListByFile(List<File> classFileList){
        List<Class> classList = new ArrayList<>();
        for (File file : classFileList) {
            String absolutePath = file.getAbsolutePath();
            int index = absolutePath.indexOf("com/");
            if(index<0) continue;
            String substring = absolutePath.substring(index);
            String className = substring.replace(File.separator,".").replace(".class","");
            // 得到class
            Class<?> aClass = null;
            try {
                aClass = Class.forName(className);
                classList.add(aClass);
            } catch (ClassNotFoundException e) {
                //e.printStackTrace();
            }
        }
        return classList;
    }


/**
     * 找出BeanDefinitionRegistryPostProcessor的后置处理器放到一个list集合中
     * @param classList
     */
    private void findBeanDefinitionRegistryPostProcessorImpl(List<Class> classList){
        for (Class aClass : classList) {
            Class[] interfaces = aClass.getInterfaces();
            for (Class anInterface : interfaces) {
                if(anInterface == BeanDefinitionRegistryPostProcessor.class){
                    try {
                        Object obj = aClass.newInstance();
                        beanFactoryPostProcessors.add((BeanDefinitionRegistryPostProcessor)obj);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

2.2.2  执行实现了BeanDefinitionRegistryPostProcessor接口的类的postProcessBeanDefinitionRegistry()方法

// 执行实现了BeanDefinitionRegistryPostProcessor接口的类
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(this,beanFactoryPostProcessors);

新建一个PostProcessorRegistrationDelegate类来调用执行:

package com.salulu.minisp.context.support;

import com.salulu.minisp.context.implement.AnnotatedBeanDefinitionReader;

import java.util.List;

public class PostProcessorRegistrationDelegate {

    public static void invokeBeanFactoryPostProcessors(AnnotatedBeanDefinitionReader registry, List<BeanDefinitionRegistryPostProcessor> beanDefinitionRegistryPostProcessorList) {
        for (BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor : beanDefinitionRegistryPostProcessorList) {
            beanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry(registry);
        }
    }
}

 

2.3 修改实例化bean的逻辑,加一个判断,如果是工厂bean的话需要做特殊处理:

/**
     * 创建bean实例
     * @param beanName
     * @return
     */
    private Object doCreateBean(String beanName) {
        Object newInstance = null;
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        // 判断是否为工厂bean
        if (beanDefinition.isFactoryBean()) { //  工厂bean
            Constructor<?> factoryBeanConstructor = beanDefinition.getFactoryBeanConstructor();
            try {
                FactoryBean factoryBean = (FactoryBean)factoryBeanConstructor.newInstance(beanDefinition.getBeanClass());
                // 通过工厂bean生成出对应的bean对象
                newInstance = factoryBean.getObject();

                // 封装工厂bean对象后放入二级缓存中
                ObjectFactory objectFactory = new ObjectFactory();
                objectFactory.setBeanDefinition(beanDefinition);
                objectFactory.setBean(newInstance);
                singletonFactories.put(beanName,objectFactory);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }else{// 为非工厂bean
            Class beanClass = beanDefinition.getBeanClass();
            // Spring 先推断构造方法,然后反射调用构造方法来实例化
//        Constructor declaredConstructor = beanClass.getDeclaredConstructor();
//        declaredConstructor.setAccessible(true);
//        Object o = declaredConstructor.newInstance(null);
            // 这里直接不用那么麻烦,直接反射生产实例

            try {
                newInstance = beanClass.newInstance();
                // 先放入三级缓存
                earlySingletonObjects.put(beanName,newInstance);
                Field[] declaredFields = beanClass.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    Autowirted autowirted = declaredField.getAnnotation(Autowirted.class);
                    if(autowirted==null) continue;
                    Class<?> fieldType = declaredField.getType();
                    String simpleName = BeanDefinitionReaderUtils.getBeanByClass(fieldType);
                    Object fieldBean = doGetBean(simpleName);
                    declaredField.setAccessible(true);

                    declaredField.set(newInstance,fieldBean);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return newInstance;
    }

3,实现mybatis与Spring整合的代码:

在原来的com.salulu包下新建一个spring的包,用来存放整合的代码:

3.1 创建MapperFactoryBean类,实现工厂bean接口的方法。

主要是实现getObject()方法:从SqlSession中获取Mapper对象

package com.salulu.spring;

import com.salulu.minisp.context.factory.FactoryBean;

public class MapperFactoryBean<T> implements FactoryBean<T> {

    private Class<T> mapperInterface;

    public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }


    @Override
    public T getObject() throws Exception {
        // 从SqlSession中获取Mapper对象
        T mapper = SqlSessionSupport.sqlSession.getMapper(this.mapperInterface);
        return mapper;
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}

这个SqlSessionSupport中其实是放了一个可以全局方法问的SqlSession对象。

package com.salulu.spring;

import com.salulu.baits.session.SqlSession;

public class SqlSessionSupport {

    public static SqlSession sqlSession = null;

}

3.2 创建MapperBeanDefinitionRegistryPostProcessor类并实现BeanDefinitionRegistryPostProcessor接口:

在postProcessBeanDefinitionRegistry()方法中主要完成一下逻辑:

1,扫描mapper接口注册到Spring中

2,读取mybatis的配置文件

3,构建session工厂

4,打开sqlsession得到SqlSession对象

5,给SqlSessionSupport的SqlSession赋值

代码如下:

package com.salulu.spring;

import com.salulu.baits.annotations.Mapper;
import com.salulu.baits.io.Resources;
import com.salulu.baits.session.SqlSession;
import com.salulu.baits.session.SqlSessionFactory;
import com.salulu.baits.session.SqlSessionFactoryBuilder;
import com.salulu.minisp.context.implement.AnnotatedBeanDefinitionReader;
import com.salulu.minisp.context.support.BeanDefinition;
import com.salulu.minisp.context.support.BeanDefinitionRegistryPostProcessor;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

public class MapperBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    private static Class<?> factoryBeanClass = MapperFactoryBean.class;

    @Override
    public void postProcessBeanDefinitionRegistry(AnnotatedBeanDefinitionReader registry){

        // 扫描指定包的Class文件
        List<Class> classList = new ArrayList<>();
        registry.scanClassFile("com",classList);// 这里写死com开头的包
        // 过滤需要由SpingIOC容器管理的class
        classList = registry.filterBeanDefinition(classList, new Class[]{Mapper.class});
        // 注册到BeanDefinitionMap中
        for (Class aClass : classList) {
            BeanDefinition beanDefinition = new BeanDefinition(aClass);
            beanDefinition.setFactoryBean(true);// 标示为工厂bean
            beanDefinition.setFactoryBeanClass(factoryBeanClass);
            Constructor<?> constructor = null;
            try {
                constructor = factoryBeanClass.getConstructor(Class.class);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            beanDefinition.setFactoryBeanConstructor(constructor);
            registry.registryBeanDefinition(beanDefinition);// 注册工厂bean的定义
        }

        // 1,读取配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 2,构建session工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 3,打开sqlsession
        SqlSession session = sqlSessionFactory.openSession();
        // 给SqlSessionSupport的SqlSession赋值
        SqlSessionSupport.sqlSession = session;

    }
}

 

4,测试

4.1 在测试模块中加入salulu-batis模块和mysql驱动的依赖

4.2 将之前batis中的mybatis-config.xml文件和UserMapper.xml文件拷贝到resource目录下

4.3 完成config,mapper,service,controller类的测试代码

controller中调用userService:

package com.salulu.test.controller;

import com.salulu.minisp.context.annotate.Autowirted;
import com.salulu.minisp.context.annotate.Controller;
import com.salulu.test.entity.User;
import com.salulu.test.service.UserServiceA;
import com.salulu.web.annotate.RequestMapping;
import com.salulu.web.annotate.RequestParam;
import com.salulu.web.domain.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowirted
    private UserServiceA userServiceA;

    //模拟返回json数据
    @RequestMapping("/getUserJson")
    public String getUserJson(HttpServletRequest request, HttpServletResponse response,@RequestParam("id") Integer id){
        User user = userServiceA.getById(id);
        return user.toString();
    }

userService中调用userMapper

package com.salulu.test.service;

import com.salulu.minisp.context.annotate.Autowirted;
import com.salulu.minisp.context.annotate.Component;
import com.salulu.test.entity.User;
import com.salulu.test.mapper.UserMapper;


@Component
public class UserServiceA {

    @Autowirted
    private UserMapper userMapper;

    public UserServiceA(){
        System.out.println("UserServiceA 构造方法被调用");
    }

    public User getById(int id){
        User user = userMapper.selectByPrimaryKey(id);
        return user;
    }

}

4.4 使用postMan请求接口:

请求成功:成功获取到数据库中id等于2的数据。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值