一、Spring两大核心
1.IOC 与 DI
- 思想:IoC(Inverse of Control:控制反转)是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。
- IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。
- 将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。
- IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件xml/注解即可,完全不用考虑对象是如何被创建出来的。
- IoC 是一个概念,是一种思想,其实现方式多种多样。Spring 框架使用依赖注入(DI)实现 IoC
- 定义: DI—Dependency Injection ,即 “ 依赖注入 ”。依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
- 通常,依赖注入可以通过三种方式完成,即:构造函数注入,setter 注入,接口注入。在 Spring Framework 中,仅使用构造函数和 setter 注入。
- Spring IOC 作用:管理项目中的Java bean的生命周期
- 为什么需要:在项目运行阶段,程序中需要很多对象来完成整体业务
- 容器和代码之间的代码控制权反转,代码中不需要明文调用来方法得到对象, 只需要声明该类需要什么类型的对象即可
2.AOP
AOP:面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术
-
AOP的作用
-
AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
-
主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入(拦截器---切入点),增强代码的可读性和可维护性。
-
简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。
-
- Spring AOP的术语
- Spring AOP 通知分类
-
定义切点
-
通过execution函数来定义切点
-
语法:execution(访问修饰符 返回类型 方法名 参数 异常)
-
具体事例说明:(..):表示方法参数列表,其中..
表示任意数量和类型的参数。
- 匹配所有类public方法:execution(public * *(..))第一个*表示返回值 ..表示任意个任意类型的参数
- 匹配指定包下所有类所有方法: execution(* cn.xxx.dao.*.*(..)) 第一个想*表示忽略权限和返回值类型
- 匹配指定包下所有类所有方法:execution(* cn.xxx.dao..*(..))包含子包
- 匹配指定类所有方法: execution(* cn.xxx.service.UserService.*(..))
- 匹配实现特定接口所有类方法 : execution(* cn.xxx.dao.GenericDAO+.*(..))
- 匹配所有save开头的方法: execution(* save*(..))
- 匹配某个指定的方法: execution(* com.yh.dao.StudentService.save(..))
二、代理模式
1.静态代理
2.动态模式
2.1 JDK动态代理(组合,被代理类必须实现接口)
-
代码实现
-
定义接口
-
-
定义代理处理器
//正常执行业务逻辑
//作用是调用被代理对象的实际方法,但并不是调用被代理对象的所有方法。它只会调用当前被拦截的方法。
- result = method.invoke(proxyedObj, args);
-
声明代理工厂
-
方法测试
2.2 CGLIB动态代理(继承,被代理类不能被final修饰)
-
定义业务类
-
定义代理业务
-
代码测试
三、 拦截器
1.拦截器(Interceptor)是 Spring MVC 提供的一种强大的功能组件。它可以对用户请求进行拦截,并在请求进入控制器(Controller)之前handler、控制器处理完请求后、甚至是渲染视图后,执行一些指定的操作。
//正常执行业务逻辑
//作用是调用被代理对象的实际方法,但并不是调用被代理对象的所有方法。它只会调用当前被拦截的方法。
- result = method.invoke(proxyedObj, args);
2.在 Spring MVC 中,拦截器的作用与 Servlet 中的过滤器类似,它主要用于拦截用户请求并做相应的处理,例如通过拦截器,我们可以执行权限验证、记录请求信息日志、判断用户是否已登录等操作。
- 1.拦截器定义与逻辑代码
- preHandle() 返回值判断是否会进行后续操作,true会继续(不拦截),false不会继续(进行拦截)
@Component
public class Interceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("-----preHandle:返回值判断是否会进行后续操作");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("--------postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("--------afterCompletion");}
}
- 2. 配置拦截器
-
拦截器的执行流程
-
- 当请求的路径与拦截器拦截的路径相匹配时,程序会先执行拦截器类(MyInterceptor)的 preHandle() 方法。若该方法返回值为 true,则继续向下执行 Controller(控制器)中的方法,否则将不再向下执行;
-
控制器方法对请求进行处理;
-
调用拦截器的 postHandle() 方法,此时我们可以对请求域中的模型(Model)数据和视图做出进一步的修改;
-
通过 DispatcherServlet 的 render() 方法对视图进行渲染;
-
调用拦截器的 afterCompletion () 方法,完成资源清理、日志记录等工作。
-
多个拦截器的执行流程。
-
在大型的企业级项目中,通常都不会只有一个拦截器,开发人员可能会定义许多不同的拦截器来实现不同的功能。
-
四、springmvc 运行原理
Spring MVC 使用 MVC 架构模式的思想,将 Web 应用进行职责解构,把一个复杂的 Web 应用划分成模型(Model)、控制器(Contorller)以及视图(View)三层,有效地简化了 Web 应用的开发,降低了出错风险,同时也方便了开发人员之间的分工配合
用户通过浏览器发起一个 HTTP 请求,该请求会被 DispatcherServlet(前端控制器)拦截;
DispatcherServlet 调用 HandlerMapping(处理器映射器)找到具体的处理器(Handler)及拦截器,
HandlerMapping将Handler以 HandlerExecutionChain 执行链的形式返回给 DispatcherServlet。
DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(即 Controller 控制器)对请求进行处理;
Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC 的底层对象,包括 Model 数据模型和 View 视图信息);
HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析;
ViewResolver 解析完成后,会将 View 视图并返回给 DispatcherServlet;
DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图);
视图负责将结果显示到浏览器(客户端)。
-
Spring MVC 常用(核心)组件
-
Spring MVC 的常用组件共有 5 个,它们分别是: DispatcherServlet(前端控制器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(处理器)、ViewResolver(视图解析器)
-
五、springmvc异常处理机制
-
如果程序发生异常,Spring MVC 会按照 ExceptionHandlerExceptionResolver → ResponseStatusExceptionResolver → DefaultHandlerExceptionResolver 的顺序,依次使用这三个异常处理器对异常进行解析,直到完成对异常的解析工作为止。
-
@ExceptionHandler 注解(局部处理)
-
全局异常处理
-
@ExceptionHandler 方法定义在一个使用了 @ControllerAdvice 注解的类中。使用 @ControllerAdvice 注解的类可以包含多个不同的带有 @ExceptionHandler 注解的方法
-
六、相关注解
1、@Controller 与 @RestController 区别:
@Controller:返回值优先作为地址,如果要返回json,需要添加@ResponseBody
- @Controller是Spring MVC中用于定义传统Web控制器的注解。它通常与视图技术(如JSP、Thymeleaf)一起使用,返回一个视图名称,由视图解析器解析并渲染视图。
特点:
- 返回视图名称:@Controller 通常返回一个地址(如
"home"
),由视图解析器解析为具体的视图实现(如JSP、Thymeleaf模板)。 - 使用@RequestMapping:方法上通常使用@RequestMapping注解来映射请求。
- 使用
@ResponseBody
:如果需要返回JSON、XML等数据,可以在方法上添加@ResponseBody
注解,将返回值直接写入HTTP响应体。
@RestController:返回值类型json
@RestController 是Spring 4.0引入的一个组合注解,它结合了@Controller
和@ResponseBody
的功能。主要用于构建RESTful Web服务,返回JSON、XML等数据,而不是视图。
特点
- 直接返回数据:@RestController 的方法默认返回数据,而不是视图名称。返回值会自动转换为JSON或XML格式,并写入HTTP响应体。
- 简化注解:不需要在每个方法上添加
@ResponseBody
注解,简化了代码。 - 适用于RESTful服务:非常适合构建RESTful Web服务,返回纯数据。
2. JSON 数据转换
-
Spring MVC 为我们提供了两个十分重要的与 JSON 格式转换相关的注解,它们分别是 @RequestBody 和 @ResponseBody。
3. Spring MVC实现RESTful
-
在 Spring MVC 中,我们可以通过 @RequestMapping +@PathVariable 注解的方式,来实现 RESTful 风格的请求。
-
通过@RequestMapping 注解的路径设置
-
当请求中携带的参数是通过请求路径传递到服务器中时,我们就可以在 @RequestMapping 注解的 value 属性中通过占位符 {xxx} 来表示传递的参数
-
@RequestMapping("/testRest/{id}/{username}")
-
value(路径) 属性中占位符的位置应当与请求 URL 中参数的位置保持一致,否则会出现传错参数的情况。
-
-
通过 @PathVariable 注解绑定参数
-
我们可以在控制器方法的形参位置通过 @PathVariable 注解,将占位符 {xxx} 所表示的参数赋值给指定的形参。
-
@RestController
//处理全局的异常
@RequestMapping("staff")
public class StaffController {@GetMapping("staff")public CommonResult getList(Staff staff){List<Staff> list =null;System.out.println("获取数据");return CommonResult.success(list);}@PostMapping("staff")public CommonResult addStaff(Staff staff){System.out.println("新增数据");return CommonResult.success();}@PutMapping("staff")public CommonResult update(){System.out.println("编辑数据");return CommonResult.success();}@DeleteMapping("staff/{id}")public CommonResult delStaff(@PathVariable int id){System.out.println("删除数据"+id);return CommonResult.success();}@GetMapping("ex")public CommonResult ex(){System.out.println("+++++++++++++++++");int a=12/0;return CommonResult.success();}@ExceptionHandler(Exception.class)@ResponseBodypublic CommonResult exh(){System.out.println("----------------");return CommonResult.success(200,"稍有问题");}
}
七、接收前端的参数
-
接收前端的参数 方法的参数名称和前台传递的参数名一样 (一)、获取表单数据1. Map接收(非常灵活,但不严谨,有安全问题)2.使用封装对象来接收参数 程序当中只接收我们所需要的数据 (二)、获取地址栏上的参数
-
查询参数: 从URL的查询参数中获取值,使用
@RequestParam
注解。@GetMapping("/users") public User getUserById(@RequestParam Long id) {// 从查询参数中获取idreturn userService.getUserById(id); }
请求示例:
GET /users?id=123
-
请求体: 从HTTP请求体中获取值,使用
@RequestBody
注解。@PostMapping("/users") public User createUser(@RequestBody User user) {// 从请求体中获取User对象return userService.createUser(user); }
请求示例:
POST /users
,请求体中包含User对象的JSON数据。
3.加 @PathVariable
注解
如果加 @PathVariable
注解,方法参数将从URL路径中获取值。
@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {// 从URL路径中获取idreturn userService.getUserById(id);
}
请求示例:GET /users/123
//@Controller 将返回值优先当做地址//@RestController//REST 请求资源状态转换//get post put deldte//get user/1 获取 user/get//get user/……(参数) 新增 user/add//delete user/1 删除 user/del?id=1//put user…… 编辑 user/edit
@RestController
@RequestMapping("user")public class EasyDController {//接收前端的参数 方法的参数名称和前台传递的参数名一样//一、获取表单数据@RequestMapping("parama")public String praamA(String name){return "Stringmvc接收到的参数是:"+name;}//同时接收多个参数:2种方式// 1. Map接收(非常灵活,但不严谨,有安全问题)@RequestMapping("paramb")//加注解:变成接收参数的容器,将参数注入到map里面,如果没有就认为是创建一个对象public Map praamb(@RequestParam Map params){return params;}//2.使用封装对象来接收参数 程序当中只接收我们所需要的数据@RequestMapping("paramc")public Staff paramc(Staff staff){return staff;}//二、获取地址栏上的参数@RequestMapping("paramd/{id}")//获取地址栏上的参数public String paramd(@PathVariable Integer id, HttpServletRequest request){String username = request.getParameter("username");return "接收到的参数是:"+id+"---username:"+username;}/*作用域对象:pageHttpServletRequestHttpSeesion(ServletContext)application*/@RequestMapping("easyaa")public String easyaa(){return "easyaa method";}}
八、请求转发与重定向区别
/*请求转发:同一个服务器中不同的服务进行转发,浏览器发送一个请求,可以转发到到本项目中受保护的资源(例如:WEB-INF)(地址栏不发生变化) forward转发是request对象执行forward方法进行转发(默认是转发)* 重定向: 可以在不同的服务器之间跳转,浏览器发送多次请求对象(地址栏发生变化) redirect重定向是通过response对象通知浏览器重新访问,使用redirect来重定向*/
@Controller
public class EasyEController {@RequestMapping("methoda")public String methodA(){System.out.println("------methodA");//return "forward:/methodb";return "redirect:/methodb";}@RequestMapping("methodb")//将返回对象直接返回到响应体里面@ResponseBodypublic String methodB(){System.out.println("------methodB");return "this is methodB";}@RequestMapping("methodc")public String methodC(){return "index.jsp";}
}