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號