纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

mybatis代码生成及自定义注解注释 mybatis代码生成+自定义注解+自定义注释实例

ziyue7575   2021-10-09 我要评论
想了解mybatis代码生成+自定义注解+自定义注释实例的相关内容吗,ziyue7575在本文为您仔细讲解mybatis代码生成及自定义注解注释的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:mybatis代码生成,mybatis自定义注解,mybatis自定义注释,下面大家一起来学习吧。

mybatis代码生成

        <!--mybatis的包和反向生成的包__用来生成dao,entity层-->
        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.4</version>
        </dependency>
        <!-- mybatis-generator-core 反向生成java代码-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

配置文件

在resources文件夹中创建文件mbgConfiguration.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<!-- 第一种mybatis逆向生成xml配置 -->
<generatorConfiguration>
    <context id="sqlserverTables" targetRuntime="MyBatis3" defaultModelType="flat">
        <!-- 自动识别数据库关键字,默认false,如果设置为true,根据SqlReservedWords中定义的关键字列表;
            一般保留默认值,遇到数据库关键字(Java关键字),使用columnOverride覆盖
         -->
        <!--<property name="autoDelimitKeywords" value="false"/>-->
        <!-- 生成的Java文件的编码 -->
        <property name="javaFileEncoding" value="UTF-8"/>
        <!-- 格式化java代码 -->
        <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/>
        <!-- 格式化XML代码 -->
        <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/>
        <!-- beginningDelimiter和endingDelimiter:指明数据库的用于标记数据库对象名的符号,比如ORACLE就是双引号,MYSQL默认是`反引号; -->
        <property name="beginningDelimiter" value="`"/>
        <property name="endingDelimiter" value="`"/>
        <!-- 使用自定义的插件:entity的lombok注解 -->
        <plugin type="net.cc.gen.utils.LombokPlugin"/>
        <!--todo 文件名替换-->
        <!-- 此处是将Example改名为Criteria 当然 想改成什么都行~    -->
        <plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
            <property name="searchString" value="Example$" />
            <!-- 替换后 <property name="replaceString" value="Criteria" /> -->
            <property name="replaceString" value="Query" />
        </plugin>
        <!-- 此处是将UserMapper.xml改名为UserDao.xml 当然 想改成什么都行~ -->
        <!--<plugin type="org.mybatis.generator.plugins.rename.RenameSqlMapperPlugin">-->
            <!--<property name="searchString" value="Mapper" />-->
            <!--<property name="replaceString" value="Dao" />-->
        <!--</plugin>-->
        <!-- 此处是将UserMapper改名为UserDao 接口 当然 想改成什么都行~  -->
        <!--<plugin type="org.mybatis.generator.plugins.rename.RenameJavaMapperPlugin">-->
            <!--<property name="searchString" value="Mapper$" />-->
            <!--<property name="replaceString" value="Dao" />-->
        <!--</plugin>-->
        <!--  通过type指定自定义的注释 -->
        <commentGenerator type="net.cc.gen.utils.MyCommentGenerator"/>
        <!--todo 数据库链接URL、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/project1?useUnicode=true&amp;useSSL=false&amp;characterEncoding=utf8"
                        userId="test"
                        password="123456"/>
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!--todo entity包路径-->
        <javaModelGenerator targetPackage="com.cc.learn.entity" targetProject="src/main/java">
            <!-- 在targetPackage的基础上,根据数据库的schema再生成一层package,最终生成的类放在这个package下,默认为false -->
            <property name="enableSubPackages" value="true"/>
            <!-- 从数据库返回的值被清理前后的空格,对String类型字段调用trim()方法  -->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>
        <!--todo mapper的xml路径-->
        <sqlMapGenerator targetPackage="mappings" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>
        <!--todo mapper的java文件路径-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.cc.learn.dao.mysql.mapper"
                             targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
        <!--todo tableName需要生成的table名字
         useActualColumnNames 驼峰命名
         字段类型为text格式的字段需要专门配置
           <columnOverride column="AIRSPACE_DATA" javaType="java.lang.String" jdbcType="VARCHAR"/>
            <columnOverride column="AIRSPACES_DESC" javaType="java.lang.String" jdbcType="VARCHAR"/>
        -->
        <table tableName="User" schema="crmii" delimitIdentifiers="true"><property name="useActualColumnNames" value="false"/></table>
        <!--<table tableName="数据表2" schema="crmii" delimitIdentifiers="true"><property name="useActualColumnNames" value="false"/></table>-->
        <!--<table tableName="数据表3" schema="crmii" delimitIdentifiers="true"><property name="useActualColumnNames" value="false"/></table>-->
    </context>
</generatorConfiguration>

配置类

自定义的lombok注解配置

效果:

在类上添加注解

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class User {
    }

在时间字段上自动添加注解

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date endTime;

代码

import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import java.util.*;
/** 自定义的lombok注解配置
* @author jingshiyu
* @date 2019/9/17 12:04:47
* @desc
*/
public class LombokPlugin extends PluginAdapter {
    private final Collection<Annotations> annotations;
    /**
     * LombokPlugin constructor
     */
    public LombokPlugin() {
        annotations = new LinkedHashSet<>(Annotations.values().length);
    }
    /**
     * @param warnings list of warnings
     * @return always true
     */
    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
    /**
     * Intercepts base record class generation 获取表
     *
     * @param topLevelClass     the generated base record class
     * @param introspectedTable The class containing information about the table as
     *                          introspected from the database
     * @return always true
     */
    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        addAnnotations(topLevelClass);
        return true;
    }
    /**
     * Intercepts primary key class generation
     *
     * @param topLevelClass     the generated primary key class
     * @param introspectedTable The class containing information about the table as
     *                          introspected from the database
     * @return always true
     */
    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        addAnnotations(topLevelClass);
        return true;
    }
    /**
     * Intercepts "record with blob" class generation
     *
     * @param topLevelClass     the generated record with BLOBs class
     * @param introspectedTable The class containing information about the table as
     *                          introspected from the database
     * @return always true
     */
    @Override
    public boolean modelRecordWithBLOBsClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        addAnnotations(topLevelClass);
        return true;
    }
    /**
     * 设置get set方法(使用lombok不需要,直接返回false)
     */
    @Override
    public boolean modelGetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        return false;
    }
    /**
     * 设置set方法
     */
    @Override
    public boolean modelSetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        return false;
    }
    /**
     * 设置lombok注解 <br>
     */
    private void addAnnotations(TopLevelClass topLevelClass) {
        for (Annotations annotation : annotations) {
            topLevelClass.addImportedType(annotation.javaType);
            topLevelClass.addAnnotation(annotation.asAnnotation());
        }
    }
    /**
     * entity类设置
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        super.setProperties(properties);
        //@Data is default annotation
        annotations.add(Annotations.DATA);
        annotations.add(Annotations.ALL_ARGS_CONSTRUCTOR);
        annotations.add(Annotations.NO_ARGS_CONSTRUCTOR);
        annotations.add(Annotations.BUILDER);
        for (String annotationName : properties.stringPropertyNames()) {
            if (annotationName.contains(".")) {
                continue;
            }
            String value = properties.getProperty(annotationName);
            if (!Boolean.parseBoolean(value)) {
                // The annotation is disabled, skip it
                continue;
            }
            Annotations annotation = Annotations.getValueOf(annotationName);
            if (annotation == null) {
                continue;
            }
            String optionsPrefix = annotationName + ".";
            for (String propertyName : properties.stringPropertyNames()) {
                if (!propertyName.startsWith(optionsPrefix)) {
                    // A property not related to this annotation
                    continue;
                }
                String propertyValue = properties.getProperty(propertyName);
                annotation.appendOptions(propertyName, propertyValue);
                annotations.add(annotation);
                annotations.addAll(Annotations.getDependencies(annotation));
            }
        }
    }
    /**
     * mapper类设置注解
     */
    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Mapper"));
        interfaze.addAnnotation("@Mapper");
        return true;
    }
    /**
     * entity字段设置
     */
    @Override
    public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, Plugin.ModelClassType modelClassType) {
        if (field.getType().getShortNameWithoutTypeArguments().equals("Date")) {
            field.getAnnotations().add(Annotations.DATE_TIME_FORMAT.asAnnotation());
            field.getAnnotations().add(Annotations.JSON_FORMAT.asAnnotation());
            topLevelClass.addImportedType(Annotations.DATE_TIME_FORMAT.javaType);
            topLevelClass.addImportedType(Annotations.JSON_FORMAT.javaType);
        }
        return true;
    }
    private enum Annotations {
        DATA("data", "@Data", "lombok.Data"),
        BUILDER("builder", "@Builder", "lombok.Builder"),
        ALL_ARGS_CONSTRUCTOR("allArgsConstructor", "@AllArgsConstructor", "lombok.AllArgsConstructor"),
        NO_ARGS_CONSTRUCTOR("noArgsConstructor", "@NoArgsConstructor", "lombok.NoArgsConstructor"),
        ACCESSORS("accessors", "@Accessors", "lombok.experimental.Accessors"),
        TO_STRING("toString", "@ToString", "lombok.ToString"),
        DATE_TIME_FORMAT("dateTimeFormat", "@DateTimeFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")", "org.springframework.format.annotation.DateTimeFormat"),
        JSON_FORMAT("jsonFormat", "@JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")", "com.fasterxml.jackson.annotation.JsonFormat");
        private final String paramName;
        private final String name;
        private final FullyQualifiedJavaType javaType;
        private final List<String> options;
        Annotations(String paramName, String name, String className) {
            this.paramName = paramName;
            this.name = name;
            this.javaType = new FullyQualifiedJavaType(className);
            this.options = new ArrayList<String>();
        }
        private static Annotations getValueOf(String paramName) {
            for (Annotations annotation : Annotations.values()) {
                if (String.CASE_INSENSITIVE_ORDER.compare(paramName, annotation.paramName) == 0) {
                    return annotation;
                }
            }
            return null;
        }
        private static Collection<Annotations> getDependencies(Annotations annotation) {
            if (annotation == ALL_ARGS_CONSTRUCTOR) {
                return Collections.singleton(NO_ARGS_CONSTRUCTOR);
            } else {
                return Collections.emptyList();
            }
        }
        // A trivial quoting.
        // Because Lombok annotation options type is almost String or boolean.
        private static String quote(String value) {
            if (Boolean.TRUE.toString().equals(value) || Boolean.FALSE.toString().equals(value))
            // case of boolean, not passed as an array.
            {
                return value;
            }
            return value.replaceAll("[\\\\w]+", "\"$0\"");
        }
        private void appendOptions(String key, String value) {
            String keyPart = key.substring(key.indexOf(".") + 1);
            String valuePart = value.contains(",") ? String.format("{%s}", value) : value;
            this.options.add(String.format("%s=%s", keyPart, quote(valuePart)));
        }
        private String asAnnotation() {
            if (options.isEmpty()) {
                return name;
            }
            StringBuilder sb = new StringBuilder();
            sb.append(name);
            sb.append("(");
            boolean first = true;
            for (String option : options) {
                if (first) {
                    first = false;
                } else {
                    sb.append(", ");
                }
                sb.append(option);
            }
            sb.append(")");
            return sb.toString();
        }
    }
}

代码注释配置

代码生成时,使用数据控的注释给字段添加文档注释

效果

    /**
     * 编号
     */
    private String planId;

代码

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.MergeConstants;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.util.StringUtility;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import static org.mybatis.generator.internal.util.StringUtility.isTrue;
/**
* mybatis逆向工程默认的注释修改(使用表的注释)
*
* @author jingshiyu
* @date 2019/7/17 17:39:36
* @desc
*/
public class MyCommentGenerator implements CommentGenerator {
    /**
     * The properties.
     */
    private Properties properties;
    /**
     * The suppress date.
     */
    private boolean suppressDate;
    /**
     * The suppress all comments.
     */
    private boolean suppressAllComments;
    /**
     * 是否添加doc注释,true:不添加,false:添加<br>
     * The addition of table remark's comments.
     * If suppressAllComments is true, this option is ignored
     */
    private boolean addRemarkComments;
    private SimpleDateFormat dateFormat;
    public MyCommentGenerator() {
        super();
        properties = new Properties();
        suppressDate = false;
        suppressAllComments = false;
        addRemarkComments = false;
    }
    @Override
    public void addJavaFileComment(CompilationUnit compilationUnit) {
    }
    /**
     * 实体类对应的mapper.xml注释,mapper类不加注释,如有需要参考 DefaultCommentGenerator
     */
    @Override
    public void addComment(XmlElement xmlElement) {
        if (suppressAllComments) {
            return;
        }
    }
    @Override
    public void addRootComment(XmlElement rootElement) {
    }
    @Override
    public void addConfigurationProperties(Properties properties) {
        this.properties.putAll(properties);
        suppressDate = isTrue(properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_DATE));
        suppressAllComments = isTrue(properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS));
        addRemarkComments = isTrue(properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_ADD_REMARK_COMMENTS));
        String dateFormatString = properties.getProperty(PropertyRegistry.COMMENT_GENERATOR_DATE_FORMAT);
        if (StringUtility.stringHasValue(dateFormatString)) {
            dateFormat = new SimpleDateFormat(dateFormatString);
        }
    }
    protected void addJavadocTag(JavaElement javaElement, boolean markAsDoNotDelete) {
        javaElement.addJavaDocLine(" *");
        StringBuilder sb = new StringBuilder();
        sb.append(" * ");
        sb.append(MergeConstants.NEW_ELEMENT_TAG);
        if (markAsDoNotDelete) {
            sb.append(" do_not_delete_during_merge");
        }
        String s = getDateString();
        if (s != null) {
            sb.append(' ');
            sb.append(s);
        }
        javaElement.addJavaDocLine(sb.toString());
    }
    protected String getDateString() {
        if (suppressDate) {
            return null;
        } else if (dateFormat != null) {
            return dateFormat.format(new Date());
        } else {
            return new Date().toString();
        }
    }
    /**
     * 设置class的注解
     */
    @Override
    public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable) {
        if (suppressAllComments) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        innerClass.addJavaDocLine("/**");
//        sb.append(" * This class corresponds to the database table ");
        sb.append(introspectedTable.getFullyQualifiedTable());
        innerClass.addJavaDocLine(sb.toString());
        addJavadocTag(innerClass, false);
        innerClass.addJavaDocLine(" */");
    }
    /**
     * 方法注释
     */
    @Override
    public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        if (suppressAllComments || !addRemarkComments) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        topLevelClass.addJavaDocLine("/**");
        //设置数据库的备注
        String remarks = introspectedTable.getRemarks();
        if (addRemarkComments && StringUtility.stringHasValue(remarks)) {
            topLevelClass.addJavaDocLine(" * Database Table Remarks:");
            String[] remarkLines = remarks.split(System.getProperty("line.separator"));
            for (String remarkLine : remarkLines) {
                topLevelClass.addJavaDocLine(" *   " + remarkLine);
            }
        }
        topLevelClass.addJavaDocLine(" *");
        sb.append(introspectedTable.getFullyQualifiedTable());
        topLevelClass.addJavaDocLine(sb.toString());
        addJavadocTag(topLevelClass, true);
        topLevelClass.addJavaDocLine(" */");
    }
    /**
     * 添加枚举的注释
     */
    @Override
    public void addEnumComment(InnerEnum innerEnum, IntrospectedTable introspectedTable) {
        if (suppressAllComments) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        innerEnum.addJavaDocLine("/**");
        sb.append(introspectedTable.getFullyQualifiedTable());
        innerEnum.addJavaDocLine(sb.toString());
        addJavadocTag(innerEnum, false);
        innerEnum.addJavaDocLine(" */");
    }
    /**
     * 实体类字段注释
     */
    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
        if (suppressAllComments || StringUtils.isEmpty(introspectedColumn.getRemarks())) {
            return;
        }
        field.addJavaDocLine("/**");
        StringBuilder sb = new StringBuilder();
        //  introspectedColumn.getRemarks() 就是获取字段注释
        sb.append(" * " + introspectedColumn.getRemarks());
        field.addJavaDocLine(sb.toString());
        field.addJavaDocLine(" */");
    }
    /**
     * 实体类的静态字段
     */
    @Override
    public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
        if (suppressAllComments) {
            return;
        }
    }
    /**
     * 实体类toString方法
     */
    @Override
    public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {
        if (suppressAllComments) {
            return;
        }
    }
    /**
     * 实体类getter方法注释
     */
    @Override
    public void addGetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
        if (suppressAllComments) {
            return;
        }
    }
    /**
     * 实体类setter注释
     */
    @Override
    public void addSetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
        if (suppressAllComments) {
            return;
        }
    }
    /**
     * 类注释
     * @param innerClass
     * @param introspectedTable
     * @param markAsDoNotDelete
     */
    @Override
    public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable, boolean markAsDoNotDelete) {
        if (suppressAllComments) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        innerClass.addJavaDocLine("/**");
        sb.append(introspectedTable.getFullyQualifiedTable());
        innerClass.addJavaDocLine(sb.toString());
        addJavadocTag(innerClass, markAsDoNotDelete);
        innerClass.addJavaDocLine(" */");
    }
}

调用

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.exception.InvalidConfigurationException;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* java代码生成器(会生成example的entity)<br>
* mybatis逆向工程
*
* @author jingshiyu
* @date 2019/7/17 17:24
* @desc java代码生成器(会生成example的entity)<br>
*/
public class JavaExampleGenerator {
    public static void main(String[] args) {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        //如果这里出现空指针,直接写绝对路径即可。
        String genCfg = "/mbgConfiguration.xml";
        File configFile = new File(JavaExampleGenerator.class.getResource(genCfg).getFile());
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = null;
        try {
            config = cp.parseConfiguration(configFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = null;
        try {
            myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        } catch (InvalidConfigurationException e) {
            e.printStackTrace();
        }
        try {
            myBatisGenerator.generate(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。


相关文章

猜您喜欢

  • Selenium介绍及安装部署 python学习Selenium介绍及安装部署详解

    想了解python学习Selenium介绍及安装部署详解的相关内容吗,爱测试的高胖胖在本文为您仔细讲解Selenium介绍及安装部署的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:python学习,Selenium介绍及安装部署,下面大家一起来学习吧。..
  • Java实现doublyLinkedList 看动画学算法之Java实现doublyLinkedList

    想了解看动画学算法之Java实现doublyLinkedList的相关内容吗,程序员那些事在本文为您仔细讲解Java实现doublyLinkedList的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:Java实现doublyLinkedList,动画算法,下面大家一起来学习吧。..

网友评论

Copyright 2020 www.hss19.cn 【绿色下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式