作为一名拥有5年Java全栈开发经验的工程师,我最近参加了一场互联网大厂的面试。这场面试涵盖了从Java基础到微服务架构的方方面面,面试官的问题既有深度又有广度。下面我将完整复盘这场面试,并补充在实际工作中积累的经验和技巧,希望能给准备面试的同行们一些参考。
面试官李老师是某头部互联网公司的技术负责人,30岁出头,在技术选型和架构设计方面有丰富经验。他的提问风格很有特点:先问基础概念,再深入原理,最后结合实际场景考察解决问题的能力。
**我(张晨)**28岁,在某中型电商公司担任Java全栈开发工程师5年,主导过多个从0到1的项目开发。技术栈覆盖Java后端、前端Vue3、微服务架构等。这次面试我准备了2个月,重点梳理了知识体系中的薄弱环节。
当被问到JVM时,我首先画出了JVM的内存结构图:
code复制JVM内存区域:
- 方法区(元空间):存储类信息、常量、静态变量
- 堆:对象实例存储区域(新生代/老年代)
- 虚拟机栈:线程私有,存储栈帧(局部变量表、操作数栈等)
- 本地方法栈:Native方法服务
- 程序计数器:线程执行位置记录
特别注意:JDK8用元空间替代永久代,避免了OOM问题。建议设置-XX:MetaspaceSize参数控制初始大小。
对于垃圾回收,我详细解释了G1收集器的工作原理:
优化经验:在高并发电商系统中,我们通过以下JVM参数优化G1表现:
bash复制-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:InitiatingHeapOccupancyPercent=45
谈到线程池,我分享了ThreadPoolExecutor的核心配置参数:
| 参数 | 说明 | 电商系统推荐值 |
|---|---|---|
| corePoolSize | 核心线程数 | CPU核数+1 |
| maximumPoolSize | 最大线程数 | corePoolSize*2 |
| keepAliveTime | 空闲线程存活时间 | 60s |
| workQueue | 任务队列 | LinkedBlockingQueue(1000) |
踩坑记录:曾经因为队列设置过大导致OOM,后来改用SynchronousQueue配合CallerRunsPolicy拒绝策略。
我通过一个实际案例说明自动配置的工作机制:
开发技巧:自定义Starter时,建议按以下结构组织:
code复制my-starter
├── src
│ ├── main
│ │ ├── java/com/example/autoconfigure
│ │ │ ├── MyAutoConfiguration.java
│ │ │ └── MyProperties.java
│ │ └── resources
│ │ └── META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
我们项目中的Swagger配置示例:
java复制@Configuration
@OpenAPIDefinition(
info = @Info(
title = "电商平台API",
version = "1.0",
description = "电商系统接口文档"
)
)
public class SwaggerConfig {
@Bean
public OpenAPI customizeOpenAPI() {
return new OpenAPI()
.addSecurityItem(new SecurityRequirement().addList("JWT"))
.components(new Components()
.addSecuritySchemes("JWT",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")));
}
}
重要提示:生产环境一定要通过@Profile("dev")限制Swagger的访问权限。
在我们的订单系统中,通过以下手段优化查询性能:
xml复制<cache type="org.mybatis.caches.redis.RedisCache"
eviction="LRU"
flushInterval="60000"
size="1024"/>
xml复制<resultMap id="orderDetailMap" type="OrderDTO">
<id property="id" column="order_id"/>
<collection property="items" ofType="OrderItem"
select="selectItemsByOrderId" column="order_id"/>
</resultMap>
java复制@Insert("<script>" +
"INSERT INTO order_item(order_id,product_id,quantity) VALUES " +
"<foreach collection='items' item='item' separator=','>" +
"(#{item.orderId},#{item.productId},#{item.quantity})" +
"</foreach>" +
"</script>")
void batchInsert(@Param("items") List<OrderItem> items);
在用户管理模块中,我们实现了自动审计功能:
java复制@EntityListeners(AuditingEntityListener.class)
public class User {
@CreatedDate
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
@Version
private Integer version;
}
配置类需要添加:
java复制@Configuration
@EnableJpaAuditing
public class JpaConfig {}
我们的用户中心采用如下架构:
typescript复制// composables/useUser.ts
export const useUser = () => {
const user = ref<User>()
const loading = ref(false)
const fetchUser = async (id: number) => {
loading.value = true
try {
user.value = await api.getUser(id)
} finally {
loading.value = false
}
}
return { user, loading, fetchUser }
}
组件中使用:
vue复制<script setup>
const { user, loading, fetchUser } = useUser()
onMounted(() => fetchUser(123))
</script>
在购物车模块中,我们这样组织store:
typescript复制export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as CartItem[],
version: 1
}),
getters: {
totalPrice: (state) => state.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
},
actions: {
async syncCart() {
const { data } = await api.getCart()
this.items = data.items
this.version++
}
}
})
性能优化:在大型表单中使用$patch批量更新状态,避免频繁触发响应式。
我们的微服务技术栈:
网关关键配置示例:
yaml复制spring:
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 100
redis-rate-limiter.burstCapacity: 200
在订单支付场景中,我们采用Seata的AT模式:
关键配置:
properties复制# seata配置
seata.tx-service-group=my_tx_group
seata.service.vgroup-mapping.my_tx_group=default
seata.enable-auto-data-source-proxy=true
我们的JWT实现包含以下安全措施:
JWT工具类示例:
java复制public class JwtUtils {
private static final KeyPair keyPair = KeyPairGenerator.getInstance("RSA")
.generateKeyPair();
public static String generateToken(UserDetails user) {
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 7200000))
.signWith(keyPair.getPrivate(), SignatureAlgorithm.RS256)
.compact();
}
}
我们的安全配置类:
java复制@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
我们的商品详情页采用多级缓存架构:
缓存击穿解决方案:
java复制public Product getProduct(Long id) {
String key = "product:" + id;
Product product = redisTemplate.opsForValue().get(key);
if (product == null) {
synchronized (this) {
product = redisTemplate.opsForValue().get(key);
if (product == null) {
product = db.getProduct(id);
redisTemplate.opsForValue().set(key, product, 30, TimeUnit.MINUTES);
}
}
}
return product;
}
订单支付成功通知的可靠性方案:
生产者配置示例:
java复制@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka:9092");
config.put(ProducerConfig.ACKS_CONFIG, "all");
config.put(ProducerConfig.RETRIES_CONFIG, 3);
config.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true);
return new DefaultKafkaProducerFactory<>(config);
}
我们的日志规范:
Logback配置示例:
xml复制<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"app":"order-service","env":"${spring.profiles.active}"}</customFields>
</encoder>
</appender>
自定义业务指标示例:
java复制@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> configureMetrics() {
return registry -> {
Counter.builder("order.created")
.description("Total created orders")
.tag("channel", "app")
.register(registry);
};
}
Grafana监控看板关键指标:
我们的测试策略:
集成测试示例:
java复制@Testcontainers
@SpringBootTest
class OrderServiceIT {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
}
@Test
void shouldCreateOrder() {
// 测试逻辑
}
}
我们的GitLab CI流程:
yaml复制stages:
- test
- build
- deploy
unit-test:
stage: test
script:
- mvn test
package:
stage: build
script:
- mvn package -DskipTests
artifacts:
paths:
- target/*.jar
deploy-prod:
stage: deploy
script:
- kubectl set image deployment/order-service order-service=registry.example.com/order:${CI_COMMIT_SHA}
when: manual
only:
- master
经验分享:在CI阶段加入SonarQube静态代码分析,可以有效提升代码质量。