当前位置: 首页 > news >正文

3.2.2.SpringMVC简介

3.2.2.SpringMVC

         Spring MVCModel-View-Controller)是一个用于构建Web应用程序的框架,属于Spring Framework的一部分。它采用了MVC设计模式,并通过Spring提供的各种功能来管理Web应用程序的请求、响应、控制和展示逻辑。Spring MVC的设计理念是解耦(Decoupling),即将业务逻辑、显示层、数据存储层等模块分开,使得各个模块之间的依赖最小化。

3.2.2.1.Spring MVC架构设计

         Spring MVC采用经典的MVCModel-View-Controller)设计模式:

               Model(模型):负责处理业务逻辑和数据。通常,Model是一个POJOPlain Old Java Object),代表应用程序的业务对象。在Spring MVC中,Model是传递给视图的数据部分。

                  View(视图):负责将Model中的数据呈现给用户,通常是JSPThymeleafFreeMarker等技术。View呈现最终的用户界面。

                  Controller(控制器):充当中介者,接收用户的请求,执行业务逻辑,并返回视图。它从Model获取数据,并将该数据传递给View

3.2.2.2.Spring MVC整体流程

         Spring MVC使用一个请求-响应的流程进行工作,基本流程如下:

                  客户端请求:浏览器发出HTTP请求,通常是通过URL(如 /home)发出。

                  DispatcherServlet接收请求:所有的请求都通过DispatcherServlet,它是Spring MVC的前端控制器。

                  请求映射到控制器:DispatcherServlet根据URL和映射配置,通过HandlerMapping来找到对应的控制器方法。

                  控制器处理请求:控制器处理请求,执行业务逻辑,并将数据放入Model中。

视图解析:返回的ModelAndView对象中包含了数据和视图名称,DispatcherServlet将根据视图名称通过ViewResolver解析得到最终视图。

                  渲染视图并返回响应:最后,DispatcherServletModel渲染到视图中,并通过HTTP响应返回给客户端。

3.2.2.3.Spring MVC核心组件

         Spring MVC的核心组件是:DispatcherServletHandlerMappingControllerModelAndViewViewResolver等。每个组件在框架中都扮演着重要角色,下面将详细介绍它们。

         1)DispatcherServlet(前端控制器)

                  DispatcherServletSpring MVC的核心,是所有请求的入口。它作为前端控制器(Front Controller)接收客户端请求,并将请求分发到相应的处理器(Controller)。

                  功能:

                          请求接收:接收来自客户端的HTTP请求。

                          请求分发:根据请求的URL和配置,选择合适的控制器来处理请求。

                          视图解析:请求处理后,返回视图(如JSPHTMLJSON等),将数据填充到视图中。

         2)HandlerMapping(请求映射)

                  HandlerMapping负责将用户的请求URL映射到具体的控制器方法。Spring MVC提供了多个HandlerMapping的实现来支持不同类型的请求映射:

                  AnnotationMethodHandlerMapping:通过@RequestMapping等注解映射请求。

                  SimpleUrlHandlerMapping:根据URL将请求映射到处理器Bean

                  BeanNameUrlHandlerMapping:根据Bean的名称进行映射。

         常见的映射方法:

                  @RequestMapping:最常用的映射注解,用于映射请求的URL

                  @GetMapping / @PostMapping / @PutMapping / @DeleteMapping:分别映射GETPOSTPUTDELETE请求。

         示例:

@Controller

public class MyController {

    @RequestMapping("/hello")

    public String sayHello() {

        return "hello";

    }

}

         3Controller(控制器)

                  控制器是Spring MVC的核心组件,负责处理用户的请求,执行相应的业务逻辑,并将数据传递给视图。控制器通常是一个Java类,带有@Controller注解,并包含多个请求处理方法。

         常用注解:

                  @Controller:标注为控制器类,负责处理用户请求。

                  @RequestMapping:标注方法来处理指定URL的请求。

                  @RestController:是@Controller@ResponseBody的组合,主要用于RESTful服务。

                  @RequestParam:用于接收请求参数。

                  @PathVariable:从路径中提取参数。

                  @ModelAttribute:将表单数据绑定到Java对象。

         示例:

@Controller

public class UserController {

    @RequestMapping("/user/{id}")

    public String getUser(@PathVariable("id") int userId, Model model) {

        User user = userService.getUserById(userId);

        model.addAttribute("user", user);

        return "userDetail";

    }

}

         4ModelAndView(模型与视图)

                  ModelAndViewSpring MVC中的一个重要对象,用来封装模型数据和视图信息。它包含两部分内容:

                  Model:封装业务数据(如通过model.addAttribute()传递的对象)。

                  View:指定渲染的视图(如JSPHTML等)。

@RequestMapping("/hello")

public ModelAndView sayHello() {

    ModelAndView modelAndView = new ModelAndView();

    modelAndView.setViewName("hello");

    modelAndView.addObject("message", "Hello, Spring MVC!");

    return modelAndView;

}

         5ViewResolver(视图解析器)

                  ViewResolver用于根据控制器返回的视图名称解析出最终的视图资源。Spring MVC提供了几种常见的视图解析器:

                  InternalResourceViewResolver:基于JSP的视图解析器。

                  ThymeleafViewResolver:支持Thymeleaf模板的视图解析器。

                  FreeMarkerViewResolver:支持FreeMarker的视图解析器。

@Bean

public InternalResourceViewResolver viewResolver() {

    InternalResourceViewResolver resolver = new InternalResourceViewResolver();

    resolver.setPrefix("/WEB-INF/views/");

    resolver.setSuffix(".jsp");

    return resolver;

}

         6HandlerInterceptor(处理器拦截器)

         HandlerInterceptor用于在请求处理过程的不同阶段执行逻辑,例如在请求到达控制器之前、控制器处理之后、视图渲染之前等。

         常用方法:

                  preHandle():请求处理之前执行。

                  postHandle():请求处理之后执行,但在视图渲染之前。

                  afterCompletion():视图渲染之后执行,清理资源。

public class MyInterceptor implements HandlerInterceptor {

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("Request pre-handling");

        return true;  // return true to continue the request, false to stop

    }

}

3.2.2.4.Spring MVC的优缺点

         优点:

                  松耦合设计:Spring MVC通过分离控制器、模型和视图,使得各个模块的耦合度较低,便于维护和扩展。

                  灵活性:Spring MVC支持多种视图技术(JSPThymeleafFreeMarker等),并且具有强大的扩展性,可以与其他Spring模块无缝集成。

                  支持RESTful架构:Spring MVC提供了非常好的支持RESTful Web服务的能力,通过@RestController等注解来简化RESTful API开发。

                  强大的注解支持:通过注解可以轻松进行请求映射、参数绑定、异常处理等操作,使得代码更加简洁和清晰。

         缺点:

                  配置繁琐:Spring MVC的配置文件较多,尤其是早期的XML配置方式可能使得项目配置较为繁杂。

                  学习曲线较高:Spring MVC的概念较为抽象,尤其是对于初学者,可能需要一定的时间来理解其工作流程。

                  性能开销:由于其基于Servlet的模型,性能可能不如某些轻量级框架(如Spring Boot嵌入式服务器)或者原生Servlet实现。

3.2.2.5.管理SpringBean

         Spring 中,Spring Bean 的管理 是核心的一部分。它涉及如何创建、配置、注入和销毁应用程序中的各种组件(Bean)。Spring 提供了强大且灵活的机制来管理 Bean 生命周期、作用域和依赖注入。做好 Spring Bean 的管理不仅有助于优化代码结构,也能提升系统的可维护性和扩展性。

         1)合理使用 Spring Bean 的作用域(Scope

                  Spring 提供了不同的作用域来管理Bean实例的生命周期。根据需求,选择合适的作用域非常重要。

                  singleton(单例):默认作用域。Spring 容器启动时会创建一个单例对象,并在整个应用程序的生命周期中共享这个实例。适用于无状态的 Bean

                  prototype(原型):每次请求都会创建一个新的 Bean 实例。适用于有状态的 Bean,或者需要动态创建的 Bean

                  request:在 Web 应用程序中,每一个 HTTP 请求都会创建一个新的 Bean 实例。适用于请求级别的 Bean

                  session:每一个 HTTP 会话都会创建一个新的 Bean 实例。适用于会话级别的 Bean

                  application:类似于 singleton,但是针对每个 Spring 容器实例,适用于上下文级别的管理。

         使用示例:

@Component

@Scope("singleton") // 默认作用域

public class MySingletonBean {

    // ... 业务逻辑

}

@Component

@Scope("prototype") // 每次注入时都创建新的实例

public class MyPrototypeBean {

    // ... 业务逻辑

}

         2)合理使用依赖注入(DI

                  Spring 提供了两种常用的依赖注入方式:构造器注入和Setter 注入。构造器注入被认为是更推荐的方式,尤其是在涉及到不可变依赖时。

                  构造器注入:使用构造函数注入依赖。这种方式的好处是可以让 Bean 在创建时就注入依赖,确保依赖是不可变的。

@Component

public class MyService {

    private final MyRepository repository;

    @Autowired

    public MyService(MyRepository repository) {

        this.repository = repository;

    }

    // 使用 repository

}

                  Setter 注入:通过 setter 方法来注入依赖。适合于可选的依赖,或当依赖关系较复杂时。

@Component

public class MyService {

    private MyRepository repository;

    @Autowired

    public void setRepository(MyRepository repository) {

        this.repository = repository;

    }

    // 使用 repository

}

                  字段注入:通过直接在字段上注入依赖。这种方式很简洁,但不推荐用于必须初始化的依赖。

@Component

public class MyService {

    @Autowired

    private MyRepository repository;

}

         推荐做法:

                  优先使用 构造器注入,可以确保依赖是不可变的,有助于避免空指针异常。

                  Setter 注入 适合于可选依赖。

                  避免过多使用 字段注入,因为它隐藏了依赖关系,降低了代码可读性和可测试性。

         3)使用 @Qualifier 解决 Bean 注入冲突

                  如果存在多个类型相同的 Bean 时,Spring 会不知道注入哪个 Bean,这时可以使用 @Qualifier 来指定要注入的具体 Bean

@Component

public class MyService {

    private final MyRepository repository;

    @Autowired

    public MyService(@Qualifier("myRepositoryImpl") MyRepository repository) {

        this.repository = repository;

    }

}

                  @Qualifier 注解与 @Autowired 配合使用,可以指定需要注入的具体 Bean 名称。

         4)合理使用 @Primary

                  如果有多个候选 Bean 可以注入,并且你希望 Spring 自动注入其中一个作为默认选择,可以使用 @Primary 注解。

@Component

@Primary

public class PrimaryRepository implements MyRepository {

    // 实现方法

}

@Component

public class SecondaryRepository implements MyRepository {

    // 实现方法

}

                  如果没有使用 @Qualifier 注解,Spring 会默认选择 @Primary 注解的 Bean

         5)初始化和销毁方法

                  Bean 的生命周期中,有时需要进行初始化和销毁操作。Spring 提供了两种方式来处理这些操作:

                  使用 @PostConstruct @PreDestroy 注解。

@Component

public class MyBean {

    @PostConstruct

    public void init() {

        // 初始化逻辑

    }

    @PreDestroy

    public void cleanup() {

        // 销毁逻辑

    }

}

                  使用 InitializingBean DisposableBean 接口。

@Component

public class MyBean implements InitializingBean, DisposableBean {

    @Override

    public void afterPropertiesSet() throws Exception {

        // 初始化逻辑

    }

    @Override

    public void destroy() throws Exception {

        // 销毁逻辑

    }

}

                  使用 @Bean 注解的 initMethod destroyMethod 属性(适用于 Java 配置方式)。

@Configuration

public class AppConfig {

    @Bean(initMethod = "init", destroyMethod = "cleanup")

    public MyBean myBean() {

        return new MyBean();

    }

}

                  这些方法可以帮助你在 Bean 的生命周期中执行一些初始化和销毁操作,确保资源的正确释放。

         6)使用 @Profile 管理环境配置

                  当你在不同的环境中使用 Spring 应用(例如开发、测试、生产环境)时,可以使用 @Profile 注解来管理 Bean 的激活状态。

@Component

@Profile("dev")

public class DevService implements MyService {

    // 开发环境的实现

}

@Component

@Profile("prod")

public class ProdService implements MyService {

    // 生产环境的实现

}

                  然后在配置文件中设置激活的 profile

                  application.properties

spring.profiles.active=dev

                  使用 @Profile 可以让你的 Spring 应用根据不同的环境注入不同的 Bean,提高灵活性。

         7)避免过度注入和过度依赖

                  避免过度注入:当一个 Bean 的依赖项过多时,可能是设计上的问题。尽量遵循 单一职责原则,拆分过大的类和过多依赖。

                  避免过度依赖:避免在类中注入大量的服务。可以通过 Service Layer Facade 模式来简化依赖关系。

         8AOP 管理 Bean 的行为

                  使用 AOP(面向切面编程) 可以帮助你对 Bean 的方法进行拦截和处理,增加日志、事务等功能,而不必修改原始代码。

@Aspect

@Component

public class LoggingAspect {

    @Before("execution(* com.example.service.*.*(..))")

    public void logBefore(JoinPoint joinPoint) {

        System.out.println("Before method: " + joinPoint.getSignature().getName());

    }

}

                  通过 AOP 管理 Bean 的横切关注点,可以提高代码的复用性和解耦性。

3.2.2.6.避免重复Bean

         在 Spring 中,避免重复的 Bean 是一个重要的设计考量,尤其是在复杂的项目中。重复的 Bean 会导致依赖注入错误、程序逻辑不清晰,甚至可能引发 NoUniqueBeanDefinitionException 错误(当存在多个相同类型的 Bean 时)。为了有效避免重复 Bean 的问题,可以采取以下几种策略:

         1)使用 @Qualifier 注解指定 Bean 名称

                  当有多个类型相同的 Bean 时,Spring 会默认选择第一个匹配的 Bean。为了明确指定使用哪个 Bean,可以使用 @Qualifier 注解指定具体的 Bean 名称。

@Component("beanA")

public class MyBeanA implements MyBean {

    // Bean A 实现

}

@Component("beanB")

public class MyBeanB implements MyBean {

    // Bean B 实现

}

@Component

public class MyService {

    @Autowired

    @Qualifier("beanA")

    private MyBean myBean; // 明确指定注入 beanA

}

         优点:

                  使用 @Qualifier 注解,可以显式地告诉 Spring 使用哪个 Bean,避免了多个同类型 Bean 的冲突。

         注意:

                  @Qualifier 必须和 @Autowired 一起使用,否则 Spring 无法知道注入哪个 Bean

         2)使用 @Primary 注解指定默认 Bean

                  当你有多个同类型的 Bean 时,可以通过 @Primary 注解指定一个默认的 Bean。当没有明确指定的情况下,Spring 会选择 @Primary 注解的 Bean

@Component

@Primary

public class MyBeanA implements MyBean {

    // 实现 A

}

@Component

public class MyBeanB implements MyBean {

    // 实现 B

}

         优点:

                  @Primary 注解让你在多个同类型的 Bean 中指定一个默认的 Bean

                  可以减少使用 @Qualifier 的需求,简化配置。

         注意:

                  仅适用于没有明确指定的场景,如果明确使用 @Qualifier@Primary 不会生效。

         3)避免 Bean 的重复定义(使用唯一 Bean 名称)

                  Spring 中,Bean 的名字默认是类名的首字母小写。确保在 @Component@Service@Repository 等注解中指定唯一的 Bean 名称,避免多个 Bean 使用相同名称。

@Component("myUniqueBeanName")

public class MyService {

    // 业务逻辑

}

         优点:

                  保证了 Bean 的名称唯一性,避免了重复的 Bean 名称问题。

         注意:

                  在复杂的项目中,建议使用规范的命名策略来管理 Bean 名称,避免出现冲突。

         4)使用 @Profile 来分离不同环境的 Bean 定义

                  如果你在开发、测试和生产环境中需要使用不同的 Bean,可以通过 @Profile 注解来管理 Bean 的加载,避免不同环境中重复加载相同的 Bean

@Component

@Profile("dev")

public class DevService implements MyService {

    // 开发环境的实现

}

@Component

@Profile("prod")

public class ProdService implements MyService {

    // 生产环境的实现

}

         优点:

                  @Profile 可以帮助你在不同的环境中加载不同的 Bean,避免不同环境中的重复 Bean

         注意:

                  使用@Profile 时,确保配置文件中正确指定了激活的 Profile,例如:spring.profiles.active=dev

         5)避免多个配置文件定义相同 Bean

在使用 Java 配置方式时,可能会在不同的 @Configuration 文件中定义相同类型的 Bean。确保在配置类中没有重复定义相同的 Bean

@Configuration

public class ConfigA {

    @Bean

    public MyService myServiceA() {

        return new MyServiceImpl();

    }

}

@Configuration

public class ConfigB {

    @Bean

    public MyService myServiceB() {

        return new MyServiceImpl();

    }

}

         这种配置容易引发 Bean 的重复定义错误,尤其是当两个配置文件中都定义了相同类型的 Bean 时。为了解决这个问题,可以:

                  使用不同的名称来区分 Bean

                  使用条件注解(如 @Profile)来确保只在特定环境中加载某个 Bean

         6)使用 @Import 来导入 Bean 定义

                  当你有多个配置类时,确保每个配置类的责任清晰且不会重复。如果需要将多个配置类合并,可以使用 @Import 注解导入其他配置类。

@Configuration

@Import(OtherConfig.class)

public class MainConfig {

    // 主配置

}

                  优点:

                          @Import 可以帮助你清晰地管理多个配置类,避免重复 Bean 定义。

         7)手动注册 Bean 时检查重复

                  如果你使用了 AnnotationConfigApplicationContext GenericWebApplicationContext 等手动注册 Bean 的方式,在注册 Bean 时,要特别注意检查是否存在重复定义的情况。

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

context.register(MyConfig.class);

context.refresh();

// 检查是否有重复的 Bean

if (context.getBeansOfType(MyService.class).size() > 1) {

    throw new IllegalStateException("存在多个 MyService Bean 定义!");

}

         优点:

                  这种方法可以帮助你在程序启动时主动检查是否有重复的 Bean 定义。

         8)避免自动扫描重复包路径

                  当使用组件扫描时,确保没有扫描到重复的包路径。如果你的项目结构很复杂,可能会无意中扫描到重复的包或类,导致同样的 Bean 被创建多次。可以通过 @ComponentScan 来指定扫描的范围,避免重复扫描。

@ComponentScan(basePackages = "com.example.services")

         优点:

                  通过限制 @ComponentScan 的范围,避免多次扫描相同的 Bean 类。

         9)检查第三方库中的 Bean 定义

                  如果你使用了第三方库(如 Spring Boot Starter),它们有时会自动注册一些 Bean。如果这些 Bean 与你的应用中的 Bean 重名或类型相同,可能会引发冲突。可以通过 @Primary @Qualifier 解决,或者在配置文件中禁用某些自动装配。

         10)避免不必要的注入

                  避免过度注入 Bean,特别是在某些类中注入大量不同类型的 Bean。如果一个类有过多的依赖项,它可能是一个 过度设计的类,应考虑重新设计或者拆分类。

         11)必要时使用文档来记录、管理Bean

                  参考:Bean管理文档

http://www.dtcms.com/a/557019.html

相关文章:

  • 帝国cms影视网站模板网站app的区别
  • Rust 结构体方法(Methods):为数据附加行为
  • Android Cursor AI代码编辑器
  • git add 一条命令太长换行
  • 数据仓库与传统数据库开发工具架构差异:Web 架构 vs 客户端工具
  • 百度网站快速排名公司营销策略ppt模板
  • 外骨骼机器人:下肢助力走路,减负 30% 的硬核机械魔法
  • Linux基础I/O-打开新世界的大门:文件描述符的“分身术”与高级重定向
  • 用Python来学微积分25-微积分中的函数奥秘:单调性、极值与最值
  • 免费信息网站排名做动画视频的网站有哪些
  • 从零搭建多子网 DHCP 服务:CentOS 双网卡多作用域实战与原理解析
  • 再议c语言的直接访问和间接访问
  • 从零开始的QT开发指南:(一)背景、特性与环境搭建
  • 网站购物车实现wordpress怎么调用分类的文章
  • oracle 19c搭建dataguard(ADG)全过程
  • 网站集群建设方案兰州免费网站建设
  • 低成本低成本低成本
  • 机器学习核心概念详解(回归、分类和聚类)
  • 基于SpringBoot+Vue的零食仓库管理系统(redis缓存、摄像头、扫描二维码)
  • ant design 做网站wordpress 上传文件名
  • 跨网络互联技术(加密算法)
  • uniapp/flutter中实现苹果IOS 26 毛玻璃效果、跟随滑动放大动画
  • I.MX6U 启动方式详解
  • flutter 生命周期管理:从 Widget 到 State 的完整解析
  • Python Selenium详解:从入门到实战,Web自动化的“瑞士军刀”
  • 正品海外购网站有哪些郑州网络推广软件
  • 腾讯网站开发规范加强档案网站建设
  • 鸿蒙原生系列之手势事件自定义处理
  • OkHttp不同类型的HTTP请求的示例
  • 【Java Web学习 | 第四篇】CSS(3) -背景