Java Web常见考题总结
1. Servlet基础
Q1: 什么是Servlet?Servlet的生命周期是什么?
答案:
Servlet: 运行在服务器端的Java程序,用于处理HTTP请求
生命周期:
加载 → 实例化 → 初始化(init) → 服务(service) → 销毁(destroy)
详细说明:
- 加载: Web容器加载Servlet类
- 实例化: 创建Servlet实例(只创建一次)
- 初始化: 调用init()方法
- 服务: 每个请求调用service()方法
- 销毁: 容器关闭时调用destroy()方法
Q2: doGet()和doPost()方法的区别?
答案:
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 处理GET请求 - 获取数据
// 参数在URL中,有长度限制
// 可以被缓存和书签
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
// 处理POST请求 - 提交数据
// 参数在请求体中,无长度限制
// 不能被缓存,更安全
}
}
Q3: 如何在Servlet中获取请求参数?
答案:
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 获取单个参数
String username = request.getParameter("username");
// 获取多个同名参数
String[] hobbies = request.getParameterValues("hobby");
// 获取所有参数名
Enumeration<String> paramNames = request.getParameterNames();
// 获取参数Map
Map<String, String[]> paramMap = request.getParameterMap();
}
2. JSP基础
Q4: JSP的执行过程是什么?
答案:
JSP文件 → 翻译成Servlet → 编译成.class → 执行
详细过程:
- 客户端请求JSP页面
- Web容器将JSP翻译成Servlet源码
- 编译Servlet源码生成.class文件
- 加载并执行Servlet
- 返回HTML响应
Q5: JSP的九大内置对象是什么?
答案:
- request: HttpServletRequest对象
- response: HttpServletResponse对象
- session: HttpSession对象
- application: ServletContext对象
- page: 当前JSP页面对象
- pageContext: PageContext对象
- out: JspWriter对象
- config: ServletConfig对象
- exception: Exception对象(错误页面中)
Q6: JSP中的四种作用域是什么?
答案:
- page: 当前页面有效
- request: 当前请求有效
- session: 当前会话有效
- application: 整个应用有效
<%
// 设置不同作用域的属性
pageContext.setAttribute("pageAttr", "page value");
request.setAttribute("requestAttr", "request value");
session.setAttribute("sessionAttr", "session value");
application.setAttribute("appAttr", "application value");
%>
3. 会话管理
Q7: Cookie和Session的区别?
答案:
| 特性 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端浏览器 | 服务器端 |
| 安全性 | 较低,可被篡改 | 较高,存储在服务器 |
| 存储容量 | 4KB限制 | 无限制(受服务器内存限制) |
| 生命周期 | 可设置过期时间 | 默认浏览器关闭失效 |
| 网络传输 | 每次请求都传输 | 只传输SessionID |
Q8: 如何创建和使用Cookie?
答案:
// 创建Cookie
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
// 创建Cookie
Cookie cookie = new Cookie("username", "john");
cookie.setMaxAge(60 * 60 * 24); // 设置过期时间(秒)
cookie.setPath("/"); // 设置路径
response.addCookie(cookie);
// 读取Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("username".equals(c.getName())) {
String username = c.getValue();
}
}
}
}
Q9: Session的工作原理是什么?
答案:
- 客户端首次访问,服务器创建Session对象
- 服务器生成唯一的SessionID
- 将SessionID通过Cookie发送给客户端
- 客户端后续请求携带SessionID
- 服务器根据SessionID找到对应Session
protected void doGet(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession(); // 获取或创建Session
session.setAttribute("user", "john"); // 存储数据
String user = (String) session.getAttribute("user"); // 获取数据
session.invalidate(); // 销毁Session
}
4. 过滤器和监听器
Q10: 什么是Filter?如何使用?
答案:
Filter: 过滤器,用于在请求到达Servlet之前或响应返回客户端之前进行处理
@WebFilter("/*")
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 请求前处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
// 继续执行
chain.doFilter(request, response);
// 响应后处理
System.out.println("Response processed");
}
@Override
public void destroy() {
// 销毁
}
}
Q11: 监听器有哪些类型?
答案:
ServletContext监听器:
- ServletContextListener: 监听应用启动/关闭
- ServletContextAttributeListener: 监听应用属性变化
HttpSession监听器:
- HttpSessionListener: 监听会话创建/销毁
- HttpSessionAttributeListener: 监听会话属性变化
ServletRequest监听器:
- ServletRequestListener: 监听请求创建/销毁
- ServletRequestAttributeListener: 监听请求属性变化
@WebListener
public class SessionCountListener implements HttpSessionListener {
private int sessionCount = 0;
@Override
public void sessionCreated(HttpSessionEvent se) {
sessionCount++;
System.out.println("Session created. Total: " + sessionCount);
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
sessionCount--;
System.out.println("Session destroyed. Total: " + sessionCount);
}
}
5. Spring MVC
Q12: Spring MVC的执行流程是什么?
答案:
1. 客户端请求 → DispatcherServlet
2. DispatcherServlet → HandlerMapping (查找Controller)
3. HandlerMapping → DispatcherServlet (返回Handler)
4. DispatcherServlet → HandlerAdapter (执行Controller)
5. Controller → 业务逻辑处理 → ModelAndView
6. DispatcherServlet → ViewResolver (解析视图)
7. ViewResolver → View (返回视图)
8. View → 渲染 → 响应客户端
Q13: Spring MVC常用注解有哪些?
答案:
@Controller // 标识控制器类
@RestController // @Controller + @ResponseBody
@RequestMapping // 映射请求URL
@GetMapping // 映射GET请求
@PostMapping // 映射POST请求
@PathVariable // 获取路径变量
@RequestParam // 获取请求参数
@RequestBody // 获取请求体
@ResponseBody // 返回JSON数据
@ModelAttribute // 绑定模型数据
// 示例
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}
Q14: 如何处理异常?
答案:
// 全局异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException e) {
ErrorResponse error = new ErrorResponse("User not found", e.getMessage());
return ResponseEntity.status(404).body(error);
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<ErrorResponse> handleGeneral(Exception e) {
ErrorResponse error = new ErrorResponse("Internal error", e.getMessage());
return ResponseEntity.status(500).body(error);
}
}
6. RESTful API
Q15: 什么是RESTful API?设计原则是什么?
答案:
REST: Representational State Transfer,表现层状态转移
设计原则:
- 统一接口: 使用标准HTTP方法
- 无状态: 每个请求独立,不依赖服务器状态
- 可缓存: 响应可以被缓存
- 分层系统: 客户端不需要知道服务器架构
- 按需代码: 可选,服务器可以返回可执行代码
HTTP方法对应:
@RestController
@RequestMapping("/api/users")
public class UserRestController {
@GetMapping // GET /api/users - 获取所有用户
public List<User> getAllUsers() { }
@GetMapping("/{id}") // GET /api/users/1 - 获取指定用户
public User getUser(@PathVariable Long id) { }
@PostMapping // POST /api/users - 创建用户
public User createUser(@RequestBody User user) { }
@PutMapping("/{id}") // PUT /api/users/1 - 更新用户
public User updateUser(@PathVariable Long id, @RequestBody User user) { }
@DeleteMapping("/{id}") // DELETE /api/users/1 - 删除用户
public void deleteUser(@PathVariable Long id) { }
}
Q16: HTTP状态码的含义?
答案:
- 1xx: 信息性状态码
- 2xx: 成功状态码
- 200 OK: 请求成功
- 201 Created: 资源创建成功
- 204 No Content: 成功但无返回内容
- 3xx: 重定向状态码
- 301 Moved Permanently: 永久重定向
- 302 Found: 临时重定向
- 4xx: 客户端错误
- 400 Bad Request: 请求错误
- 401 Unauthorized: 未授权
- 403 Forbidden: 禁止访问
- 404 Not Found: 资源不存在
- 5xx: 服务器错误
- 500 Internal Server Error: 服务器内部错误
- 502 Bad Gateway: 网关错误
7. 数据库连接
Q17: 什么是数据库连接池?为什么要使用?
答案:
连接池: 预先创建一定数量的数据库连接,放在池中供应用程序重复使用
优点:
- 减少连接创建/销毁的开销
- 控制并发连接数
- 提高系统性能
- 避免数据库连接泄漏
// 使用HikariCP连接池配置
@Configuration
public class DatabaseConfig {
@Bean
@ConfigurationProperties("spring.datasource.hikari")
public HikariDataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setMaximumPoolSize(20); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接
config.setConnectionTimeout(30000); // 连接超时
config.setIdleTimeout(600000); // 空闲超时
return new HikariDataSource(config);
}
}
Q18: 如何防止SQL注入?
答案:
SQL注入: 通过在输入中插入恶意SQL代码来攻击数据库
防护措施:
// 1. 使用PreparedStatement(推荐)
public User findUser(String username, String password) {
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
try (PreparedStatement stmt = connection.prepareStatement(sql)) {
stmt.setString(1, username); // 参数化查询
stmt.setString(2, password);
ResultSet rs = stmt.executeQuery();
// 处理结果
}
}
// 2. 输入验证
public boolean isValidInput(String input) {
return input != null &&
input.matches("^[a-zA-Z0-9_]+$") &&
input.length() <= 50;
}
// 3. 使用ORM框架
@Repository
public class UserRepository {
@Query("SELECT u FROM User u WHERE u.username = :username")
User findByUsername(@Param("username") String username);
}
8. 安全性
Q19: 如何实现用户认证和授权?
答案:
认证: 验证用户身份
授权: 验证用户权限
// 使用Spring Security
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
);
return http.build();
}
}
Q20: 什么是CSRF攻击?如何防护?
答案:
CSRF: Cross-Site Request Forgery,跨站请求伪造
攻击原理: 利用用户已登录的身份,在用户不知情的情况下执行恶意操作
防护措施:
// 1. CSRF Token
@Controller
public class UserController {
@PostMapping("/transfer")
public String transfer(@RequestParam String amount,
@RequestParam String account,
CsrfToken csrfToken) {
// Spring Security自动验证CSRF Token
return "success";
}
}
// 2. 验证Referer头
// 3. 使用SameSite Cookie属性
// 4. 双重提交Cookie
9. 性能优化
Q21: Web应用性能优化有哪些方法?
答案:
前端优化:
- 压缩CSS/JS文件
- 使用CDN
- 图片优化
- 浏览器缓存
后端优化:
// 1. 数据库优化
@Query("SELECT u FROM User u WHERE u.status = :status")
List<User> findActiveUsers(@Param("status") String status);
// 2. 缓存使用
@Cacheable("users")
public User findById(Long id) {
return userRepository.findById(id);
}
// 3. 异步处理
@Async
public CompletableFuture<String> processLongTask() {
// 长时间任务
return CompletableFuture.completedFuture("result");
}
// 4. 连接池优化
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
Q22: 如何进行负载均衡?
答案:
负载均衡: 将请求分发到多个服务器
实现方式:
- 硬件负载均衡: F5、A10等
- 软件负载均衡: Nginx、HAProxy
- 应用层负载均衡: Spring Cloud LoadBalancer
# Nginx负载均衡配置
upstream backend {
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 weight=1;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
10. 实际项目问题
Q23: 如何处理文件上传?
答案:
@Controller
public class FileUploadController {
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file");
return "redirect:/upload";
}
try {
// 保存文件
byte[] bytes = file.getBytes();
Path path = Paths.get("uploads/" + file.getOriginalFilename());
Files.write(path, bytes);
redirectAttributes.addFlashAttribute("message", "Upload successful");
} catch (IOException e) {
redirectAttributes.addFlashAttribute("message", "Upload failed");
}
return "redirect:/upload";
}
}
// 配置文件大小限制
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
Q24: 如何实现分页查询?
答案:
// 使用Spring Data JPA
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
Page<User> findByNameContaining(@Param("name") String name, Pageable pageable);
}
@RestController
public class UserController {
@GetMapping("/users")
public Page<User> getUsers(@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String name) {
Pageable pageable = PageRequest.of(page, size, Sort.by("id").descending());
if (name != null) {
return userRepository.findByNameContaining(name, pageable);
}
return userRepository.findAll(pageable);
}
}
浙公网安备 33010602011771号