Spring原理篇(12)--架构整合流程;该篇章讲解Spring在整合架构的时候的思路;

Posted 喜欢编码的老胡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring原理篇(12)--架构整合流程;该篇章讲解Spring在整合架构的时候的思路;相关的知识,希望对你有一定的参考价值。

@TOC# Spring系列
记录在程序走的每一步___auth:huf


该篇章讲解架构整合的思路; 讲解该篇章主要是为了将之前的知识点进行一个串联; 有很多接口 我仅仅是介绍了它们的使用;并没有实战场景;所以有很多同学想知道 这些接口 到底在什么场景下使用; 怎么使用; 能达到什么样的效果;例如我们整合Mybatis的时候,怎么去整合的? 如果有疑问的同学;可私信我; 回复不一定及时;请各位同学谅解;
我们学习了Spring 那么多功能 主要围绕是围绕 Bean 做一些事情; 创建Bean 初始化Bean 运行Bean 销毁Bean. 我们Java程序 主要围绕着 对象 构成一个一个的模块 最后组合成庞大的系统; 做一些计算机的处理. 我们架构整合的时候 主要是整合 对象 , 对象的创建; 对象集合的管理; 如果将一个不相关的框架 跟Spring整合; 主要整合其内部的功能集; 也就是对象; 例如 我们要整合Mybatis; 我们应该如何进行整合? 也许 在别的 大神文章里面 我们也能看到 别人是 怎么整合的 复制粘贴之后 我们也能行; 但是这样能给我们技术带来实际上的提升吗? 这篇文章 我就带大家一起手写一个超简易版本 mybatis-spring.jar

1:我们Mybatis 使用流程 大致如下

		创建一个流 把配置文件传入
       InputStream is = Resources.getResourceAsStream("MybatisConfig.xml");
       创建SqlSessionBeanFactory 
       SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
       利用sqlSessionBeanFactory 创建SqlSession
       SqlSession sqlSession = sqlSessionFactory.openSession();
   	   通过SqlSession 拿到相对应的Mapper
       StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
       最后执行我们的sql语句;
       List list = mapper.selectAll();

2 :我们大致准备一个架子结构;

简简单单的 一个扫描的Config 一个main启动 类 一个Service 一个Mapper

注意这个Mapper 是一个接口


这就是我们平时使用Mapper的时候 最常见的方式; 直接注入Mapper 然后直接调用即可;


# 我们的Mapper 是一个接口 此时 在我们Service 注入了这个接口; 并且进行sql执行; 在Mybatis中 它会使用代理 进行接口实现; 并且最终把该对象的实现类 注入到这里来; 我们现在这里并没做这个类的实现; 现在执行肯定是报错的; 我们该如何把这个接口变成Bean呢? 答:使用FactoryBean 进行对象的创建. 我们知道 Spring内部使用了Cglib代理 所以不能代理接口;

1:我们利用FactoryBean 间接的创建Bean:

/**
 * auth :huf
 */
@Component
public class HufFatoryBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        Object object = Proxy.newProxyInstance(HufFatoryBean.class.getClassLoader(), new Class[]{StudentMapper.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName());
                return null;
            }
        });
        return object;
    }
    @Override
    public Class<?> getObjectType() {
        return StudentMapper.class;
    }
}

这样我们就可以通过FactoryBean 再利用 JDK 代理; 把接口变成对象 并且注入到容器中;

为什么是null 因为我Factory返回出来就是Null;


我们继续扩展; 假设我们有多个Mapper 一个StudentMapper 一个 TeacherMapper … 多个Mapper.那怎么办?

答: 我们把FactoryBean 变成 BeanDefinition 就可以了; 以下是演进展示;

我们先把HufFatoryBean 写活; 通过构造方法传入即将要代理的类Class. 这样 我们就可以只用一个FactoryBean 进行全部Mapper的代理.

/**
 * auth :huf
 */
@Component
public class HufFatoryBean implements FactoryBean {
    private Class clazz;
    public HufFatoryBean(Class clazz) {
        this.clazz = clazz;
    }
    @Override
    public Object getObject() throws Exception {
        Object object = Proxy.newProxyInstance(HufFatoryBean.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName());
                return null;
            }
        });
        return object;
    }
    @Override
    public Class<?> getObjectType() {
        return clazz;
    }
}

然后 我们注册BeanDefinition

第一种方式


/**
 * Application启动类;
 * auth : huf
 */
public class ApplictionMain {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ap  = new AnnotationConfigApplicationContext();
        ap.register(AppConfig.class);
        -------------
		创建一个beanDefinition 
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        传入我们的FactoryBean 
        beanDefinition.setBeanClass(HufFatoryBean.class);
        通过构造方法 把Student传入 
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(StudentMapper.class);
        注册BeanDefinition 把创建好的 BeanDefinition 传入进去; 
        ap.registerBeanDefinition("studentMapper",beanDefinition);
-------------------------以下也是一样
        AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition1.setBeanClass(HufFatoryBean.class);
        beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(TeacherMapper.class);
        ap.registerBeanDefinition("teacherMapper",beanDefinition1);
		启动
        ap.refresh();
        
        StudentService studentService = (StudentService) ap.getBean("studentService");
        studentService.test();
    }
}

这里 同学们应该都理解了吧? 到这里 其实思路就已经出来了; 我们不可能以这种方式去加载BeanDefinition; 以下 我介绍2种加载BeanDefinition的方式; 其实以前说过 再之前的篇章里边; 这里我写一次;


第二种BeanDefinitionRegistryPostProcessor

/**
 * auth : huf
 */
public class HufBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		创建一个beanDefinition 
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        传入我们的FactoryBean 
        beanDefinition.setBeanClass(HufFatoryBean.class);
        通过构造方法 把Student传入 
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(StudentMapper.class);
        注册BeanDefinition 把创建好的 BeanDefinition 传入进去; 
        ap.registerBeanDefinition("studentMapper",beanDefinition);
-------------------------以下也是一样
        AbstractBeanDefinition beanDefinition1 = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
        beanDefinition1.setBeanClass(HufFatoryBean.class);
        beanDefinition1.getConstructorArgumentValues().addGenericArgumentValue(TeacherMapper.class);
        ap.registerBeanDefinition("teacherMapper",beanDefinition1);
    }
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

这样是否是合理很多? 我们场景再继续往下深入一下; 如果我们之后想加一个Mapper 我们还得再这里写多一个BeanDefinition 是不是又突然觉得不太合理了?

答: 我们可以利用 Scan 扫描的方式 进行Mapper扫描. 凡是再某个包下面的所有接口 我都认为它是Mapper 是否OK? 我们定义一个注解;


现在我们注入了扫描 我们会发现 之前用的BeanDefinitionRegistryPostProcessor 无法拿到我们的配置;我们就引出了另外一个

第三种ImportBeanDefinitionRegistrar


这样 我们在配置文件中 可以通过@Inport(HufImportBeanDefinitionRegistrar.class) Spring捕捉到配置文件中的Import后 就可以调度这个方法 把BeanDefinition 加入容器中去; 这样我们继续改造我们的HufImportBeanDefinitionRegistrar 让他通过扫描的方式得到所有MapperClass 然后通过工厂的方式 生产BeanDefinition 注册到容器中; 以下是改造后的代码:


/**
 *
 * auth : huf
 */
public class HufImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes =  importingClassMetadata.getAnnotationAttributes(HufMapperScan.class.getName());
        String path = MapUtils.getString(annotationAttributes,"value");
        //Spring 原生扫描的方式 因为我们使用的是接口 所以我们必须继承这个类 并且进行改造
//        ClassPathBeanDefinitionScanner scan = new ClassPathBeanDefinitionScanner(registry);
//        scan.scan(path);
		创建自己的MapperBeanDefinitionScanner 
        HufMapperBeanDefinitionScanner scanner = new HufMapperBeanDefinitionScanner(registry);
        该步骤是为了在扫描其中 如果有@Component 才会加入到容器 默认false  我改成true 就是 不管有没有 都是Bean
        scanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;
            }
        });
        scanner.scan(path);
    }
}

HufMapperBeanDefinitionScanner

中间的doScan方法重写 是为了什么? 我们通过path路径扫描到的 是Mapper本身 并不是Factory 现在我们变成Factory 所以要从写Scan 方法 这时候 我们得到的BeanDefinition 就是我们想要的了; 这样我们的扫描组件就写完了


以上就是我们的扫描组件; 但是我们留了一个问题; 我们的对象 Mapper对象 并没有实现我们 sql的执行; 没有访问数据库; 我们继续往下扩展:


我们只需要把之前我们流程所说的Mybatis Sqlsession.getMapper 对象 返回出去; 即可;
改造后代码为:

这样就彻底的整合完毕;实际上 mybatis-spring 就是为我们做了这么一件事; 真正的代码 核心就是这样 但是因为更丰富的功能实现 以及兼容 . 实际代码肯定是比这里更复杂;

总结:

1: Import 注解使用场景
2: FactoryBean 使用场景
3: BeanDefinitionRegistryPostProcessor使用场景;
4:ImportBeanDefinitionRegistrar使用场景
5:ClassPathBeanDefinitionScanner使用场景
6: Beandefinition 的使用场景;

See You

以上是关于Spring原理篇(12)--架构整合流程;该篇章讲解Spring在整合架构的时候的思路;的主要内容,如果未能解决你的问题,请参考以下文章

整合spring cloud云架构 - SSO单点登录之OAuth2.0 登出流程

整合spring cloud云架构 - SSO单点登录之OAuth2.0登录流程

整合spring cloud云架构 - SSO单点登录之OAuth2.0登录流程

MyBatis与Spring整合

(十三) 整合spring cloud云架构 - SSO单点登录之OAuth2.0 根据token获

整合spring cloud云架构-SSO单点登录之OAuth2.0根据token获取用户信息