美文网首页
2020-06-25_Mybatis和Spring整合高级系列知

2020-06-25_Mybatis和Spring整合高级系列知

作者: kikop | 来源:发表于2020-06-26 19:26 被阅读0次

    Mybatis和Spring整合高级系列知识学习之factoryBeanByParseAnnotation8

    1概述

    我们知道MyBatis的Mapper Bean扫描注入是通过MapperScan注解扫描basePackages完成的,Java代码配置的方式如下:

    Java代码方式:

    
    @Configuration
    
    // 本质:扫描 service、bean、controller, 转成 BeanDefinition类,然后交给 spring管理
    
    @ComponentScan("com.kikop.myspringstudy.mybatislinkspring1")
    
    // mybatis 自带的生成动态代理类
    
    // mybatis会扫描 classpath*:com/kikop/myspringstudy/mycommon/mapper/**/*.class
    
    @MapperScan("com.kikop.myspringstudy.mycommon.mapper")
    
    public class AppConfig {
    
    

    本节来模仿MapperScan注解(存在mybatis-spring包中)方式手动实现注解及对应的参数动态解析。通过扫描mapper包名, 完成多个mapper bean的注入。

    1.1 MapperScan核心类MapperScannerRegistrar

    通过如下配置,spring会扫描classpath* com.kikop.myspringstudy.mycommon.mapper的所有java类,包括子目录,排除package-info.java。

    // mybatis 自带的生成动态代理类

    // mybatis会扫描 classpath:com/kikop/myspringstudy/mycommon/mapper//.class

    @MapperScan("com.kikop.myspringstudy.mycommon.mapper")

    1.2 SpringClassUtils

    spring-core-5.1.6.RELEASE-sources.jar!\org\springframework\util\ClassUtils.java

    
    // 取 className最后的名称
    
    public static String getShortName(String className) {     Assert.hasLength(className, "Class name must not be empty");     int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);     int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);     if (nameEndIndex == -1) {        nameEndIndex = className.length();     }     String shortName = className.substring(lastDotIndex + 1, nameEndIndex);     shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);     return shortName;  }
    
    // 首字母小写(URL except)
    
    protected String buildDefaultBeanName(BeanDefinition definition) {
    
    String beanClassName = definition.getBeanClassName();
    
    Assert.state(beanClassName != null, "No bean class name set");
    
    String shortClassName = ClassUtils.getShortName(beanClassName);
    
    return Introspector.decapitalize(shortClassName);
    
    }
    
    

    2 多个Bean注入(扫描mapper类目录)

    2.1 config

    
    package com.kikop.myspringstudy.mybatislinkspring8.config;
    
    import com.kikop.myspringstudy.mybatislinkspring8.annotations.MyMapperScan2;
    
    import org.mybatis.spring.SqlSessionFactoryBean;
    
    import org.springframework.context.annotation.Bean;
    
    import org.springframework.context.annotation.ComponentScan;
    
    import org.springframework.context.annotation.Configuration;
    
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    
    import javax.sql.DataSource;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: AppConfig
    
     * @desc 功能描述
    
     * @date 2020/6/22
    
     * @time 8:43
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    @Configuration
    
    // 本质:扫描 service、bean、controller, 转成 BeanDefinition类,然后交给 spring管理
    
    @ComponentScan("com.kikop.myspringstudy.mybatislinkspring8")
    
    // mybatis 自带的生成动态代理类
    
    //@MapperScan("com.kikop.myspringstudy.mycommon.mapper")
    
    //参数传给:AnnotationMetadata
    
    @MyMapperScan2("com.kikop.myspringstudy.mycommon.mapperbak")
    
    public class AppConfig {
    
        @Bean
    
        public DataSource dataSource() {
    
            // 基于 Spring-jdbc
    
            DriverManagerDataSource drivermanagerDataSource = new DriverManagerDataSource();
    
            drivermanagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
    
            drivermanagerDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis?userUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true");
    
            drivermanagerDataSource.setUsername("root");
    
            drivermanagerDataSource.setPassword("123456");
    
            return drivermanagerDataSource;
    
        }
    
        @Bean
    
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
    
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    
            sqlSessionFactoryBean.setDataSource(dataSource);
    
            return sqlSessionFactoryBean;
    
        }
    
    }
    
    

    2.2 service

    
    package com.kikop.myspringstudy.mybatislinkspring1.service;
    
    import com.kikop.myspringstudy.mycommon.mapper.UsersMapper;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    import java.util.Map;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: UsersService
    
     * @desc 功能描述
    
     * @date 2020/6/22
    
     * @time 9:16
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    @Service // appconfig 就可以扫描到
    
    public class UsersService {
    
        /**
    
         * 这里注入是关键
    
         */
    
        @Autowired
    
        UsersMapper usersMapper;
    
        public List<Map<String, Object>> list() {
    
            return  usersMapper.list();
    
        }
    
    }
    
    

    2.3 factoryBean

    
    package com.kikop.myspringstudy.mybatislinkspring5.factorybean;//package com.kikop.mybatisaspring.factorybean;
    
    import com.kikop.myspringstudy.mycommon.sqlsession.MySqlSession;
    
    import org.springframework.beans.factory.FactoryBean;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: MyFactoryBean
    
     * @desc 功能描述 2个Bean
    
     * @date 2020/6/23
    
     * @time 23:01
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    //注意:
    
    //1.属性注入时,不能加 @Component,@Service 主要是无法实例化 mapperInterface属性。
    
    //2.无法自定义一个bean名称
    
    // @Component
    
    public class MySetterEnableFactoryBean implements FactoryBean {
    
        private Class mapperInterface;
    
        /**
    
         * setter 属性注入
    
         *
    
         * @param mapperInterface
    
         */
    
        public void setMapperInterface(Class mapperInterface) {
    
            this.mapperInterface = mapperInterface;
    
        }
    
        @Override
    
        public Object getObject() throws Exception {
    
            Object usersMapper = MySqlSession.getMapper(mapperInterface);
    
            return usersMapper;
    
        }
    
        @Override
    
        public Class<?> getObjectType() {
    
            return mapperInterface;
    
        }
    
    }
    
    

    2.4 annotation

    
    package com.kikop.myspringstudy.mybatislinkspring8.annotations;
    
    import com.kikop.myspringstudy.mybatislinkspring8.beandef.MyImportBeanDefinitionRegistrar;
    
    import org.springframework.context.annotation.Import;
    
    import java.lang.annotation.Documented;
    
    import java.lang.annotation.Retention;
    
    import java.lang.annotation.RetentionPolicy;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: MyMapperScan
    
     * @desc 功能描述
    
     * @date 2020/6/25
    
     * @time 17:47
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    @Retention(RetentionPolicy.RUNTIME)
    
    @Documented
    
    @Import({MyImportBeanDefinitionRegistrar.class})
    
    public @interface MyMapperScan2 {
    
        String value() ;
    
        int type() default 0;
    
    }
    
    

    2.5 beandef

    
    package com.kikop.myspringstudy.mybatislinkspring8.beandef;
    
    import com.kikop.myspringstudy.mybatislinkspring8.factorybean.MySetterEnableFactoryBean;
    
    import org.springframework.beans.factory.support.AbstractBeanDefinition;
    
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    
    import org.springframework.core.type.AnnotationMetadata;
    
    import org.springframework.core.type.StandardAnnotationMetadata;
    
    import java.io.File;
    
    import java.io.IOException;
    
    import java.net.URL;
    
    import java.util.*;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: MyImportBeanDefinitionRegistrar
    
     * @desc 功能描述
    
     * @date 2020/6/24
    
     * @time 17:17
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
        // 1.获取所有的类名,such as :com.kikop.myspringstudy.mycommon.mapper.UsersMapper
    
        private List<String> clsNames = new ArrayList<String>();
    
        /**
    
         * Get a list of {@link URL}s from the context classloader for all the resources found at the
    
         * specified path.
    
         *
    
         * @param path The resource path.
    
         * @return A list of {@link URL}s, as returned by {@link ClassLoader#getResources(String)}.
    
         * @throws IOException If I/O errors occur
    
         */
    
        private List<URL> getResources(String path) throws IOException {
    
            return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
    
        }
    
        /**
    
         * 扫描包路径,提取相关的类
    
         * .class > clsNames
    
         * 思路:
    
         * com.kikop.myspringstudy.mycommon.mapper 转成文件目录
    
         * 并遍历放入到list中
    
         * XMLConfigBuilder.java
    
         * configuration.addMappers(mapperPackage);
    
         *
    
         * @param scanPackages com.kikop.myspringstudy.mycommon.mapper
    
         */
    
        private void doScanner(String scanPackages) throws IOException {
    
            //: com.kikop.myspringstudy.mycommon.mapper--> com/kikop/myspringstudy/mycommon/mapper
    
            String slatingBarPackages = scanPackages.replaceAll("\\.", "/");
    
            List<URL> currentMapperDirectory = getResources(slatingBarPackages);
    
            if (currentMapperDirectory == null || currentMapperDirectory.size() == 0) {
    
                return;
    
            }
    
    //        URL webUrl = this.getClass().getClassLoader().getResource("/" + scanPackages.replaceAll("\\.", "/"));
    
            // file:/E:/MyWorkspace/javawebinaction/target/classes/com/kikop/myspringstudy/mycommon/mapper
    
            URL url = currentMapperDirectory.get(0);
    
            File classPathDir = new File(url.getFile());
    
            for (File file : classPathDir.listFiles()) {
    
                if (file.isDirectory()) {
    
                    doScanner(scanPackages + "." + file.getName());
    
                } else {
    
                    if (!file.getName().endsWith(".class")) { // 只扫描:.class,排除可能的.xml
    
                        continue;
    
                    }
    
                    String clazzName = (scanPackages + "." + file.getName().replace(".class", ""));
    
                    clsNames.add(clazzName);
    
                }
    
            }
    
        }
    
        /**
    
         * 首字母小写
    
         *
    
         * @param simpleName
    
         * @return
    
         */
    
        private String toLowerFirstCase(String simpleName) {
    
            char[] chars = simpleName.toCharArray();
    
            chars[0] += 32; // 首字母 ascii加32  A 65;a:97
    
            return String.valueOf(chars);
    
        }
    
        @Override
    
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
            if (importingClassMetadata instanceof StandardAnnotationMetadata) {
    
                Set<String> annotationTypes = importingClassMetadata.getAnnotationTypes();
    
                for (String annotation : annotationTypes) {
    
                    //  获取 appconfig 中 自定义注解 MyMapperScan2的 value 值(com.kikop.myspringstudy.mycommon.mapper)
    
                    if (annotation.equalsIgnoreCase("com.kikop.myspringstudy.mybatislinkspring8.annotations.MyMapperScan2")) {
    
                        Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(annotation);
    
    //                    for (Map.Entry<String, Object> entries : annotationAttributes.entrySet()) {
    
    //                        System.out.println(String.format("[key]:%s,[value]:%s", entries.getKey(), entries.getValue()));
    
    //                    }
    
                        if (annotationAttributes.containsKey("value")) {  // 扫描 所有 com.kikop.myspringstudy.mycommon.mapper
    
                            Object scanPackageName = annotationAttributes.get("value");
    
                            try {
    
                                doScanner((String) scanPackageName);
    
                                if (clsNames.isEmpty()) {
    
                                    return;
    
                                }
    
                                for (String strClsName : clsNames) { // 根据包名 annotationValue(com.kikop.myspringstudy.mycommon.mapper):循环遍历里的所有 classPath中 Mapper接口
    
                                    // 1.根据 MySetterEnableFactoryBean构建一个 BeanDefinitionBuilder,与 Bean的关系一对一
    
                                    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MySetterEnableFactoryBean.class);
    
                                    // 2.获取 beanDefinition
    
                                    // 该类 MySetterEnableFactoryBean 描述了 bean的所有信息
    
                                    AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    
                                    // 3.给自定义的bean MySetterEnableFactoryBean
    
                                    // 设定自定义Property 参数mapperInterface并放到 ArrayList
    
                                    // 3.1.设置属性 mapperInterface
    
                                    // strClsName = "com.kikop.myspringstudy.mycommon.mapper.UsersMapper";
    
                                    beanDefinition.getPropertyValues().add("mapperInterface", strClsName);
    
                                    // 3.2.设置 beanName
    
                                    // String strBeanName = "usersMapper"; // 默认驼峰表示, 首字母小写
    
                                    String strBeanName = strClsName.substring(strClsName.lastIndexOf(".") + 1);
    
                                    strBeanName = toLowerFirstCase(strBeanName);
    
                                    // 3.3.将构建好的 beanDefinition 放到 beanDefinitionMap中
    
                                    registry.registerBeanDefinition(strBeanName, beanDefinition);
    
                                    System.out.println(String.format("[strClsName]:%s,[strBeanName]:%s", strClsName, strBeanName));
    
                                }
    
                            } catch (IOException e) {
    
                                e.printStackTrace();
    
                            }
    
                        }
    
                    }
    
                }
    
            }
    
        }
    
    }
    
    

    2.6 test

    
    package com.kikop.myspringstudy.mybatislinkspring8.test;
    
    import com.kikop.myspringstudy.mybatislinkspring8.config.AppConfig;
    
    import com.kikop.myspringstudy.mybatislinkspring8.factorybean.MySetterEnableFactoryBean;
    
    import com.kikop.myspringstudy.mybatislinkspring8.service.UsersService;
    
    import com.kikop.myspringstudy.mycommon.mapper.UsersMapper;
    
    import com.kikop.myspringstudy.mycommon.mapperbak.CountryMapper;
    
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: UsersServiceTest
    
     * @desc 功能描述
    
     * @date 2020/6/22
    
     * @time 9:17
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    public class BeanToSpringByFactoryBean2Test {
    
        /**
    
         * 模拟mybatis进行代理Mapper的创建
    
         */
    
        public static void createProxyMapperClsTest1() {
    
            // 1.初始化spring容器(通过AppConfig进行 依赖注入、对象创建、service\bean\controller注解的扫描
    
            // 类似web.xml中配置的ContextLoaderListener
    
            // 已经完成spring初始化(注意时机)
    
            AnnotationConfigApplicationContext annotationConfigWebApplicationContext =
    
                    new AnnotationConfigApplicationContext(AppConfig.class);
    
            System.out.println(annotationConfigWebApplicationContext.getBean(MySetterEnableFactoryBean.class));
    
    //        // 包装类(Srping本身)
    
            System.out.println(annotationConfigWebApplicationContext.getBean("&usersMapper"));
    
    //        // 代理类(自定义,执行 TosTRING 方法)
    
            System.out.println(annotationConfigWebApplicationContext.getBean("usersMapper"));
    
            // call service
    
            System.out.println("--------------by bean----------------");
    
            System.out.println(annotationConfigWebApplicationContext.getBean(UsersMapper.class).list());
    
            System.out.println("--------------by service,注意 appconfig包扫描要修改----------------");
    
            System.out.println(annotationConfigWebApplicationContext.getBean(UsersService.class).list());
    
        }
    
        /**
    
         * 模拟mybatis进行代理Mapper的创建
    
         */
    
        public static void createProxyMapperClsTest2() {
    
            // 1.初始化spring容器(通过AppConfig进行 依赖注入、对象创建、service\bean\controller注解的扫描
    
            // 类似web.xml中配置的ContextLoaderListener
    
            // 已经完成spring初始化(注意时机)
    
            AnnotationConfigApplicationContext annotationConfigWebApplicationContext =
    
                    new AnnotationConfigApplicationContext(AppConfig.class);
    
            // System.out.println(annotationConfigWebApplicationContext.getBean(MySetterEnableFactoryBean.class));
    
    //        // 包装类(Srping本身)
    
            System.out.println(annotationConfigWebApplicationContext.getBean("&countryMapper"));
    
    //        // 代理类(自定义,执行 TosTRING 方法)
    
            System.out.println(annotationConfigWebApplicationContext.getBean("countryMapper"));
    
            // call service
    
            System.out.println("--------------by bean----------------");
    
            System.out.println(annotationConfigWebApplicationContext.getBean(CountryMapper.class).list());
    
        }
    
        public static void main(String[] args) {
    
            createProxyMapperClsTest2();
    
        }
    
    }
    
    

    3框架说明

    3.1 项目依赖

    
    Spring core:
    
    Spring-context、spring-webmvc
    
    Spring自带连接池:
    
    Spring-JDBC(对标:c3p0 druid(德鲁伊)
    
    Mybatis core :
    
    Mybatis(v3.5.0)
    
    Spring-mybatis 插件包
    
    Mybatis-spring(v2.0.0)
    
    mysql-connector-java v6.0.6驱动
    
    

    [图片上传失败...(image-25935f-1593170712556)]

    图 1 公共部分

    3.2 Users

    
    package com.kikop.myspringstudy.mycommon.model;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: mybatis
    
     * @file Name: User
    
     * @desc 功能描述
    
     * @date 2020/6/21
    
     * @time 16:41
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    public class Users {
    
      public int getId() {
    
        return id;
    
      }
    
      public void setId(int id) {
    
        this.id = id;
    
      }
    
      public String getName() {
    
        return name;
    
      }
    
      public void setName(String name) {
    
        this.name = name;
    
      }
    
      public int getAge() {
    
        return age;
    
      }
    
      public void setAge(int age) {
    
        this.age = age;
    
      }
    
      public String getRemark() {
    
        return remark;
    
      }
    
      public void setRemark(String remark) {
    
        this.remark = remark;
    
      }
    
      private int id;
    
      private String name;
    
      private int age;
    
      private String remark;
    
    }
    
    

    3.3 UsersMapper

    
    package com.kikop.myspringstudy.mycommon.mapper;
    
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    import java.util.Map;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: mybatis
    
     * @file Name: UserMapper(IUserDao)
    
     * @desc 功能描述
    
     * @date 2020/6/21
    
     * @time 16:40
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    public interface UsersMapper {
    
        @Select("select * from users")
    
        public List<Map<String, Object>> list();
    
    }
    
    

    3.4 MyInvocationHandler

    
    package com.kikop.myspringstudy.mycommon.handler;
    
    import org.apache.ibatis.annotations.Select;
    
    import java.lang.reflect.InvocationHandler;
    
    import java.lang.reflect.Method;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: MyInvocationHandler
    
     * @desc 功能描述
    
     * @date 2020/6/22
    
     * @time 10:03
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    public class MyInvocationHandler implements InvocationHandler {
    
        @Override
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            if (Object.class.equals(method.getDeclaringClass())) {
    
                return method.invoke(this, args);
    
            } else {
    
                Select selectAnnotation = method.getAnnotation(Select.class);
    
                if (selectAnnotation != null) {
    
                    String strSql = selectAnnotation.value()[0];
    
                    // 模拟执行 jdbc 数据查询,并返回数据
    
                    System.out.println(strSql);
    
                    if (method.getName().equals("toString")) {
    
                        // proxy.getClass()--> userMapper,返回被代理类的名称
    
                        return proxy.getClass().getInterfaces()[0].getName();
    
                    }
    
                }
    
                return null;
    
            }
    
        }
    
    }
    
    

    3.5 MySqlSession

    
    package com.kikop.myspringstudy.mycommon.sqlsession;
    
    import com.kikop.myspringstudy.mycommon.handler.MyInvocationHandler;
    
    import java.lang.reflect.Proxy;
    
    /**
    
     * @author kikop
    
     * @version 1.0
    
     * @project Name: javawebinaction
    
     * @file Name: MySqlSession
    
     * @desc 功能描述 模拟Mapper接口代理类的创建
    
     * @date 2020/6/22
    
     * @time 9:57
    
     * @by IDE: IntelliJ IDEA
    
     */
    
    public class MySqlSession {
    
        /**
    
         * 模拟Mapper接口代理类的创建
    
         *
    
         * @return
    
         */
    
        public static Object getMapper(Class<?> clazz) {
    
            // 第1个参数:AppClassLoader
    
            // 第2个参数:数组,因为java单继承,多实现,所以要定义为数组
    
            // 第3个参数:invaocationHandler
    
            Class<?> classArray[]=new Class<?>[]{clazz};
    
            Object proxy = Proxy.newProxyInstance(MySqlSession.class.getClassLoader(),classArray,
    
                    new MyInvocationHandler());
    
            return proxy;
    
        }
    
    }
    
    

    参考

    相关文章

      网友评论

          本文标题:2020-06-25_Mybatis和Spring整合高级系列知

          本文链接:https://www.haomeiwen.com/subject/leiufktx.html