本文共 18516 字,大约阅读时间需要 61 分钟。
一、Spring入门
1. Spring中的基本概念
IOC(是一种思想)和DI(对这种思想实现的一种描述)
2.框架编写流程:
1)导包
2)写配置
spring的配置文件中,集合了spring的【在eclipse中配置xml文件】
3)测试
3.依赖注入(DI)的理解:
4. AOP(面向切面编程)
-
概念:是一种基于OOP基础之上新的编程思想,指在程序运行期间,将某段代码动态地切入到指定方法的指定位置的这种编程方式。
-
场景:计算器运行计算方法的时候进行日志记录;
加日志记录:
1)直接编写在方法内部(不推荐,维护困难)
日志记录:系统的辅助功能
业务逻辑:核心功能
耦合;
2)我们希望:
业务逻辑:核心功能;日志模块:在核心功能运行时,自动地动态加上;
运行的时候,日志功能可以加上;
可以使用动态代理来将日志代码动态地在目标方法执行前后进行执行;
动态代理:
1.可以利用Spring一句代码都不写地去创建动态代理;
实现简单,而且没有强制要求目标对象必须实现接口;
将某段代码(日志)动态地切入(不把日志代码写死在业务逻辑方法中)到指定方法的指定位置(方法的开始、返回、结束、异常)进行运行的这种编程方式(Spring简化了切面编程)
AOP专业术语以及它们之间的联系:
- AOP使用步骤:
1)导包(标配)
2)写配置:
1)将目标和切面类(封装了通知方法(在目标方法执行后执行的方法))加入到ioc容器中
2)还应该告诉Spring到底哪个是切面类@Aspect
3)告诉Spring,切面类里面的每一个方法,都是何时何地运行?
//想在执行目标方法运行:写切入表达式 //execution(访问权限符 返回值类型 方法签名) @Before("execution(public int com.xm.impl.MyMathCalculator.*(int,int))") public static void logStart(){ System.out.println("方法开始执行,用的参数列表为"); } //在目标方法正常执行完成之后执行 @AfterReturning(("execution(public int com.xm.impl.MyMathCalculator.*(int,int))")) public static void logReturn(){ System.out.println("方法正常执行,计算结果是" ); } //想在目标方法出现异常的时候执行 @AfterThrowing("execution(public int com.xm.impl.MyMathCalculator.*(int,int))") public static void logException(){ System.out.println("方法执行出现了异常,异常信息为:"); } //想在目标方法结束的时候执行 @After(("execution(public int com.xm.impl.MyMathCalculator.*(int,int))")) public static void logEnd(){ System.out.println("【xxxx】方法最终结束了"); }
4)开启基于注解的AOP功能
3)测 试
Calculator bean = ioc.getBean(Calculator.class);bean.add(2,2);
- 环绕通知:
环绕通知优先于普通通知执行,执行顺序:
多切面执行顺序剖析:
LogUtils里嵌套环绕通知(环绕只是影响当前切面层)与Vaspect属于不同层
-
AOP使用场景:
1)AOP加日志保存到数据库‘;
2)AOP做权限验证;
3)AOP做安全检查;
4)AOP做事务控制;
-
事务:操作数据库;
Spring提供了JDBCTemplate
1.配置Template:
2.更新数据实验演示:
3.批量插入的实现:
4.查询某条记录,并封装成java对象
5.
6.查询最高的salary:
7.
8.
-
声明式事务:
以前通过复杂的编程来编写一个事务,替换为只需要告诉Spring哪个方法是事务方法即可;Spring自动进行事务控制
-
编程式事务:
AOP:环绕通知可以去做:
事务配置:
【在配置jdbcTemplate操作数据库中,ref = “xxxxxxx” 可以被替换为:value = “#{xxxxxxx}”】
创建好的对象最终都会保存在一个map中:
ioc容器之一,保存单实例bean的地方;
ioc就是一个容器,单实例bean保存在一个map中;
ApplicationContext和BeanFactory的区别:
事务细节:
具体分析:
修改隔离事务级别的命令:
读未提交:READ UNCOMMITED:
读已提交:READ COMMITED
可重复读【只要在同一个事务期间(快照读)】
事务的传播行为:
REQUIRED和REQUIRED_NEW的区别:
多事务执行总结(REQUIRED和REQUIRED_NEW):
multx(){ //RESUIRED A(){ //REQUIRES_NEW B(){ } //REQUIRED c(){ } } //REQUIRES_NEW D(){ DDDD(); //REQUIRES_NEW:不崩;REQUIRED:崩 //REQUIRED E(){ //REQUIRES_NEW F(){ //10/0; (E崩,G崩,A,C崩) } } //REQUIRES_NEW G(){ } } 10/0; //B成功,D的整个分支都不成功 //任何处崩,已经执行的REQUIRES_NEW都会成功 //如果是required:事务的属性都是继承于大事务的; //而propagation=Propagation.REQUIRES_NEW可以调整 //默认:required /* required:将之前事务用的connection传递给这个方法使用; requires_new:这个方法直接使用新的connection; */ }
Spring与JavaWeb整合使用:
1)Spring来控制事务(Dao-JdbcTempplate)
2)所有的组件都AutoWired
3)管理数据库
Spring与JavaWeb整合的步骤:
使用范例(可能会用到的常见场景,CRUD操作):
1.所有的Servlet需要的Service对象(Servlet本身不能被加到容器中),都通过WebUtils的静态方法从ioc容器中获取组件(这里使用泛型,任何类型的组件都可以被获取到)
【改进以后,使用监听器来启动程序,实现项目启动,即ioc容器创建;项目销毁,即ioc容器销毁】
2.查询一条数据的方法
3.查询一组数据
4.删除某条数据
5.获取单个数据
二、SpringMVC入门
1. SpringMVC概念
【POJO:Plain Old Java Object】
2. MVC架构模式
3. Spring视角下的MVC
4. 创建SpringMVC框架
SpringMVC创建第一个项目的文件目录显示:
HelloWorld的测试代码如下:
package com.xm.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;/** * 1.告诉SpringMVC这是一个处理器,可以处理请求 * * 细节: * 1.运行流程: * 1)客户端点击点击链接会发送 http://localhost:/SpringMVC_1/hello 请求 * 2)来到Tomcat客户端 * 3)SpringMVC前段控制器收到所有的请求 * 4)来看请求地址@RequestMapping标注,进行匹配,找到到底使用哪个类的哪个方法 * 5)前端控制器找到了目标处理器类和目标方法,直接利用返回执行目标方法 * 6)方法执行完后,会有一个返回值:SpringMVC认为这个返回值就是要访问的页面地址 * 7)拿到方法返回值后,用视图解析器进行拼串得到完整的页面地址 * 8)拿到页面地址,前端控制器帮我们转发到页面 * * 2.@RequestMapping * 就是告诉SpringMVC,这个方法处理什么请求 * 这个“/”是可以省略的,即使省略了,也是默认从当前项目开始的 * * 3.如果不指定配置文件位置? * /WEB-INF/springDispatcherServlet-servlet.xml * 如果不指定,会默认去找一个文件 * 就在web应用的/WEB-INF下创建一个名为:前端控制器名-servlet.xml 的文件 */@Controllerpublic class MyController { /** * /:代表从当前项目下开始,处理当前项目下的hello请求 */ @RequestMapping("/hello") public String myFirstRequest(){ System.out.println("请求收到了……正在处理中……"); //视图解析器自动拼串(下面这个是在springmvc.xml中的配置)// //// return "success"; }}// //
springmvc.xml
HelloWorld细节:
/*** 细节:* 1.运行流程:* 1)客户端点击点击链接会发送请求* 2)来到Tomcat客户端* 3)SpringMVC前段控制器收到所有的请求* 4)来看请求地址@RequestMapping标注,进行匹配,找到到底使用哪个类的哪个方法* 5)前端控制器找到了目标处理器类和目标方法,直接利用返回执行目标方法* 6)方法执行完后,会有一个返回值:SpringMVC认为这个返回值就是要访问的页面地址* 7)拿到方法返回值后,用视图解析器进行拼串得到完整的页面地址* 8)拿到页面地址,前端控制器帮我们转发到页面**/
一个方法处理一个请求
5. REST
REST :即Representational State Transfer。( 资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用资源( Resources) : 网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、-张图片、一首歌曲、-种服务, 总之就是一个具体的存在。可以用一个URI (统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI即为每一个资源的独一无二的识别符。
表现层( Representation) :把资源具体呈现出来的形式,叫做它的表现层( Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON 格式表现,甚至可以采用二进制格式。 状态转化( State Transfer): 每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态 转化" ( State Transfer )。而这种转化是建立在表现层之上的,所以就是“表现层状态转化"。具体说,就是HTTP协议里面,四个表示操作方式的动词: GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DEL ETE用来删除资源。REST:系统希望以非常简洁的URL地址来发送请求;怎样表示?对一个资源的CRUD用请求方式来区分:
URL地址起名 | /资源名/资源标识符 |
---|---|
/book/1 | get----查询1号图书 |
/book/1 | put----更新1号图书 |
/book/1 | delete----删除1号图书 |
/book | post----添加图书 |
【系统的URL地址就如此来设计即可,以请求方式来区别】
问题:从页面上只能发送两种请求:post和get
所以使用REST来构建一个CRUD系统:
BookController.java
package com.xm.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;@Controllerpublic class BookController { @RequestMapping(value = "/book/{bid}",method = RequestMethod.GET) public String getBook(@PathVariable Integer bid){ System.out.println("查询到了" + bid); return "success"; } @RequestMapping(value = "/book/{bid}",method = RequestMethod.DELETE) public String addBook(@PathVariable Integer bid){ System.out.println("删除了" + bid + "图书"); return "success"; } @RequestMapping(value = "/book/{bid}",method = RequestMethod.PUT) public String delBook(@PathVariable Integer bid){ System.out.println("更新了" + bid + "图书"); return "success"; } @RequestMapping(value = "/book",method = RequestMethod.POST) public String updateBook(){ System.out.println("添加了图书"); return "success"; }}
web.xml的配置
SpringMVC_Rest index.jsp springmvc org.springframework.web.servlet.DispatcherServlet 1 springmvc / HiddenHttpMethodFilter org.springframework.web.filter.HiddenHttpMethodFilter HiddenHttpMethodFilter /*
/** 1)SpringMVC中有一个filter,它可以把普通的请求转化为规定形式的请求;配置这个filter 2)按照以下要求: 1.创建一个类型的表单; 2.表单项中携带一个_method的参数; 3.这个_method*///以下是源码分析------ HiddenHttpMethodFilterprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { //获取表单上_method带来的值(delete/put) String paramValue = request.getParameter(this.methodParam); //判断如果表单是一个post而且_method有值 if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) { //转为put、delete请求 String method = paramValue.toUpperCase(Locale.ENGLISH); //重写了request.getMethod(); HttpServletRequest wrapper = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method); //wrapper.getMethod()====put filterChain.doFilter(wrapper, response); } else { //直接放行 filterChain.doFilter(request, response); }}
6.请求参数
此次实验的文件结构
HelloController.java
package com.xm.controller;import com.xm.book.Book;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.CookieValue;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import javax.servlet.ServletInputStream;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.BufferedReader;import java.io.IOException;import java.io.PrintWriter;@Controllerpublic class HelloController { @RequestMapping("/hello") public String handle01(){ System.out.println("请求收到了……正在处理中……"); return "success"; } /** * SpringMVC如何获取请 求带来的各种信息 * 默认方式获取请求参数: * 直接给方法入参上写一个和请求参数名相同的变量.这个变量就来接收请求参数的值: * 带:有值;没带:NULL; * @RequestParam :获取请求参数,具有指定性;默认是必须带(下面可以通过require的属性来改变默认状态) * @RequestParam("user")String username * 相当于 username = request.getParameter("user") * * value:指定要获取的参数的key * require:这个参数是否必须的 * defaultValue:如果没带值,指定一个默认值 * eg:( @RequestParam(value = "user",required = false,defaultValue = "None") String username ) * * 区别: * @RequestParam("user") : * @PathVariable("user") : /book/ [{user}pathVariable] ? [user=admin( requestParam) * 一个是获取问号后面的请求参数的值,另一个是获取路径上的占位符的值 * * @RequestHeader :获取请求头中某个key的值 * request.getHeader("User-Agent") * * @RequestHeader("User-Agent") String UserAgent * 等价于 UserAgent = request.getHeader("User-Agent") * * value:指定要获取的参数的key * require:这个参数是否必须的 * defaultValue:如果没带值,指定一个默认值 * eg:( @RequestHeader(value = "user",required = false,defaultValue = "None") String UserAgent ) * * @CookieValue :获取某个cookie的值 * * @return */ @RequestMapping("/handle01") public String handle02(@RequestParam(value = "user",required = false,defaultValue = "None") String username, @RequestHeader("User-Agent") String UserAgent, @CookieValue("JSESSIONID") String jid) { System.out.println("这个变量的值为:" + username); System.out.println("请求头信息为:" + UserAgent); System.out.println("cookie中的jid的值:" + jid); return "success"; } /** * 如果我们的请求参数是一个POJO: * SpringMVC会自动为这个POJO进行赋值 * 1)将POJO中的每一个属性,从request参数中尝试取出来,并封装即可; * 2)还可以级联封装————属性的属性 * 3)请求参数的参数名和对象中的属性名一一对应就行 * * 提交的数据可能有乱码: * 请求乱码: * get请求:改server.xml;在8080端口处,加URLEncoding="UTF-8" * post请求: * 在第一次获取请求参数之前设置 * ---response.setCharacterEncoding(this.encoding); * * 自己写一个filter;SpringMVC有这个filter * 响应乱码: * response.setContextType("text/html;charset=UTF-8"); * * @param book * @return */ @RequestMapping("/book") public String addBook(Book book){ System.out.println("我要保存图书" + book); return "success"; } /** * SpringMVC可以直接在参数上写原生API: * * HttpServletResponse * HTTPServletRequest * HTTPSession * * java.security.Principal: * * Locale:国际化有关的区域信息对象 * InputStream: * ServletInputStream inputStream = request.getInputStream(); * OutputStream: * ServletOutputStream outputStream = response.getOutputStream(); * Reader: * BufferedReader reader = request.getReader(); * Writer: * PrintWriter writer = response.getWriter(); */ @RequestMapping("/handle03") public String handle03(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException { request.setAttribute("reqParam","我是请求域中的"); session.setAttribute("sessionParam","我是session域中的"); ServletInputStream inputStream = request.getInputStream(); ServletOutputStream outputStream = response.getOutputStream(); BufferedReader reader = request.getReader(); PrintWriter writer = response.getWriter(); return "success"; }}
web.xml
SpringMVC_Rest index.jsp springmvc org.springframework.web.servlet.DispatcherServlet 1 springmvc / CharacterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true CharacterEncodingFilter /*
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>$Title$ helloWorld 写在方法上的RequestMapping
7. 数据输出
index.jsp【测试代码】
<%-- Created by IntelliJ IDEA. User: Shaw Date: 2022.01.23 Time: 22:34 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %>$Title$ hello handle01 handle02 handle03 handle04
OutputController.java
package com.xm.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.ui.ModelMap;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.SessionAttributes;import org.springframework.web.servlet.ModelAndView;import java.util.Map;/** * SpringMVC除了通过传入原生API,还可以怎么样把数据带给页面? * * 1)可以在方法处传入map或者Model、ModelMap * 在这些参数里面保存的所有数据都会放在请求域中,可以在页面获取 * 关系:Map或者Model、ModelMap,最终都是BindingAwareModelMap在工作; * 相当于给BindingAwareModelMap中保存的东西都会被放在请求域中; * * Map(interface(jdk)) Model(interface(spring)) * ↓↓ ↙ * ↓↓ ↙ * ↓↓ ↙ * ModelMap(class) ↙ * ↘ ↙ * ↘ ↙ * ExtendedModelMap * ↓ * BindingAwareModelMap * * 2)方法的返回值可以是ModelView类型(页面和数据的合体对象) * 既包含视图信息(页面地址),也包含模型数据(给页面的数据) * 而且数据是在请求域中(requestScope) * * 3)SpringMVC提供一种可以临时给Session域中保存数据的方式 * 使用一个注解 * @SessionAttributes(value = "msg") * 给BindingAwareModelMap或者ModelAndView中保存的数据,同时给session中放一份; * value:指定保存数据时要给session中放的数据的key; * * value = {"msg","haha"}:只要是保存这种key的数据,给session也放一份; * types = String.class:只要是保存这种类型的数据,给session也放一份; * * @SessionAttributes :使用这个可以能会发生异常; * 给session中数据就使用原生API; */@SessionAttributes(value = { "msg"},types = String.class)@Controllerpublic class OutputController { @RequestMapping("/handle01") public String handle01(Mapmap){ map.put("msg","你好"); map.put("haha",18); return "success"; } /** * model:一个接口 * @param model * @return */ @RequestMapping("/handle02") public String handle02(Model model){ model.addAttribute("msg","你好坏!"); model.addAttribute("ha","你好坏!"); return "success"; } @RequestMapping("/handle03") public String handle03(ModelMap modelMap){ modelMap.addAttribute("msg","你好毒!"); return "success"; } /** * 返回值是ModelAndView,可以为页面携带数据 * @param modelMap * @return */ @RequestMapping("/handle04") public ModelAndView handle04(ModelMap modelMap){ //之前的返回值就是视图名:视图解析器会拼串得到页面的真实地址 //ModelAndView mv = new ModelAndView("success"); //上面这句等价于下面这句 ModelAndView mv = new ModelAndView(); mv.setViewName("success"); mv.addObject("msg","你好哇!"); return mv; }}
测试代码:index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>$Title$ hello handle01 handle02 handle03 handle04
@ModelAttribute 注解的使用原理:
ModelAttributeTestController.java
package com.xm.controller;import com.xm.bean.Book;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.RequestMapping;import java.util.Map;/** * 测试ModelAttribute注解: * 使用场景:图书修改; * 1)页面端:显示要修改的图书的信息,图书的所有字段都有 * 2)servlet收到修改请求,调用dao * 3)实际场景? * 只修改部分字段 * 1.不修改的字段可以不显示,或者显示但不提供修改输入框 * 2.为了简单:Controller直接在参数位置来写Book对象 * 3.SpringMVC为我们自动封装book,没有带值的为null * 4.如果接下来调用了一个全字段的dao操作;会将其他的字段可能变为null * * 4)如何能保证全字段更新时,只更新页面携带的数据? * */@Controllerpublic class ModelAttributeTestController { /** * 可以告诉SpringMVC不要new这个book了,我刚才保存了一个book; * 哪个就是从数据库中查询出来的;用我这个book? * @param book * @return */ @RequestMapping("/updateBook") public String updateBook(@ModelAttribute("haha") Book book){ System.out.println(book); return "success"; } /** * 1)SpringMVC要封装请求参数的Book对象应该是从数据库拿出来的,而不是new出来的 * 2)再来使用这个拿出来的对象封装请求参数 * * ModelAttribute: * 参数:取出刚才保存的数据 * 方法位置:这个方法就会提前于目标方法先运行; * 1.在这里可以提前查出数据库中的图书信息; * 2.将这个信息保存起来【方便下一个方法还能使用】 * * 参数的map还是BindingAwareModelMap */ @ModelAttribute public void MyModelAttribute(Mapmap){ Book book = new Book(007,"西游记","吴承恩",144.0,50,48); System.out.println(book); map.put("haha",book); System.out.println("MyModelAttribute方法运行……"); }}
8. 源码分析系列
8.1DispatchServlet
原理图:
9. 视图解析
视图和视图解析器:
视图:
常用的视图类
常用的视图解析器实现类
未完待续…
转载地址:https://blog.csdn.net/weixin_43376042/article/details/122677083 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!