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);
    }
}