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 → 执行

详细过程:

  1. 客户端请求JSP页面
  2. Web容器将JSP翻译成Servlet源码
  3. 编译Servlet源码生成.class文件
  4. 加载并执行Servlet
  5. 返回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的工作原理是什么?

答案:

  1. 客户端首次访问,服务器创建Session对象
  2. 服务器生成唯一的SessionID
  3. 将SessionID通过Cookie发送给客户端
  4. 客户端后续请求携带SessionID
  5. 服务器根据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);
    }
}