zoukankan      html  css  js  c++  java
  • 一个C#开发编写Java框架的心路历程

    前言

    这一篇絮絮叨叨,逻辑不太清晰的编写Java框架的的一个过程,主要描述我作为一个java初学者,在编写Java框架时的一些心得感悟。

    因为我是C#的开发者,所以,在编写Java框架时,或多或少会带入一些C#的固有观念,所以,这也是一个C#观念与Java观念碰撞的一个框架。

    Java与C#的一些小区别

    命名空间:在C#中命名空间+类名是类,在Java中命名空间+类名是命名空间,即,Java中会出现Import某一个类的完全限定名。

    反射:在C#中反射可以只用类名反射,Java中必须是完全限定名;在C#中反射是在内存或DLL类库中查找文件,一个方法就搞定了,在Java中则需要手写扫描文件夹或扫描Jar包的文件,然后找到名称一样的文件再反射。

    for循环:在C#中有for循环和foreach循环,在Java中for循环支持foreach模式,如:

    for(Kiba_User u : ul)

    Java之Spring脉络简介

    对于C#开发而言,Java开发的脉络实在是清奇的不得了,因为Java使用了大量的依赖注入和控制反转,从而让它的结构非常的反人类。但这也是有一定的历史原因的,因为它的开源语言,所以,大家在扩展框架时,都等于在做二次开发,因为依赖注入和控制反转是二次开发最好的模式,所以,它就越积累越多,最后它彻底的变成了控制反转的完全体,也就说,它在反人类的路上一去不反复了(注意,Java开发者通常认为他们才是正常开发,为了避免冲突,请不要当面说他们反人类)。

    下面我使用C#的描述的方式来勾勒一下Java之Spring的脉络,如下图:

    因为,java很多对象都是用注解标识,然后在解析时实例化的,为了统一代码,所以,java形成了一种新的标准,实例化对象都用注解。

    准备工作

    本框架因为是学习框架,所以有些设计会常规的java不同,框架中不会使用类似@Service这样的注解,但会使用@Data,因为Java中写属性确实有点费劲。

    下面我们进行准备工作。

    开发工具:IDEA。

    项目框架:Spring。

    JDK:1.8。

    ORM:Mybatis。

    首先我们创建一个Spring的Web项目——k_framework,C#开发可以参考:一个C#开发者重温Java的心路历程。(这里只做WebApi的介绍)

    然后我们编辑Pom.xml引入所需的Jar包,依赖如下:

     <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>com.oracle</groupId>
                <artifactId>ojdbc8</artifactId>
                <version>12.1.0.2.0</version>
            </dependency>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.2</version>
            </dependency> 
      </dependencies>

    PS:这里使用的数据库是Oracle,然后我们的包管理工具Maven居然不能下载oracle的jar包。。。所以我们只能去官网下载,然后在CMD里,使用Maven提供的命令安装这个jar包。

    然后结合Java的Spring框架的特质,设计一个项目结构,并在包k_framework下面实现。

    项目结构设计如下:

    系统约定如下:

    DTO类名后缀需为Command和Query,标记命令用于处理的业务为增删改、或查询。

    DTO类必须在同一包下,且类名不得重复。

    前台页面必须定义一个同名的,属性一致的Javascript的DTO类。

    业务域类名=DTO的类名+Handler。

    业务域类使用Excute函数处理业务。

    关于结构

    关于配置类与工具类:设计时,我们尽量让控制器使用配置类,让业务域使用工具类。当然,特殊情况下也可以一起使用。

    关于业务域:Java中通常使用Service来命名处理业务的包,但因为有时候我们会把部署的Web项目也称为服务,比如微服务项目里每个WebApi都是服务,所以,这里为了避免歧义,使用域来命名处理业务的包。

    关于数据库映射:在C#项目里,我们是先建立映射,然后用仓储通过泛型来处理数据库数据,但在Mybatis里,需要使用映射的对象来处理数据库数据,即,每处理一个表,就要建立一个这个表的映射对象实例。

    关于数据库实体和数据库扩展实体:顾名思义,数据库扩展实体是数据库实体的扩展,可以的简单把它理解为视图实体。

    注:在C#中,图中的这些大类的结构,通常会搞一个类库项目来单独处理,因为在C#中共享使用一个启动项目的配置文件,并且C#的项目文件在VS中管理起来非常简单便捷,但Java的项目文件pom.xml并不是特别灵活,所以,这里我们就在一个项目里做结构。

    整体结构图如下:

    代码实现——逻辑

    工具类

    首先,我们先建立工具类。

    因为是简单实现,所以我们只建立三个最基础的工具类,ReflexHelper、StringHelper、FileHelper。(在java中通常工具类命名会以util结尾,这里我保持c#的命名风格)

    控制器

    定义CommandController类,Get和Post两个函数,用于处理全部的Get和Post请求。函数接受两个参数,命令类型和命令的Json内容,然后通过命令类型发射调用业务域。

    代码如下:

    @RestController
    @RequestMapping("/Command")
    public class CommandController
    { 
        @Autowired
        private SqlSession sqlSession; 
        @RequestMapping(value = "/Get", method = RequestMethod.GET)
        @ResponseBody
        public BaseResult Get(String commandName,String commandJson) throws Exception {  
            Set<Class<?>> classes = ReflexHelper.getClasses("com.kiba.k_framework.dto");
            String newName ="";
            for(Class c:classes){
                String fullName = c.getName();
                System.out.println(fullName);
                String className = fullName.substring(fullName.lastIndexOf(".")+1,fullName.length());
                System.out.println(className);
                if(className.equals(commandName)) {
                    newName = fullName.replace(".dto.", ".domain.") + "Handler";
                    System.out.println(newName);
                    break;
                }
            }
    ​
            Class<?> clazz = Class.forName(newName);
            Method method = clazz.getMethod("Excute", String.class,SqlSession.class);
            BaseResult ret = (BaseResult)method.invoke(clazz.newInstance(), commandJson,sqlSession);
            return ret;
        }
        @PostMapping(value = "/Post")
        @ResponseBody
        public BaseResult Post(String commandName,String commandJson) throws Exception {
            System.out.println(commandName);
            Set<Class<?>> classes = ReflexHelper.getClasses("com.kiba.k_framework.dto");
            String newName ="";
            for(Class c:classes){
                String fullName = c.getName();
                System.out.println(fullName);
                String className = fullName.substring(fullName.lastIndexOf(".")+1,fullName.length());
                System.out.println(className);
                if(className.equals(commandName)) {
                    newName = fullName.replace(".dto.", ".domain.") + "Handler";
                    System.out.println(newName);
                    break;
                }
            } 
            Class<?> clazz = Class.forName(newName);
            Method method = clazz.getMethod("Excute", String.class,SqlSession.class);
            BaseResult ret = (BaseResult) method.invoke(clazz.newInstance(), commandJson,sqlSession);
            return ret;
        }  
    }

    如上代码所示,Controller接受到的请求,会被直接发送到业务域处理,也就是说,理论上,这里写完了,就再也不用关注了。

    注1:代码一开始使用注解@Autowired实例化了sqlSession,这个对象是mybatis的内部对象,后面会把它发送到业务域,业务域里通过它获取mapper对象,这是因为,我们的业务域是反射调用的,所以在业务里@Autowired注解将失效,它将无法对继承BaseMapper的接口进行实例化。

    注2:使用这种结构,我们的AOP除了可以使用@Aspect注解,还可以直接写在Controller里了。

    注3:并不是所有项目和团队组成都适用这个的框架。

    代码实现——数据库

    在本框架中,数据库链接使用Mybatis开源包。

    Mybatis学习

    在使用mybatis之前需要先学习一些知识,搞懂mybatis的一些类库的关系,不然用起来会很迷茫。

    mybatis:一个java的orm包。

    mybatis-spring-boot-starter:一个mybatis工作组为了spring单独开发的包,他让spring框架使用mybatis更简单,springBoot,springCloud等框架都可以用(映射使用注解@mapper,最新版 2.1.2)。

    mybatis-plus:一个基于mybatis的扩展包,拥有一些在mapper创建后,会自带一些基础的增删改查的方法。

    mybatis-plus-boot-starter:mybatis-plus工作组为了spring单独开发的包,,他让spring框架使用mybatis-plus更简单,springBoot,springCloud等框架都可以用(映射使用继承BaseMapper,最新版3.42,mybatisplus-springboot-starter是mybatis-plus-boot-starter的增强包)。

    了解了以上概念后,我们可得知,在springboot项目中使用mybatis,我们有两个选择,即使用mybatis-spring-boot-starter或mybatis-plus-boot-starter。

    因为我是C#出身,所以,映射我更倾向于继承,所以下面代码使用的是mybatis-plus-boot-starter。

    Mybatis配置

    在resources/application.yml下输入配置代码如下:

    server:
      port: 8088
    spring:
      servlet:
        multipart:
          max-file-size: 5000MB
          max-request-size: 5000MB
      datasource:
        driver-class-name: oracle.jdbc.OracleDriver
        url: jdbc:oracle:thin:@192.168.1.1:1521/orcl
        username: abc
        password: 123
    # mybatis
    mybatis:
      mapper-locations: classpath:mapper/**/*.xml

    代码中配置了Spring节点下的数据源,配置为Oracle并设置链接账户密码;还配置了mybatis节点下的映射路径。该映射路径下面会用到。

    然后配置启动类,增加注解@MapperScan("com.kiba.k_framework.mapper"),如下图:

    数据库实体

    接着我们建立数据库实体,属性跟数据库表字段一样即可。但Java里写属性太麻烦,所以这里使用了@Data注解,被注解的类下,只要写私有字段即可,编译时会为我们生成首字母大写的属性,并且编写代码时,还可以点出【getName()/setName()】这样的方法来获取或设置属性的值。代码如下:

    package com.kiba.k_framework.entity;
    import lombok.Data;
    @Data
    public class Kiba_User {
        private int id;
        private String name;
    }

    如果是第一次使用Idea,我们编写代码时,在对象的后面是点不出【getName()/setName()】这样的方法的,这是因为,我们没有安装lombok插件,安装插件在File—Setting中,如下图所示。

    映射类

    映射类,顾名思义,就是建立实体与数据库关系的类,在这里类中会指定实体类与数据库表的关系,和实体字段和表字段的关系(通常情况是同名映射)。不过在Java里,映射类除了要处理映射关系,还要担任数据库访问的角色,而C#的映射类就是处理映射关系,访问数据库则有数据库上下文实体负责,说实话,Java这种模式是有点奇怪,不过用久了也就无所谓了。

    映射类代码如下:

    public interface Kiba_UserMapper extends BaseMapper<Kiba_User> {
        @Select("select * from Kiba_User")
        List<Kiba_User> test();
        @Select("select * from Kiba_User where id=#{value}")
        List<Kiba_User> test2(Integer id);
    ​
        List<Kiba_User> test3(Integer id);
    }

    如上代码所示,映射类通过继承泛型BaseMapper<Kiba_User> 实现了数据库实体和表的映射。

    然后代码里定义了三个方法,都是查询数据库数据。

    第一个方法—test:在方法上加了@Select注解,并且在注解里编写sql语句,这样调用这个方法时,就会执行注解里的语句。

    第二个方法—test2:方法2多了一个入参,注解里多了一个查询条件, 注解里通过#{value}的方式使用了入参的值。看到这,我们可以发现,注解里有自己的方言,即注解里还有一套自己的语法,这显然明目张胆的增加了开发者的学习内容,我表示反对,但无效。

    第二个方法—test3:这个方法没有注解,但有对应的XML配置文件,什么是XML配置文件?

    如下图所示,里面有两个同名,但后缀名不同的文件,下方的Kiba_UserMapper.xml文件就是,Kiba_UserMapper.java的xml配置文件,这两个文件编译的时候会被捏成一个类。系统根据什么把他们捏一起的呢?还记得我们上面的配置吗?我们配置了一个映射扫描包和一个映射配置路径,系统就是根据它俩的扫描文件结果,然后把同名的捏到一起的。

    现在我们看一下Kiba_UserMapper.xml的内容。

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    ​
    <mapper namespace="com.kiba.k_framework.mapper.Kiba_UserMapper"> 
        <select id="test3" resultType="com.kiba.k_framework.entity.Kiba_User" parameterType="Integer">
            select * from Kiba_User where id=#{value}
        </select>  
    </mapper>

    如代码所示,在mapper标签里配置了命名空间——com.kiba.k_framework.mapper.Kiba_UserMapper,即它明确指定了这个XML要和谁捏在一起。(java里命名空间+类名还是命名空间)

    然后在mapper标签里配置了一个select标签,【id="test3"】标记了它对应的函数名,resultType和parameterType标签标识这他们对应的这个函数传入传出类型,然后内容是一个带方言的sql语句。

    看到这里,我们可以得出,这个xml的select标签是等于@select注解的,即为函数设置sql语句有两种方式,一种是注解一种是xml文件配置,因为上面的映射类中的前两个方法已经有注解了,所以,xml配置文件中并没有重复配置。

    这个模式嘛,是有点,呃。。。是比较特别。

    业务域

    现在我们在业务域里使用一下映射类来获取数据。

    代码如下:

    public class GetUserQueryHandler implements IHandler
    {
        @Override
        public GetUserQueryReuslt Excute(String commandJson, SqlSession sqlSession)     {
            Kiba_UserMapper mapper = sqlSession.getMapper(Kiba_UserMapper.class);
            List<Kiba_User> users = mapper.test();
            GetUserQueryReuslt ret=new GetUserQueryReuslt();
            ret.setUsers(users);
            ret.setSuccess(true);
            return ret;
        }
    }

    这里使用sqlSession.getMapper(Kiba_UserMapper.class)来获取我们的maper实例,然后下面就可以正常调用他下面的方法了。

    测试

    现在我们启动项目,用postman测试一下。

    输入http://localhost:8088/Command/Get?commandName=GetUserQuery&commandJson={}进行测试,得到结构如下图所示:

    测试成功,我们成功的通过发送DTO实体实现了业务查询。

    结语

    在使用Java的时候,我总感觉像回到了旧社会,错误提示、开发工具的使用、工程文件的管理等等都很不友好。Spring框架看上去很简洁,但因为这些不友好的朋友在中间阻碍着,整体的开发进度,并没有想象中那么快速。也许,我们都被微软宠坏了吧。

    ----------------------------------------------------------------------------------------------------

    到此,到此Java框架的开发就已经介绍完了。

    代码已经传到Github上了,欢迎大家下载。

    Github地址: https://github.com/kiba518/Kiba_Java_Framework

    ----------------------------------------------------------------------------------------------------

    注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
    若您觉得这篇文章还不错,请点击下方的推荐】,非常感谢!

    https://www.cnblogs.com/kiba/p/14518906.html

     

    https://www.cnblogs.com/kiba/
  • 相关阅读:
    ABAP接口用法
    监听textarea数值变化
    The first step in solving any problem is recognizing there is one.
    Wrinkles should merely indicate where smiles have been.
    God made relatives.Thank God we can choose our friends.
    Home is where your heart is
    ABAP跳转屏幕
    Python 工具包 werkzeug 初探
    atom通过remote ftp同步本地文件到远程主机的方法
    Mongodb学习笔记一
  • 原文地址:https://www.cnblogs.com/kiba/p/14518906.html
Copyright © 2011-2022 走看看