实现 Spring 业务层与控制器层配置的独立化管理
架构分层概述
在传统的 Java EE 开发中,为了保持职责单一性和维护性,通常会将 Spring 的核心应用上下文(负责业务逻辑、数据访问)与 Spring MVC 的 DispatcherServlet 上下文(负责请求分发、视图解析)进行物理隔离。这种模式利用父子容器的继承关系,避免重复扫描资源,同时确保服务层对象能被控制器层正确复用。
核心启动配置 (web.xml)
首先需要定义部署描述符,初始化两个不同的 ApplicationContext 实例。通过 ContextLoaderListener 加载父容器配置,通过 DispatcherServlet 加载子容器配置。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:bean-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
业务容器配置 (bean-context.xml)
此文件仅用于注册非控制层面的组件。利用 component-scan 指令排除带有 @Controller 注解的类,防止 Bean 重复定义。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.demo.mvc" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
服务层实现
创建一个典型的服务类,处理具体的业务逻辑。该 Bean 将会被注册到父容器中。
package com.demo.mvc.service;
import org.springframework.stereotype.Service;
@Service
public class UserGreetingService {
public String generateMessage(String userName) {
return "欢迎用户:" + userName + " 登录系统";
}
}
MVC 容器配置 (dispatcher-config.xml)
这是前端控制器的专属配置文件。我们需要配置只扫描控制器,并开启基于注解的请求映射以及视图解析功能。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.demo.mvc" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/views/">
<property name="suffix" value=".jsp">
</bean>
</beans>
控制器层实现
控制器位于子容器作用域内,可以通过依赖注入的方式获取父容器中的 Service 对象。这里使用标准的响应式编程模型来处理 GET 请求。
package com.demo.mvc.controller;
import com.demo.mvc.service.UserGreetingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/main")
public class MainPageController {
private final UserGreetingService userService;
// 构造函数注入方式更利于测试和不可变对象
@Autowired
public MainPageController(UserGreetingService userService) {
this.userService = userService;
}
@GetMapping("/entry")
public String handleEntryRequest(Model model) {
// 调用业务逻辑
String message = userService.generateMessage("Admin");
// 将数据放入 Model 供视图展示
model.addAttribute("welcomeMsg", message);
System.out.println("业务处理完成:" + message);
return "index"; // 对应 /views/index.jsp
}
}
运行验证
启动 Tomcat 服务器后,在浏览器地址栏输入:
http://localhost:8080/main/entry
页面应正常渲染,控制台日志输出:业务处理完成:欢迎用户:Admin 登录系统,表明父子容器的依赖注入机制工作正常。