欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > 交大智邦后端Java笔试题

交大智邦后端Java笔试题

2025/3/10 16:52:36 来源:https://blog.csdn.net/qq_36534560/article/details/146067772  浏览:    关键词:交大智邦后端Java笔试题

交大智邦后端Java笔试题

简答题

  1. 只要一个类加上了@Component注解,就一定能成为一个Spring Bean吗?如果不是,请举出反例。

    不一定

    1. 扫描范围不包括 com.code.lab.web.component 或者被 @ComponentScan 显式排除 通过 excludeFilters 手动排除特定类
    2. 被条件注解排除 @ConditionalOnProperty(name = “feature.enabled”, havingValue = “false”)
    3. 被@Profile(“test”)限制环境
    package com.code.lab.web.component;import org.springframework.stereotype.Component;/*** @author Cai*/
    @Component
    @ConditionalOnProperty(name = "feature.enabled", havingValue = "false")
    @Profile("test")
    public class MyComponent {}
    package com.code.lab.web;import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.scheduling.annotation.EnableAsync;/*** @author Cai*/
    @EnableAsync
    @SpringBootApplication
    @ComponentScan("com.code.lab.web.config",excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = MyComponent.class))
    public class SpringbootCommonsPool2DemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootCommonsPool2DemoApplication.class, args);}
    }
    
  2. 如果有两个相同类型,但不重名的String Bean,在引入的时候应如何处理?针对@Autowired@Resource分别作答。

package com.code.lab.web.config.thrad;import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskDecorator;
import org.springframework.lang.NonNull;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;/*** @author Cai*/
@Configuration
@Slf4j
public class EnhancedThreadPoolConfig {/*** 订单处理线程池(高优先级)** @return 线程池*/@Bean("orderThreadPool")@Primarypublic ThreadPoolTaskExecutor orderThreadPool() {// 新增关键配置ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(4);executor.setMaxPoolSize(8);executor.setKeepAliveSeconds(60);executor.setQueueCapacity(2000);executor.setThreadFactory(new CustomThreadFactory("Order-Thread-"));executor.setRejectedExecutionHandler(new CustomRejectPolicy());executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(30);
//    优雅停机executor.setWaitForTasksToCompleteOnShutdown(true);executor.setTaskDecorator(new ContextCopyDecorator());executor.initialize();return executor;}/*** 日志记录线程(低优先级)** @return 线程池*/@Bean("logThreadPool")public ThreadPoolTaskExecutor logThreadPool() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(2);executor.setMaxPoolSize(2);executor.setQueueCapacity(1000);executor.setThreadNamePrefix("Log-Thread-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());executor.initialize();return executor;}
}
1. 使用 @Autowired 注入 结合 @Qualifier 指定 Bean 名称
显式指定要注入的 Bean 名称:
@Qualifier("orderThreadPool") 
@Autowired
ThreadPoolTaskExecutor orderExecutor2.使用 @Primary 标记默认 Bean
3.使用 @Resource 注入
@Resource(name="orderThreadPool")
ThreadPoolTaskExecutor orderExecutor

3.如果需要修改默认的时间输出格式,需要如何处理?(单个字段、全局)

局部
1.第一种使用 @JsonFormat(Jackson 序列化)@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private LocalDateTime createTime;
2:使用 @DateTimeFormat(Spring MVC 参数绑定)@DateTimeFormat(pattern = "yyyy/MM/dd")private Date startDate;
全局时间格式修改
修改 application.yml
spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8
通过代码自定义全局序列化规则:    
@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();// 注册 JavaTime 模块(处理 LocalDateTime 等类型)mapper.registerModule(new JavaTimeModule());// 全局日期格式mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));// 禁用时间戳格式mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);return mapper;}
}

4.如何保证接口消息的幂等性。

同一操作多次执行的结果与一次执行的结果一致:

1.唯一标识符(ID 幂等)

2.Token 机制

3.数据库唯一约束

4.锁

5.在请求接口中开启一个线程执行高CPU操作,是否会导致接口线程阻塞?

当请求到达接口时,主线程会执行以下操作

1.创建并启动一个新线程用于执行高 CPU 操作

2.继续执行后续代码

在启动新线程后不会被阻塞,会立即释放并处理其他请求


能耗统计

在项目现场需要统计用电量,因此拟使用一块电表进行电量的采集。与家用的电表一样,此表的采集值永远是累计值。采集到的JSON数据如下:

{"timestamp": "采集时的时间戳,数字格式","meterId": "电表唯一号","value": "采集到的结果,数字格式"
}

由于现场的不稳定,可能存在以下的特殊情况:

  1. 可能由于网络问题,导致采集数据失败,得到的value为0。
  2. 可能由于设备更换(不考虑更换期间的耗电),此累积值归零,但下一次采集的结果中,meterId会发生变化。(存在可能设备修复后继续使用)

请编写一段伪代码,以完成对此电表的采集,并按天给出使用电量。

package com.code.lab.pool;import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;/*** @author Cai*/
public class ElectricityMeterProcessor {// 记录每个电表的上次有效读数:meterId -> (lastTimestamp, lastValue)private final Map<String, MeterRecord> meterDb = new HashMap<>();// 按天统计总用电量:dateStr -> totalUsageprivate final Map<String, Long> dailyUsage = new HashMap<>();private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");// 处理电表数据public void processMeterData(MeterData data) {// 过滤无效数据(value=0)if (data.value == 0) {return;}String meterId = data.meterId;long currentTimestamp = data.timestamp;long currentValue = data.value;// 转换时间戳为日期字符串(UTC时区)LocalDate date = Instant.ofEpochSecond(currentTimestamp).atZone(ZoneId.of("UTC")).toLocalDate();String dateStr = date.format(DATE_FORMATTER);// 新电表或设备更换后首次读数if (!meterDb.containsKey(meterId)) {meterDb.put(meterId, new MeterRecord(currentTimestamp, currentValue));return;}// 获取上次记录MeterRecord lastRecord = meterDb.get(meterId);long lastValue = lastRecord.value;// 计算增量long increment;if (currentValue < lastValue) {// 设备重置,视为新设备开始累计increment = currentValue;} else {// 正常增量increment = currentValue - lastValue;}// 更新电表记录meterDb.put(meterId, new MeterRecord(currentTimestamp, currentValue));// 累加当日用电量dailyUsage.merge(dateStr, increment, Long::sum);}// 获取按天统计结果public Map<String, Long> getDailyUsage() {return new HashMap<>(dailyUsage);}// 电表数据类public static class MeterData {long timestamp; // 时间戳(秒级)String meterId; // 电表IDlong value; // 电表读数public MeterData(long timestamp, String meterId, long value) {this.timestamp = timestamp;this.meterId = meterId;this.value = value;}}// 电表记录类(保存上次读数和时间)private static class MeterRecord {long timestamp;long value;public MeterRecord(long timestamp, long value) {this.timestamp = timestamp;this.value = value;}}// 测试示例public static void main(String[] args) {ElectricityMeterProcessor processor = new ElectricityMeterProcessor();// 示例数据流(时间戳为秒级)// 2023-10-01,初始值processor.processMeterData(new MeterData(1696118400L, "A", 100));// 2023-10-02,+200processor.processMeterData(new MeterData(1696204800L, "A", 300));// 无效数据(过滤)processor.processMeterData(new MeterData(1696291200L, "A", 0));// 2023-10-03,设备重置 +50processor.processMeterData(new MeterData(1696291200L, "A", 50));// 无效数据(过滤)processor.processMeterData(new MeterData(1696377600L, "B", 0));// 2023-10-05,新电表初始值processor.processMeterData(new MeterData(1696464000L, "B", 200));// 输出结果System.out.println(processor.getDailyUsage());}
}

权限管理

有一套系统,可以对用户、权限、菜单进行管理,即通过配置用户的权限、权限可访问的菜单,完成对用户操作区域的管理。

  1. 请设计上述内容的表结构,并完成service部分的伪代码,以及所使用的SQL语句(或使用JPA表达)。
  2. 请完成接口鉴权的伪代码。(假设同一菜单的URL前缀一致)
#用户表 (user)
CREATE TABLE user (id BIGINT PRIMARY KEY AUTO_INCREMENT,username VARCHAR(50) UNIQUE NOT NULL,password VARCHAR(100) NOT NULL
);
#权限表 (permission)
CREATE TABLE permission (id BIGINT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL,      -- 如 "用户管理-查询"code VARCHAR(50) UNIQUE NOT NULL -- 如 "user:read"
);
#菜单表 (menu)
CREATE TABLE menu (id BIGINT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(50) NOT NULL,      -- 如 "用户管理"url_prefix VARCHAR(100) NOT NULL, -- 如 "/api/users"parent_id BIGINT,               -- 支持多级菜单FOREIGN KEY (parent_id) REFERENCES menu(id)
);
#用户权限关联表 (user_permission)
CREATE TABLE user_permission (user_id BIGINT NOT NULL,permission_id BIGINT NOT NULL,PRIMARY KEY (user_id, permission_id),FOREIGN KEY (user_id) REFERENCES user(id),FOREIGN KEY (permission_id) REFERENCES permission(id)
);
#权限菜单关联表 (permission_menu)
CREATE TABLE permission_menu (permission_id BIGINT NOT NULL,menu_id BIGINT NOT NULL,PRIMARY KEY (permission_id, menu_id),FOREIGN KEY (permission_id) REFERENCES permission(id),FOREIGN KEY (menu_id) REFERENCES menu(id)
);

Service 伪代码与 SQL/JPA 实现

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;// 为用户分配权限public void assignPermission(Long userId, Long permissionId) {userRepository.addUserPermission(userId, permissionId);}// 查询用户拥有的所有菜单public List<Menu> getUserMenus(Long userId) {return userRepository.findMenusByUserId(userId);}
}// PermissionService.java
@Service
public class PermissionService {@Autowiredprivate PermissionRepository permissionRepository;// 为权限关联菜单public void linkMenuToPermission(Long permissionId, Long menuId) {permissionRepository.addPermissionMenu(permissionId, menuId);}
}// MenuService.java
@Service
public class MenuService {@Autowiredprivate MenuRepository menuRepository;// 根据 URL 前缀查找菜单public Menu findMenuByUrlPrefix(String urlPrefix) {return menuRepository.findByUrlPrefix(urlPrefix);}
}
// User.java(实体类)
@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;@ManyToMany@JoinTable(name = "user_permission",joinColumns = @JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "permission_id"))private Set<Permission> permissions;
}// Permission.java(实体类)
@Entity
public class Permission {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String code;@ManyToMany@JoinTable(name = "permission_menu",joinColumns = @JoinColumn(name = "permission_id"),inverseJoinColumns = @JoinColumn(name = "menu_id"))private Set<Menu> menus;
}// Menu.java(实体类)
@Entity
public class Menu {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String urlPrefix;@ManyToOne@JoinColumn(name = "parent_id")private Menu parent;
}

SQL 查询示例

-- 查询用户的所有菜单(JPA 会自动生成类似 SQL)
SELECT m.* FROM menu m
JOIN permission_menu pm ON m.id = pm.menu_id
JOIN permission p ON pm.permission_id = p.id
JOIN user_permission up ON p.id = up.permission_id
WHERE up.user_id = 1;

接口鉴权伪代码

  1. 用户请求到达接口时,解析请求 URL。
  2. 根据 URL 前缀匹配对应的菜单。
  3. 检查用户是否有权限访问该菜单关联的权限。
// AuthInterceptor.java(拦截器伪代码)
public class AuthInterceptor implements HandlerInterceptor {@Autowiredprivate MenuService menuService;@Autowiredprivate UserService userService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {String requestUrl = request.getRequestURI();// 1. 根据 URL 前缀匹配菜单Menu menu = menuService.findMenuByUrlPrefix(requestUrl);if (menu == null) {// 无菜单配置,默认放行或拒绝return true; }// 2. 获取当前用户 ID(假设已通过登录验证)Long userId = (Long) request.getAttribute("currentUserId");// 3. 检查用户是否有权限访问该菜单List<Menu> userMenus = userService.getUserMenus(userId);if (!userMenus.contains(menu)) {response.sendError(403, "无权限访问");return false;}return true;}
}

分布式锁

基于spring data redis中提供的redisTempalte,实现分布式锁的功能,要求满足:

  1. 能在持有锁的线程终止后,自动释放锁。

    @Component
    public class RedisDistributedLock {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final String LOCK_PREFIX = "LOCK:";private static final String LOCK_VALUE = UUID.randomUUID().toString(); // 确保唯一性private static final long DEFAULT_EXPIRE = 30000; // 默认锁过期时间 30 秒// 获取锁public boolean tryLock(String lockKey, long expireMillis) {String key = LOCK_PREFIX + lockKey;return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, LOCK_VALUE, expireMillis > 0 ? expireMillis : DEFAULT_EXPIRE, TimeUnit.MILLISECONDS));}// 释放锁(Lua 脚本保证原子性)public void unlock(String lockKey) {String key = LOCK_PREFIX + lockKey;String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"   return redis.call('del', KEYS[1]) " +"else " +"   return 0 " +"end";redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(key), LOCK_VALUE);}
    }
    
  2. 能强行抢占锁,被强的线程能暂停执行,并等待锁的恢复。

    public class LockInterruptHandler {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final String CHANNEL_PREFIX = "LOCK_INTERRUPT:";// 发送抢占信号public void sendInterruptSignal(String lockKey) {String channel = CHANNEL_PREFIX + lockKey;redisTemplate.convertAndSend(channel, "INTERRUPT");}// 订阅抢占信号public void subscribe(String lockKey, Runnable onInterrupt) {String channel = CHANNEL_PREFIX + lockKey;redisTemplate.getConnectionFactory().getConnection().subscribe((message, pattern) -> onInterrupt.run(), channel.getBytes(StandardCharsets.UTF_8));}
    }
    
  3. 实现公平锁,即锁的持有是有序的。(扩展题,可不作答)

​ 使用 Redis List 维护等待队列,确保按请求顺序获取锁。

public class FairRedisLock extends RedisDistributedLock {private static final String QUEUE_PREFIX = "LOCK_QUEUE:";private static final String QUEUE_VALUE_PREFIX = "REQUEST:";// 获取公平锁public boolean tryFairLock(String lockKey, long timeout) {String queueKey = QUEUE_PREFIX + lockKey;String requestId = QUEUE_VALUE_PREFIX + UUID.randomUUID();// 加入等待队列redisTemplate.opsForList().rightPush(queueKey, requestId);try {long start = System.currentTimeMillis();while (System.currentTimeMillis() - start < timeout) {// 检查是否轮到当前请求String head = redisTemplate.opsForList().index(queueKey, 0);if (requestId.equals(head) && tryLock(lockKey, DEFAULT_EXPIRE)) {redisTemplate.opsForList().remove(queueKey, 0, requestId);return true;}TimeUnit.MILLISECONDS.sleep(100);}return false;} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;} finally {redisTemplate.opsForList().remove(queueKey, 0, requestId);}}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词