《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
本文是《MyBatis初级实战》系列的第四篇,一个springboot应用同时操作两个数据库的场景,在平时也会遇到,今天要实战的就是通过druid配置两个数据源,让一个springboot应用同时使用这两个数据源;
多数据源配置的基本思路
-
首先要明确的是:数据源是通过配置类实现的,因此要去掉springboot中和数据源相关的自动装配;
-
最核心的问题有两个,第一个是确定表和数据源的关系,这个关系是在SqlSessionFactory实例中确立的,代码如下所示:
@Bean(name = “secondSqlSessionFactory”)
public SqlSessionFactory sqlSessionFactory(@Qualifier(“secondDataSource”) DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“classpath:mappers/second/**/*Mapper.xml”));
return bean.getObject();
}
- 第二个核心问题是包扫描,即指定的mapper接口要使用指定的sqlSessionTemplat,这个关系在SqlSessionTemplate配置类中(相当于旧版的xml配置bean),如下图所示:
-
从上述代码可见,如果上层的业务代码想操作secondDataSource这个数据源的表,只要把对应的*Mapper.xml文件和Mapper接口文件对应的目录下即可;
-
整个配置的关键步骤如下图所示:
实战概览
本次实战的内容如下:
-
一共有两个数据库:mybatis和mybatis_second;
-
mybatis中有名为user的表,mybatis_second中有名为address的表;
-
新建名为druidtwosource的springboot应用,里面有两个controller,可以分别对user、address这两个表进行操作;
-
编写单元测试用例,通过调用controller接口验证应用功能正常;
-
启动springboot应用,通过swagger验证功能正常;
-
进入druid监控页面;
源码下载
- 如果您不想编码,可以在GitHub下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
| 名称 | 链接 | 备注 |
| :-- | :-- | :-- |
| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
| git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
| git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,本章的应用在mybatis文件夹下,如下图红框所示:
创建数据库和表
- 创建名为mybatis的数据库,建表语句如下:
DROP TABLE IF EXISTS user
;
CREATE TABLE user
(
id
int(32) NOT NULL AUTO_INCREMENT,
name
varchar(32) NOT NULL,
age
int(32) NOT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
- 创建名为mybatis_second的数据库,建表语句如下:
DROP TABLE IF EXISTS address
;
CREATE TABLE address
(
id
int(32) NOT NULL AUTO_INCREMENT,
city
varchar(32) NOT NULL,
street
varchar(32) NOT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
编码
- 前文《MyBatis初级实战之一:Spring Boot集成》创建了父工程mybatis,本文继续在此工程中新增子工程,名为druidtwosource,先提前看整个子工程文件结构,如下图,要注意的是红框1中的mapper接口,以及红框2中的mapper映射文件,这两处都按照数据库的不同放入各自文件夹:
- druidtwosource工程的pom.xml内容如下:
<project xmlns=“http://maven.apache.org/POM/4.0.0” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd”>
4.0.0
com.bolingcavalry
mybatis
1.0-SNAPSHOT
…/pom.xml
com.bolingcavalry
druidtwosource
0.0.1-SNAPSHOT
druidtwosource
Demo project for Mybatis Druid (two datasource) in Spring Boot
<java.version>1.8</java.version>
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
io.springfox
springfox-swagger2
io.springfox
springfox-swagger-ui
com.alibaba
druid-spring-boot-starter
junit
junit
test
com.google.code.gson
gson
org.springframework.boot
spring-boot-maven-plugin
- 配置文件application.yml,可见这里面有first和second两个数据源配置,而druid的web-stat-filter和stat-view-servlet这两个配置是公用的:
server:
port: 8080
spring:
#1.JDBC数据源
datasource:
druid:
first:
username: root
password: 123456
url: jdbc:mysql://192.168.50.43:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
#初始化连接池的连接数量 大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 20
#配置获取连接等待超时的时间
max-wait: 60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 30000
配置一个连接在池中最大生存的时间,单位是毫秒
max-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM user
test-while-idle: true
test-on-borrow: true
test-on-return: false
是否缓存preparedStatement,也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall’用于防火墙
filters: stat,wall,slf4j
filter:
stat:
merge-sql: true
slow-sql-millis: 5000
second:
username: root
password: 123456
url: jdbc:mysql://192.168.50.43:3306/mybatis_second?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
#初始化连接池的连接数量 大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 20
#配置获取连接等待超时的时间
max-wait: 60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 30000
配置一个连接在池中最大生存的时间,单位是毫秒
max-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM user
test-while-idle: true
test-on-borrow: true
test-on-return: false
是否缓存preparedStatement,也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall’用于防火墙
filters: stat,wall,slf4j
filter:
stat:
merge-sql: true###
slow-sql-millis: 5000
#3.基础监控配置
web-stat-filter:
enabled: true
url-pattern: /*
#设置不统计哪些URL
exclusions: “.js,.gif,.jpg,.png,.css,.ico,/druid/*”
session-stat-enable: true
session-stat-max-count: 100
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: true
#设置监控页面的登录名和密码
login-username: admin
login-password: admin
allow: 127.0.0.1
#deny: 192.168.1.100
日志配置
logging:
level:
root: INFO
com:
bolingcavalry:
druidtwosource:
mapper: debug
- user的映射配置,请注意文件位置:
insert into user (id, name, age) values (#{id}, #{name}, #{age})
select id, name, age from user where name like concat(‘%’, #{name}, ‘%’)
delete from user where id= #{id}
- address的映射配置:
insert into address (id, city, street) values (#{id}, #{city}, #{street})
select id, city, street from address where city like concat(‘%’, #{cityname}, ‘%’)
delete from address where id= #{id}
- user表的实体类,注意swagger用到的注解:
package com.bolingcavalry.druidtwosource.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(description = “用户实体类”)
public class User {
@ApiModelProperty(value = “用户ID”)
private Integer id;
@ApiModelProperty(value = “用户名”, required = true)
private String name;
@ApiModelProperty(value = “用户地址”, required = false)
private Integer age;
@Override
public String toString() {
return “User{” +
“id=” + id +
“, name='” + name + ‘’’ +
“, age=” + age +
‘}’;
}
…省略get和set方法
}
- address表的实体类:
package com.bolingcavalry.druidtwosource.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(description = “地址实体类”)
public class Address {
@ApiModelProperty(value = “地址ID”)
private Integer id;
@ApiModelProperty(value = “城市名”, required = true)
private String city;
@ApiModelProperty(value = “街道名”, required = true)
private String street;
@Override
public String toString() {
return “Address{” +
“id=” + id +
“, city='” + city + ‘’’ +
“, street='” + street + ‘’’ +
‘}’;
}
…省略get和set方法
}
- 启动类DuridTwoSourceApplication.java,要注意的是排除掉数据源和事务的自动装配,因为后面会手动编码执行这些配置:
package com.bolingcavalry.druidtwosource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
@SpringBootApplication(exclude={
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
})
public class DuridTwoSourceApplication {
public static void main(String[] args) {
SpringApplication.run(DuridTwoSourceApplication.class, args);
}
}
- swagger配置:
package com.bolingcavalry.druidtwosource;
import springfox.documentation.service.Contact;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Tag;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
-
@Description: swagger配置类
-
@author: willzhao E-mail: zq2599@gmail.com
-
@date: 2020/8/11 7:54
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.tags(new Tag(“UserController”, “用户服务”),
new Tag(“AddressController”, “地址服务”))
.select()
// 当前包路径
.apis(RequestHandlerSelectors.basePackage(“com.bolingcavalry.druidtwosource.controller”))
.paths(PathSelectors.any())
.build();
}
//构建 api文档的详细信息函数,注意这里的注解引用的是哪个
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//页面标题
.title(“MyBatis CURD操作”)
//创建人
.contact(new Contact(“程序员欣宸”, “https://github.com/zq2599/blog_demos”, “zq2599@gmail.com”))
//版本号
.version(“1.0”)
//描述
.description(“API 描述”)
.build();
}
}
- 数据源配置TwoDataSourceConfig.java,可见是通过ConfigurationProperties注解来确定配置信息,另外不要忘记在默认数据源上添加Primary注解:
package com.bolingcavalry.druidtwosource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
/**
-
@Description: druid配置类
-
@author: willzhao E-mail: zq2599@gmail.com
-
@date: 2020/8/18 08:12
*/
@Configuration
public class TwoDataSourceConfig {
@Primary
@Bean(name = “firstDataSource”)
@ConfigurationProperties(“spring.datasource.druid.first”)
public DataSource first() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = “secondDataSource”)
@ConfigurationProperties(“spring.datasource.druid.second”)
public DataSource second() {
return DruidDataSourceBuilder.create().build();
}
}
- 第一个数据源的mybatis配置类DruidConfigFirst.java,可以结合本篇的第一幅图来看,注意MapperScan注解的两个属性basePackages和sqlSessionTemplateRef是关键,它们最终决定了哪些mapper接口使用哪个数据源,另外注意要带上Primary注解:
package com.bolingcavalry.druidtwosource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
/**
-
@Description: druid配置类
-
@author: willzhao E-mail: zq2599@gmail.com
-
@date: 2020/8/18 08:12
*/
@Configuration
@MapperScan(basePackages = “com.bolingcavalry.druidtwosource.mapper.first”, sqlSessionTemplateRef = “firstSqlSessionTemplate”)
public class DruidConfigFirst {
@Bean(name = “firstSqlSessionFactory”)
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier(“firstDataSource”) DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“classpath:mappers/first/**/*Mapper.xml”));
return bean.getObject();
}
@Bean(name = “firstTransactionManager”)
@Primary
public DataSourceTransactionManager transactionManager(@Qualifier(“firstDataSource”) DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = “firstSqlSessionTemplate”)
@Primary
public SqlSessionTemplate sqlSessionTemplate(@Qualifier(“firstSqlSessionFactory”) SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
- 第二个数据源的mybatis配置DruidConfigSecond.java,注意不要带Primary注解:
package com.bolingcavalry.druidtwosource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
/**
-
@Description: druid配置类
-
@author: willzhao E-mail: zq2599@gmail.com
-
@date: 2020/8/18 08:12
*/
@Configuration
@MapperScan(basePackages = “com.bolingcavalry.druidtwosource.mapper.second”, sqlSessionTemplateRef = “secondSqlSessionTemplate”)
public class DruidConfigSecond {
@Bean(name = “secondSqlSessionFactory”)
public SqlSessionFactory sqlSessionFactory(@Qualifier(“secondDataSource”) DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(“classpath:mappers/second/**/*Mapper.xml”));
return bean.getObject();
}
@Bean(name = “secondTransactionManager”)
public DataSourceTransactionManager transactionManager(@Qualifier(“secondDataSource”) DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = “secondSqlSessionTemplate”)
public SqlSessionTemplate sqlSessionTemplate(@Qualifier(“secondSqlSessionFactory”) SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
- user表的mapper接口类很简单,只有三个接口,注意package位置:
package com.bolingcavalry.druidtwosource.mapper.first;
import com.bolingcavalry.druidtwosource.entity.User;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface UserMapper {
int insertWithFields(User user);
List findByName(String name);
int delete(int id);
}
- address表的Mapper接口类:
package com.bolingcavalry.druidtwosource.mapper.second;
import com.bolingcavalry.druidtwosource.entity.Address;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
-
@Description: 地址实体的接口类
-
@author: willzhao E-mail: zq2599@gmail.com
-
@date: 2020/8/4 8:32
*/
@Repository
public interface AddressMapper {
int insertWithFields(Address address);
List
findByCityName(String cityName);int delete(int id);
}
- user表的service类:
package com.bolingcavalry.druidtwosource.service;
import com.bolingcavalry.druidtwosource.entity.User;
import com.bolingcavalry.druidtwosource.mapper.first.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
public class UserService {
@Autowired
UserMapper userMapper;
public User insertWithFields(User user) {
userMapper.insertWithFields(user);
return user;
}
public List findByName(String name) {
return userMapper.findByName(name);
}
public int delete(int id) {
return userMapper.delete(id);
}
}
- address表的service类:
package com.bolingcavalry.druidtwosource.service;
import com.bolingcavalry.druidtwosource.entity.Address;
import com.bolingcavalry.druidtwosource.entity.User;
import com.bolingcavalry.druidtwosource.mapper.first.UserMapper;
import com.bolingcavalry.druidtwosource.mapper.second.AddressMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class AddressService {
@Autowired
AddressMapper addressMapper;
public Address insertWithFields(Address address) {
addressMapper.insertWithFields(address);
return address;
}
public List
findByCityName(String cityName) {return addressMapper.findByCityName(cityName);
}
public int delete(int id) {
return addressMapper.delete(id);
}
}
- user表的controller:
最后
分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。
面试经验技巧篇
- 经验技巧1 如何巧妙地回答面试官的问题
- 经验技巧2 如何回答技术性的问题
- 经验技巧3 如何回答非技术性问题
- 经验技巧4 如何回答快速估算类问题
- 经验技巧5 如何回答算法设计问题
- 经验技巧6 如何回答系统设计题
- 经验技巧7 如何解决求职中的时间冲突问题
- 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
- 经验技巧9 在被企业拒绝后是否可以再申请
- 经验技巧10 如何应对自己不会回答的问题
- 经验技巧11 如何应对面试官的“激将法”语言
- 经验技巧12 如何处理与面试官持不同观点这个问题
- 经验技巧13 什么是职场暗语
面试真题篇
- 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
- 真题详解2 某知名社交平台软件工程师笔试题
- 真题详解3 某知名安全软件服务提供商软件工程师笔试题
- 真题详解4 某知名互联网金融企业软件工程师笔试题
- 真题详解5 某知名搜索引擎提供商软件工程师笔试题
- 真题详解6 某初创公司软件工程师笔试题
- 真题详解7 某知名游戏软件开发公司软件工程师笔试题
- 真题详解8 某知名电子商务公司软件工程师笔试题
- 真题详解9 某顶级生活消费类网站软件工程师笔试题
- 真题详解10 某知名门户网站软件工程师笔试题
- 真题详解11 某知名互联网金融企业软件工程师笔试题
- 真题详解12 国内某知名网络设备提供商软件工程师笔试题
- 真题详解13 国内某顶级手机制造商软件工程师笔试题
- 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
- 真题详解15 某著名社交类上市公司软件工程师笔试题
- 真题详解16 某知名互联网公司软件工程师笔试题
- 真题详解17 某知名网络安全公司校园招聘技术类笔试题
- 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题
资料整理不易,点个关注再走吧
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!
public Address insertWithFields(Address address) {
addressMapper.insertWithFields(address);
return address;
}
public List
findByCityName(String cityName) {return addressMapper.findByCityName(cityName);
}
public int delete(int id) {
return addressMapper.delete(id);
}
}
- user表的controller:
最后
分享一套我整理的面试干货,这份文档结合了我多年的面试官经验,站在面试官的角度来告诉你,面试官提的那些问题他最想听到你给他的回答是什么,分享出来帮助那些对前途感到迷茫的朋友。
面试经验技巧篇
- 经验技巧1 如何巧妙地回答面试官的问题
- 经验技巧2 如何回答技术性的问题
- 经验技巧3 如何回答非技术性问题
- 经验技巧4 如何回答快速估算类问题
- 经验技巧5 如何回答算法设计问题
- 经验技巧6 如何回答系统设计题
- 经验技巧7 如何解决求职中的时间冲突问题
- 经验技巧8 如果面试问题曾经遇见过,是否要告知面试官
- 经验技巧9 在被企业拒绝后是否可以再申请
- 经验技巧10 如何应对自己不会回答的问题
- 经验技巧11 如何应对面试官的“激将法”语言
- 经验技巧12 如何处理与面试官持不同观点这个问题
- 经验技巧13 什么是职场暗语
[外链图片转存中…(img-xZ71BLME-1714707809302)]
面试真题篇
- 真题详解1 某知名互联网下载服务提供商软件工程师笔试题
- 真题详解2 某知名社交平台软件工程师笔试题
- 真题详解3 某知名安全软件服务提供商软件工程师笔试题
- 真题详解4 某知名互联网金融企业软件工程师笔试题
- 真题详解5 某知名搜索引擎提供商软件工程师笔试题
- 真题详解6 某初创公司软件工程师笔试题
- 真题详解7 某知名游戏软件开发公司软件工程师笔试题
- 真题详解8 某知名电子商务公司软件工程师笔试题
- 真题详解9 某顶级生活消费类网站软件工程师笔试题
- 真题详解10 某知名门户网站软件工程师笔试题
- 真题详解11 某知名互联网金融企业软件工程师笔试题
- 真题详解12 国内某知名网络设备提供商软件工程师笔试题
- 真题详解13 国内某顶级手机制造商软件工程师笔试题
- 真题详解14 某顶级大数据综合服务提供商软件工程师笔试题
- 真题详解15 某著名社交类上市公司软件工程师笔试题
- 真题详解16 某知名互联网公司软件工程师笔试题
- 真题详解17 某知名网络安全公司校园招聘技术类笔试题
- 真题详解18 某知名互联网游戏公司校园招聘运维开发岗笔试题
[外链图片转存中…(img-EAJks1HK-1714707809302)]
资料整理不易,点个关注再走吧
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门,即可获取!