一、什么是SpringMvc?
Spring MVC(Model-View-Controller)是 Spring 框架的一部分,它是一个基于 Java 的框架,用于构建 Web 应用程序。Spring MVC 提供了一种强大且灵活的方式来开发 Web 应用,它遵循了 MVC 设计模式,将应用程序的业务逻辑、视图和用户输入分离开来,从而使应用程序更易于管理和扩展。
Spring MVC 的核心概念
-
模型(Model):
- 职责:代表应用程序的数据和业务逻辑。
- 组成:通常包括 Java 对象(POJO)、数据访问层(DAO)和服务层(Service)。
- 功能:处理业务逻辑和数据访问,与视图层分离。
-
视图(View):
- 职责:负责呈现数据给用户。
- 组成:视图可以是 JSP(JavaServer Pages)、Thymeleaf、FreeMarker 等模板引擎。
- 功能:展示模型数据,将用户界面与业务逻辑分开。
-
控制器(Controller):
- 职责:处理用户请求,调用相应的业务逻辑,选择视图来呈现结果。
- 组成:Spring MVC 中的控制器通常是使用
@Controller
注解的类中的方法。 - 功能:接收用户请求,调用业务层进行处理,返回模型数据和视图名称。
二、说说SpringMVC的优点?
Spring MVC 作为一个基于 Java 的 Web 框架,具有多种优点,使得它在开发 Web 应用程序时非常受欢迎。以下是 Spring MVC 的主要优点(1,2,3,8一定要答出来):
1. 清晰的分层架构
- MVC 模式:Spring MVC 实现了 Model-View-Controller(模型-视图-控制器)设计模式,将业务逻辑、视图和用户输入分离,提升了应用的可维护性和扩展性。
- 松耦合:通过将控制器、模型和视图分离,减少了各层之间的耦合,使得应用程序更容易理解和修改。
2. 灵活性
- 多视图技术支持:Spring MVC 支持多种视图技术,如 JSP、Thymeleaf、FreeMarker、Velocity 等,开发者可以根据需求选择合适的视图技术。
- 多种请求处理方式:支持多种请求处理方式,包括传统的基于注解的方式(如
@RequestMapping
、@GetMapping
)和更灵活的配置方式。
3. 强大的数据绑定
- 表单数据绑定:Spring MVC 提供了强大的数据绑定功能,可以将 HTTP 请求中的参数直接绑定到 Java 对象属性中,简化了表单处理。
- 数据验证:通过 JSR-303/JSR-380(如 Hibernate Validator)实现数据验证,能够自动校验用户输入的合法性,并提供自定义错误信息。
4. 支持 RESTful 风格
- RESTful API:Spring MVC 支持创建 RESTful 风格的 Web 服务,提供了
@RestController
和@RequestMapping
等注解,简化了 RESTful API 的开发。
5. 强大的支持和集成
- 集成 Spring 生态系统:与 Spring 框架的其他组件(如 Spring Security、Spring Data、Spring Boot)无缝集成,能够利用整个 Spring 生态系统的功能。
- 支持异步处理:支持异步请求处理,通过
@Async
注解和 Spring WebFlux 等组件处理异步任务,提高应用性能和响应速度。
6. 高度可配置
- 灵活的配置:支持 XML 配置、注解配置以及 Java 配置等多种方式,开发者可以根据项目需求选择最合适的配置方式。
- 自定义处理器和拦截器:允许开发者自定义处理器、拦截器和异常处理器,以满足特定的需求。
7. 丰富的开发工具
- 集成开发环境(IDE)支持:支持主流的 IDE(如 IntelliJ IDEA、Eclipse)中的 Spring 插件,提供了丰富的开发和调试工具。
- 自动化测试:支持自动化测试,能够与 JUnit 和其他测试框架集成,简化测试过程。
8. 高性能和可扩展性
- 缓存支持:可以通过 Spring 的缓存抽象来提升性能,减少数据库查询和计算开销。
- 扩展性:提供了丰富的扩展点,允许开发者根据需求扩展和定制框架的功能。
9. 社区支持和文档
- 广泛的社区支持:Spring MVC 拥有一个活跃的开源社区,提供了大量的文档、教程和示例代码,帮助开发者解决问题。
- 丰富的文档:官方文档详细且易于理解,为开发者提供了全面的参考资料。
三、说说SpringMVC的原理/工作流程?
1)用户发送请求至前端控制器DispatcherServlet。
2)DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。
3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象 及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4)DispatcherServlet 调用 HandlerAdapter 处理器适配器。
5)HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)。
6)Controller 执行完成返回ModelAndView。
7)HandlerAdapter 将 controller 执行结果 ModelAndView 返回给 DispatcherServlet。
8)DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。
9)ViewReslover 解析后返回具体View。
10)DispatcherServlet 根据 View 进行渲染视图(即将模型数据填充至视图中)。
11)DispatcherServlet 响应用户。
四、SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决
是的,Spring MVC 的控制器默认是单例模式。这意味着在整个应用程序生命周期内,Spring 容器只会创建一个控制器实例,并且这个实例会被多个线程共享和使用。这种单例模式可以提高性能,因为控制器的实例化和销毁操作只发生一次。
1.单例模式带来的问题
-
线程安全问题:由于单例控制器实例会被多个线程并发访问,如果控制器中包含了非线程安全的成员变量,那么可能会导致线程安全问题。例如,如果控制器中有一个普通字段,它会被所有线程共享,这可能导致数据不一致或竞争条件。
@Controller public class MyController { private int counter = 0; // 非线程安全的字段 @RequestMapping("/increment") public String increment() { counter++; // 多线程下可能导致数据不一致 return "counterValue"; } }
-
状态管理:由于控制器是单例的,如果控制器持有状态(如上例中的
counter
变量),这些状态会被所有请求共享,这可能导致意想不到的行为。
2.解决方法
-
避免在控制器中使用可变状态:
- 尽量避免在控制器中定义可变的成员变量。如果需要状态,请将状态保存在方法的局部变量中,或者使用线程安全的方式处理。
@Controller public class MyController { @RequestMapping("/increment") public String increment() { int counter = 0; // 方法局部变量,不会被共享 counter++; return "counterValue"; } }
-
使用线程安全的数据结构:
- 如果确实需要在控制器中使用共享状态,请使用线程安全的数据结构,如
ConcurrentHashMap
、AtomicInteger
等。
@Controller public class MyController { private AtomicInteger counter = new AtomicInteger(0); // 线程安全的计数器 @RequestMapping("/increment") public String increment() { counter.incrementAndGet(); // 线程安全操作 return "counterValue"; } }
- 如果确实需要在控制器中使用共享状态,请使用线程安全的数据结构,如
-
使用
@Scope("prototype")
注解:- 如果控制器需要使用非线程安全的成员变量,可以将控制器的作用域从默认的单例(singleton)改为原型(prototype),这样每次请求都会创建一个新的控制器实例。不过这种方式会影响性能,因为每次请求都会创建新的实例。
@Controller @Scope("prototype") public class MyController { private int counter = 0; // 非线程安全的字段,但每次请求都有自己的实例 @RequestMapping("/increment") public String increment() { counter++; return "counterValue"; } }
五、SpringMVC用什么对象向前台传送数据?
实际上有多个可以向前台传送数据的对象,如果只让答出一个,答ModelMap
1. Model 和 ModelMap
Model
和 ModelMap
是 Spring MVC 提供的两个接口,用于在控制器中传递数据到视图层。它们的使用方式相似,都是通过将数据添加到模型中,然后由视图层进行渲染。
@Controller
public class MyController {
@RequestMapping("/welcome")
public String welcome(Model model) {
model.addAttribute("message", "Welcome to Spring MVC!");
return "welcomeView"; // 视图名称
}
}
@Controller
public class MyController {
@RequestMapping("/welcome")
public String welcome(ModelMap modelMap) {
modelMap.addAttribute("message", "Welcome to Spring MVC!");
return "welcomeView"; // 视图名称
}
}
2. ModelAndView
ModelAndView
是一个组合对象,既包含模型数据又包含视图名称。通过 ModelAndView
,可以同时设置模型数据和视图信息。
@Controller
public class MyController {
@RequestMapping("/welcome")
public ModelAndView welcome() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("welcomeView"); // 视图名称
modelAndView.addObject("message", "Welcome to Spring MVC!"); // 模型数据
return modelAndView;
}
}
3. Map
Spring MVC 允许使用 Map
作为参数来传递数据到视图层。Map
的用法类似于 Model
和 ModelMap
,但更加通用。
@Controller
public class MyController {
@RequestMapping("/welcome")
public String welcome(Map<String, Object> map) {
map.put("message", "Welcome to Spring MVC!");
return "welcomeView"; // 视图名称
}
}
4. @ModelAttribute
@ModelAttribute
注解用于绑定请求参数到方法参数或将方法返回值添加到模型中。可以用在方法参数上,也可以用在方法上。
@Controller
public class MyController {
@RequestMapping("/welcome")
public String welcome(@ModelAttribute("message") String message) {
return "welcomeView"; // 视图名称
}
@ModelAttribute("message")
public String addMessage() {
return "Welcome to Spring MVC!";
}
}
5. @SessionAttributes 和 @RequestAttributes
@SessionAttributes
用于将模型中的属性放入 HttpSession
中,这样它们在多个请求之间可以保持。
@Controller
@SessionAttributes("user")
public class MyController {
@RequestMapping("/user")
public String user(Model model) {
model.addAttribute("user", new User("John", "Doe"));
return "userView";
}
}
@RequestAttributes
则是将属性存储在当前请求中,可以在后续的处理过程中使用。
@Controller
public class MyController {
@RequestMapping("/request")
public String request(HttpServletRequest request) {
request.setAttribute("message", "This is a request attribute");
return "requestView";
}
}
6. RedirectAttributes
用于重定向时传递参数,特别适合在 Post-Redirect-Get 模式下使用。
@Controller
public class MyController {
@PostMapping("/submit")
public String submit(RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("message", "Submission successful!");
return "redirect:/result";
}
}
六、SpringMVC中过滤器与拦截器的区别
1. 拦截器是基于java的反射机制,而过滤器是基于函数回调的。
2. 过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。
3.因为拦截器更接近业务系统,所以拦截器主要用来实现项目中的业务判断的,比如:登录判断、权限判断、日志记录等业务。
而过滤器通常是用来实现通用功能过滤的,比如:敏感词过滤、字符集编码设置、响应数据压缩等功能。