---
url: /notes/面试/真实面经/武汉中科通达/index.md
---
## 平时开发异常怎么处理

[Java 中 Exception 和 Error 有什么区别？ - Java 基础面试题 - 面试鸭 - 程序员求职面试刷题神器](https://www.mianshiya.com/bank/1787463103423897602/question/1780933294469181442)

[异常处理-自定义异常](http://xiaofei.site:10082/#/JavaSE/异常处理?id=自定义异常)

try catch

继承exception | RuntimeException

## 多线程的几种实现方式

[线程](http://xiaofei.site:10082/#/JavaSE/线程?id=创建线程)

继承`Thread`,重写`Run`方法,创建一个`Thread`**子类对象**,调用`start()`开启多线程

实现Runnable接口：应该由那些打算通过某一线程执行其实例的类来实现。实现了接口后需要重写run方法

实现Callable接口

## 说一下spring的IOC和AOP

[什么是 Spring 的 IOC 和 AOP？ - 面试鸭 - 程序员求职面试刷题神器](https://www.mianshiya.com/question/1776518079614816257)

[JDK 动态代理和 CGLIB 动态代理有什么区别？ - 面试鸭 - 程序员求职面试刷题神器](https://www.mianshiya.com/question/1780933294599204866)

IOC：控制反转，把对象的控制权交给spring容器去管理

AOP：面向切面，对业务进行解耦

JDK动态代理（有接口）

CGLIB动态代理（没有接口，继承生成目标类的子类来进行代理），final修饰不可被代理

## 说一下开发过程怎么通过多线程处理实际的业务

[什么是 Java 的 CountDownLatch？ - 面试鸭 - 程序员求职面试刷题神器](https://www.mianshiya.com/question/1780933294943137794)

通过countdownlatch计数器，多个线程并行执行，一个线程执行完成后计数器减1，等待所有线程执行完成后，继续执行其它逻辑

## 工作中用到的设计模式

[设计模式](http://xiaofei.site:10082/#/面试/设计模式?id=建造者模式)

建造者模式：将复杂对象的构建过程与其表示分离。如报表对象的生成

模板模式：抽象类定义了需要的业务骨架，具体步骤由子类实现

策略模式：避免大量的if else、switch，实现解耦

外观（门面）模式：提供统一的接口，简化客户端与多个子系统之间的交互

## 如果要设计一个订单表，怎么设计表结构

主键、订单id、订单状态

对应的商品/产品明细表（外键）

创建时间，创建人，更新时间，更新人

其它
产品BOM

产品配方

产品检验标准

## SQL优化，执行计划主要有哪些需要注意的

使用explain命令，查询mysql的执行计划，主要关注

select\_type：SIMPLE(简单查询)/PRIMARY(主查询)/SUBQUERY(子查询)

type（访问类型）：查询使用的访问方法，ALL（全表扫描）、index、range、ref（非唯一索引）

key（使用的索引）：如果是NULL，就没使用索引

rows（扫描的行数）

Extra（额外信息）：using index（使用覆盖索引）、using where（使用where条件进行过滤）、using temporary（使用临时表）、using filesort（使用文件排序）

## 开发过程中遇到的有成就感的需求

[飞速创新-介绍独立开发的需求-辅料到货计划](http://xiaofei.site:10082/#/面试/真实面经/飞速创新?id=介绍独立开发的需求-辅料到货计划)

## 项目中遇到的难解决的问题

[飞速创新-项目上遇到的难解决的问题](http://xiaofei.site:10082/#/面试/真实面经/飞速创新?id=项目上遇到的难解决的问题)

## ES通过spring boot怎么集成

导入maven依赖

设置配置文件

```properties
  elasticsearch:
    uris: http://localhost:9200
    username: root
    password: 123456
```

通过PUT命令创建一个索引

```json
{
  "aliases": {
    "post": {}
  },
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "content": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "tags": {
        "type": "keyword"
      },
      "thumbNum": {
        "type": "long"
      },
      "favourNum": {
        "type": "long"
      },
      "userId": {
        "type": "keyword"
      },
      "createTime": {
        "type": "date"
      },
      "updateTime": {
        "type": "date"
      },
      "isDelete": {
        "type": "keyword"
      }
    }
  }
}
```

同步数据使用logstash

```properties
# Sample Logstash configuration for creating a simple
# Beats -> Logstash -> Elasticsearch pipeline.

input {
  jdbc {
    jdbc_driver_library => "C:\Users\Administrator\AppData\Roaming\JetBrains\DataGrip2021.1\jdbc-drivers\MySQL ConnectorJ\8.0.25\mysql\mysql-connector-java\8.0.25\mysql-connector-java-8.0.25.jar"
    jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
    jdbc_connection_string => "jdbc:mysql://localhost:3306/xiaofei_site_search"
    jdbc_user => "root"
    jdbc_password => "root"
    use_column_value => true
    tracking_column => "updateTime"
    tracking_column_type => "timestamp"
    parameters => { "isDelete" => 1 }
    schedule => "*/5 * * * * *"
    jdbc_default_timezone => "Asia/Shanghai"
    # 记录上次执行位置
    last_run_metadata_path => "D:/tools/Elastic_Search/logstash-7.17.12/data/plugins/last_run_metadata_file"

    statement => "
      SELECT 
        id,
        fileName,
        description,
        file_type as fileType,
        file_extension as fileExtension,
        file_content as textContent,
        description,
        upload_time as uploadTime,
        upload_user_id as uploadUserId,
        biz,
        is_public as isPublic,
        created_at as createdAt,
        updated_at as updatedAt,
        status as status
      FROM file where upload_time > :sql_last_value and upload_time < now() order by upload_time desc
    "
  }
}

filter{
  mutate {
    rename => {
      "file_name" => "fileName"
      "file_path" => "filePath"
      "file_type" => "filetype"
      "file_extension" => "fileExtension"
      "file_content" => "textContent"
      "upload_time" => "uploadTime"
      "upload_user_id" => "uploadUserId"
      "is_public" => "isPublic"
      "created_at" => "createdAt"
      "updated_at" => "updatedAt"
      "description" => "description"
      "status" => "status"
    }
  }
}

output {
  stdout { codec => rubydebug }
  elasticsearch {
        hosts => "127.0.0.1:9200"
        index => "file_v1"
        document_id => "%{id}"
    }
}
```

查询数据后，通过对比数据库的数据，不存在的直接删除

## 怎么实现一个自定义注解

时机：编译时，运行时
目标：应用搭配方法，属性上...

```java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    DataSourceType value() default DataSourceType.MASTER;
}
```

```java
/**
 * @author tuaofei
 * @description 数据源切换切面
 * @date 2024/12/24
 */
@Slf4j
@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("@annotation(com.xiaofei.springbootinit.annotation.DataSource)")
    public void apiLogPointcut() {
    }

    @Around("apiLogPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        DataSource dataSource = signature.getMethod().getAnnotation(DataSource.class);
        if (dataSource != null) {
            DataSourceType dataSourceType = dataSource.value();
            log.info("切换数据源到: {}", dataSourceType);
            DataSourceContextHolder.setDataSource(dataSourceType);
        }

        try {
            return point.proceed();
        } finally {
            log.info("清除数据源配置");
            DataSourceContextHolder.clearDataSource();
        }
    }
}
```
