Skip to content

Latest commit

 

History

History
1993 lines (1579 loc) · 65.3 KB

框架预备知识.md

File metadata and controls

1993 lines (1579 loc) · 65.3 KB

框架预备知识

一、Maven

  1. 认识:maven在后端中就相当于npm在前端中,主要用来管理jar包,解决版本冲突问题

  2. 下载maven安装包并进行解压

  3. 修改conf/setting.xml中的localRepository标签中的地址

    image-20200911213743040

  4. 配置环境变量:直接在path中新建,然后浏览至maven中的bin目录

    image-20200911213837704

  5. 检查maven是否安装:在dos窗口中运行:mvn -version指令

  6. 在eclipse中配置maven:打开eclipse > window > Preferences > Maven > User setting。然后将下图所示的三处地方正确配置

    image-20200911213935174

  7. 创建maven项目:file new 一个 Maven Project

  8. ==所有的依赖必须放在dependecies里面,依赖的jar下载到了maven的本地仓库里==

  9. mvnrepository的链接

  10. 新建Maven项目:

    在eclipse中依次点击:File --> New --> Maven Project --> 选择Maven项目的存放位置 --> Next --> 在Filter中选择maven archetypes-quickstart --> Next --> 填写自己想要创建的Group Id、Artifact Id --> finish。等待依赖加载即可(如果直接没加载过,第一次可能会很慢)

  11. 将Maven工程打包成jar包:

    选中整个maven项目 --> 右击选择Run as --> Maven install。等待执行完成出现 BUILD SUCCESS即可。

二、SpringBoot

  1. 认识:springboot——整合springweb,mail,用web显示mail框架执行的结果,相当于前端的vue

  2. Spring官网可以生成项目

  3. 生成项目的要点:

    image-20200914201624249

    image-20200914202155213

  4. 将下载好的项目的.zip文件解压到eclipse的项目空间当中,打开eclipse --> File --> import --> Maven --> Existing Maven Projects --> 选择下载好的spring项目,当出现pow.xml文件前的选框被勾选上时,点击Finish。至此,spring项目的准备工作已经完成。

1、开始搞案例

  1. 认识注解@RestController:在Spring中@RestController的作用等同于@Controller + @ResponseBody。所以想要理解@RestController注解就要先了解@Controller@ResponseBody注解。

    • @Controller注解:将当前修饰的类注入SpringBoot IOC容器,使得从该类所在的项目跑起来的过程中,这个类就被实例化。当然也有语义化的作用,即代表该类是充当Controller的作用
    • @ResponseBody:它的作用简短截说就是指该类中所有的API接口返回的数据,甭管你对应的方法返回Map或是其他Object,它会以Json字符串的形式返回给客户端,本人尝试了一下,如果返回的是String类型,则仍然是String。
  2. 认识注解@RequestMapping:用来映射请求,也就是通过它来指定控制器可以处理哪些URL请求,相当于Servlet中在web.xml中配置访问的url。

  3. 写一个简单的SpringBoot案例:

    • 首先设计一个实体类

      package com.tedu.sp01_test.demo;
      public class ItemCategory {
      	int id;
      	String name;
      	public int getId() {
      		return id;
      	}
      	public void setId(int id) {
      		this.id = id;
      	}
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      }
    • 再设计一个控制类来像网页返回实体类的json对象

      package com.tedu.sp01_test.demo;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      //加了@RestController后,框架会自动创建对象
      @RestController
      public class ItemCategoryController {
      	//浏览器不能直接运行find(),每个方法都运行在虚拟机中
      	//用户在浏览器中输入/cat,框架会调用find()
      	@RequestMapping("/cat")
      	public ItemCategory find() {
      		//模拟返回数据库中的数据
      		ItemCategory itemCategory=new ItemCategory();
      		itemCategory.setId(90);
      		itemCategory.setName("手机");
      		//返回的是对象,框架会自动调用 jackson把对象转成json
      		return itemCategory;
              //上面提到的jackson是一个类库,用来将Java对象转换成json对象和xml文档,且同时可以将json和xml文档转换为Java对象。慢慢理解
      	}
      }
    • 通过Application类来启动这个项目,当我们在控制台看到Tomcat started on port(s):8080…… 时说明这个项目启动成功。打开浏览器访问:localhost:8080/cat地址,即可在页面上看到由Controller类返回到页面上的json字符串了

      image-20200914210702525

  4. ==上面的案例说白了就是创建一个实体类,然后对实体类进行封装。再创建一个控制类来设置实体类的属性,最后返回实体类的对象。只不过是用到了springboot中的注解,让框架自动为我们将对象转化为json字符串再配置相应的url供我们在浏览器上访问而已。将对象转成json字符串由注解@RestController完成,自动配置url由注解@RequestMapping完成。==

2、将服务器异常信息发送至qq邮箱

  1. 邮箱环境准备:进入qq邮箱 --> 设置 --> 帐户 --> 开启POP3/SMTP服务(需要进行身份验证)。

  2. 生成邮箱的授权码(针对发件人的邮箱账户,哪怕既是发件人又是收信人)。通过后可获得自己(发件人)邮箱的授权码。==记得妥善使用自己的授权码,谨防泄露,否则会引发一系列不必要的麻烦,若被有心人拿去从事非法活动,后果不堪设想==。

  3. 创建springboot项目,并为该项目添加web和mail依赖。

    image-20200914213142710

  4. 在eclipse中import该项目

  5. 设置application.properties

    spring.mail.host=smtp.qq.com
    #改成自己的邮箱
    spring.mail.username=##########@qq.com
    #邮箱的授权码不是密码改成自己的   
    spring.mail.password=##############
    spring.mail.default-encoding=utf-8
    spring.mail.properties.mail.smtp.auth=true
    spring.mail.properties.mail.smtp.starttls.enable=true
    spring.mail.properties.mail.smtp.starttls.required=true
  6. 创建一个存放控制类的包controller。并在其中新建一个类MaillController

    @RestController
    public class MailController {
    	//加上autowired之后,框架创建一个JavaMailSender,赋值给javaMailSender
    	@Autowired
    	JavaMailSender javaMailSender;
    	public void sendMail(String subject,String text ) {
    		//创建一封邮件
    		SimpleMailMessage mailMessage=new SimpleMailMessage();
    		//设置收件人
    		mailMessage.setTo("##########@qq.com");
    		//设置发件人
    		mailMessage.setFrom("##########@qq.com");
    		//设置标题
    		mailMessage.setSubject(subject);
    		//设置正文
    		mailMessage.setText(text);
    		//发送邮件
    		javaMailSender.send(mailMessage);
    	}
    	@RequestMapping("/send1")//测试1——直接发送
    	public String send1() {
    		sendMail("邮件标题", "邮件正文");
    		return "返回成功";
    	}
        
        @RequestMapping("/send2")//测试2——触发try-catch后发送
    	public String send2() {
    		try {
    			int n=10/0;//这里会发生错误
    		} catch (Exception e) {
    			//异常信息打印到服务器中,程序员看不到
    			//e.printStackTrace();
    			//把异常信息转成字符串
    			StringWriter stingWriter=new StringWriter();
    			PrintWriter printWriter=new PrintWriter(stingWriter);
    			e.printStackTrace(printWriter);
    			String execeptionInfo=stingWriter.toString();
    			//return execeptionInfo;
    			this.sendMail("异常信息", execeptionInfo);
    			return "操作失败";
    		}
    		return "操作成功!";
    	}
    }
  7. 在Application类中启动该项目,当项目成功启动后,通过浏览器访问localhost:8080/send1执行测试1,访问localhost:8080/send2执行测试2。若收件人的邮箱成功收到发件人的邮件,则测试成功。

3、发短信

  1. 短信环境准备:

    • 注册云通讯账号,主要使用测试账号来进行测试
    • 控制台 --> 管理 --> 号码管理 --> 测试号码 --> 输入测试号码,获取验证码即可
    • 根据帮助文档配置相应环境
    • 创建一个添加有web依赖的springboot项目,新建一个controller包,再再包里面创建一个名为SMSController的类
  2. 复制并添加JDK帮助文档中的Maven依赖至pom.xml里

    <dependency>
        <groupId>com.cloopen</groupId>
        <artifactId>java-sms-sdk</artifactId>
        <version>1.0.3</version>
    </dependency>
  3. 复制调试用例至SMSController类中

    @RestController
    public class SMSController {
    	@RequestMapping("/sendSMS")
    	public String send() {
    		//生产环境请求地址:app.cloopen.com
    	    String serverIp = "app.cloopen.com";
    	    //请求端口
    	    String serverPort = "8883";
    	    //共有7处地方需要修改
    	    //修改1 :管理-->控制台首页中能查到accountSId
    	    String accountSId = "";
    	    //修改2 :管理-->控制台首页中能查到accountToken
    	    String accountToken = "";
    	    //修改3 :管理-->应用管理-->应用列表-->应用01-->应用管理,查看appId
    	    String appId = "";
    	    CCPRestSmsSDK sdk = new CCPRestSmsSDK();
    	    sdk.init(serverIp, serverPort);
    	    sdk.setAccount(accountSId, accountToken);
    	    sdk.setAppId(appId);
    	    sdk.setBodyType(BodyType.Type_JSON);
    	    //修改4 :修改接收短信的手机号,手机号要在后台添加为测试号码进去
    	    String to = "132********";
    	    //修改5 :templateId改成1
    	    String templateId= "1";
    	    //修改6 :
    	    String[] datas = {"6688","3"};//前一个为验证码,后一个为有效时间
    	    String subAppend="1234";  //可选 扩展码,四位数字 0~9999
    	    //修改7 :修改reqId为新的
    	    String reqId="fadfafas";  //可选 第三方自定义消息id,最大支持32位英文数字,同账号下同一自然天内不允许重复
    	    //HashMap<String, Object> result = sdk.sendTemplateSMS(to,templateId,datas);
    	    HashMap<String, Object> result = sdk.sendTemplateSMS(to,templateId,datas,subAppend,reqId);
    	    if("000000".equals(result.get("statusCode"))){
    	        //正常返回输出data包体信息(map)
    	        HashMap<String,Object> data = (HashMap<String, Object>) result.get("data");
    	        Set<String> keySet = data.keySet();
    	        for(String key:keySet){
    	            Object object = data.get(key);
    	            System.out.println(key +" = "+object);
    	        }
    	    }else{
    	        //异常返回输出错误码和错误信息
    	        System.out.println("错误码=" + result.get("statusCode") +" 错误信息= "+result.get("statusMsg"));
    	    }
    		
    		return "发送成功";
    	}
    }
  4. 运行Application成功启动服务后即可通过访问localhost:8080/sendSMS来使测试号码收到验证码信息

4、直接启动jar包开启服务

  1. 直接运行jar,需要pom中添加spring-boot-maven-plugin依赖。我们通过spring官网生成的springboot项目自带这个依赖
  2. 生成jar包:选中项目右击选择 --> Run as --> Maven install
  3. 在dos窗口下,进入改jar包所在的路径,直接输入jar包的全名称回车即可开启服务。

三、SpringMVC

1、SpringMVC的原理

==复杂,暂时无法理解==

2、Cookie和Session

HTTP 协议是一种无状态协议,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性。

3、Cookie

HTTP 协议中的 Cookie 包括 Web Cookie浏览器 Cookie,它是服务器发送到 Web 浏览器的一小块数据。服务器发送到浏览器的 Cookie,浏览器会进行存储,并与下一个请求一起发送到服务器。通常,它用于判断两个请求是否来自于同一个浏览器,例如用户保持登录状态。HTTP Cookie 机制是 HTTP 协议无状态的一种补充和改良

4、Session

客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个对象便是 Session 对象,存储结构为 ConcurrentHashMap。Session 弥补了 HTTP 无状态特性,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录。

5、拦截器

6、案例(SpringMVC+vue+jsp+redirect+forward+RESTFul)

  1. 展示服务器的数据到前端(SpringMVC+vue)

    • 实体类:Item

      package com.tedu.springmvc08_itemlist.controller;
      //商品
      public class Item {
      	//商品名称
      	String itemName;
      	//商品价格
      	Integer itemPrice;
      	public Item(String itemName, Integer itemPrice) {
      		super();
      		this.itemName = itemName;
      		this.itemPrice = itemPrice;
      	}
      	public String getItemName() {
      		return itemName;
      	}
      	public void setItemName(String itemName) {
      		this.itemName = itemName;
      	}
      	public Integer getItemPrice() {
      		return itemPrice;
      	}
      	public void setItemPrice(Integer itemPrice) {
      		this.itemPrice = itemPrice;
      	}
      }
    • 控制类:ItemController

      package com.tedu.springmvc08_itemlist.controller;
      import java.util.ArrayList;
      import java.util.List;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      @RestController
      public class ItemCotroller {
      	//返回商品列表
      	@RequestMapping("/list")
      	public List<Item> list(){
      		ArrayList<Item> items=new ArrayList<>();
      		items.add(new Item("手机", 99999));
      		items.add(new Item("小米电视", 888888));
      		return items;
      	}
      }
    • 前端:index.html

      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      <script type="text/javascript" src="vue.js"></script>
      <script type="text/javascript" src="axios.min.js"></script>
      </head>
      <body>
      	<div id="app">
      		<table>
      			<tr v-for="item in itemList">
      				<td>{{item.itemName}}:{{item.itemPrice}}</td>
      			</tr>
      		</table>
      	</div>
      </body>
      <script type="text/javascript">
      	//定义配置对象
      	var config = {
      		el : "#app",
      		data : {
      			itemList:[]},
      		mounted : function() {
      			var serverUrl="/list";
      			axios.get(serverUrl)
      			.then(function(response){
      				debugger;
      				//response中有多个属性
      				var result=response.data;
      				//把服务器返回的list赋值给data中的itemList
      				this.vue.itemList=result;
      			})
      			.catch(function(e){
      				window.alert("联网失败了");
      				console.log(e);
      			});
      		}
      	}
      	//启动vue
      	var vue = new Vue(config);
      </script>
      </html>

      引入vue.js和axios.min.js。

      ==重点掌握vue中使用axios与服务器进行数据交互==

  2. 模拟商城的前后台(使用vue框架,实现前台操作后台数据)

    • 创建业务类,用数组存储数据:ItemController

      package com.tedu.springmvc09_item.controller;
      import java.util.ArrayList;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      @RestController
      public class ItemController {
      	//商品数据
      	static ArrayList<String> itemList=new ArrayList<String>();
      	@RequestMapping("/insert")
      	public String insert(String itemName) {
      		itemList.add(itemName);
      		return "添加成功";	
      	}
      	@RequestMapping("/select")
      	public ArrayList<String> select() {
      		return itemList;
      	}
      	@RequestMapping("/delete")
      	public String delete(String itemName) {
      		itemList.remove(itemName);
      		return "删除成功";
      	}
      	@RequestMapping("/update")
      	public String update(String oldItemName,String newItemName) {
      		for (int i = 0; i < itemList.size(); i++) {
      			if (itemList.get(i).equals(oldItemName)) {
      				itemList.set(i, newItemName);
      			}
      		}
      		return "修改成功";
      	}
      }

      很显然我们可以通过访问每个方法对应的RequestMapping声明的路径来执行相应的功能,但是如何通过网页使用鼠标来完成相应的操作呢?

    • 后台管理模块,页面(admin.html)。这个界面可以实现增、删、查功能,改的功能需要另外弹出一个页面

      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      <script type="text/javascript" src="vue.js"></script>
      <script type="text/javascript" src="axios.min.js"></script>
      </head>
      <body>
      	<div id="app">
      		<h1>后台管理系统</h1>
      		<input v-model="insertItemName">
      		<button v-on:click="insert">添加</button>
      		<br>
      		<table border="2" width="50%">
      			<tr>
      				<td>商品名称</td>
      				<td>删除</td>
      				<td>修改</td>
      			</tr>
      			<tr v-for="itemName in itemList">
      				<td>{{itemName}}</td>
      				<td v-on:click="deleteAll(itemName)">删除</td>
      				<td>
      				<!-- v-bind:href。href属性由vue来处理表达式 -->
      				<a v-bind:href="'update.html?oldItemName='
      				+itemName">修改</a>
      				</td>
      			</tr>
      		</table>
      	</div>
      </body>
      <script type="text/javascript">
      var config={
      		el:"#app",
      		data:{
      			insertItemName:"请在这里输入商品名称",
      			itemList:[]
      		},
      		mounted:function(){
      			this.selectAll();
      		},
      		methods:{
      			deleteAll:function(itemName){
      				var serverUrl="/delete?itemName="+itemName;
      				axios.get(serverUrl)
      				.then(function(response){
      					//window.alert(response.data);
      					this.vue.selectAll();
      				})
      				.catch()
      			},
      			selectAll:function(){
      				var serverUrl="/select";
      				axios.get(serverUrl)
      				.then(function(response){
      					var result=response.data;
      					this.vue.itemList=result;
      				})
      				.catch()
      			},
      			insert:function(){
      				//window.alert(this.insertItemName);
      				var serverUrl="/insert?itemName="+this.insertItemName;
      				axios.get(serverUrl)
      				.then(function(response){
      					var result=response.data;
      					//window.alert(result);
      					this.vue.selectAll();
      				})
      				.catch(function(e){
      					window.alert(e);
      				})
      			}
      		}
      }
      var vue=new Vue(config);
      </script>
      </html>
    • 查:通过axios获取/select页面的数据,然后将得到的数据用数组储存起来,遍历这个数组,显示在页面上

      <table border="2" width="50%">
          <tr>
              <td>商品名称</td>
          </tr>
          <tr v-for="itemName in itemList">
              <button v-on:click="insert">添加</button>
              <td>{{itemName}}</td>
          </tr>
      </table>
      <script>
          var config={
              el:"#app",
              data:{
                  insertItemName:"请在这里输入商品名称",
                  itemList:[]
              },
              mounted:function(){
                  this.selectAll();
              },
              methods:{
                  selectAll:function(){
                      var serverUrl="/select";
                      axios.get(serverUrl)
                      .then(function(response){
                          var result=response.data;
                          this.vue.itemList=result;
                      })
                      .catch()
                  }
              }
          }
          var vue = new Vue(config);
      </script>
    • 增:

      <input v-model="insertItemName">
      <button v-on:click="insert">添加</button>
      
      <table border="2" width="50%">
          <tr>
              <td>商品名称</td>
          </tr>
          <tr v-for="itemName in itemList">
              <td>{{itemName}}</td>
          </tr>
      </table>
      <script>
          var config={
              el:"#app",
              data:{
                  insertItemName:"请在这里输入商品名称",
                  itemList:[]
              },
              mounted:function(){
                  this.selectAll();
              },
              methods:{
                  insert:function(){
      				//window.alert(this.insertItemName);
      				var serverUrl="/insert?itemName="+this.insertItemName;
      				axios.get(serverUrl)
      				.then(function(response){
      					var result=response.data;
      					//window.alert(result);
      					this.vue.selectAll();
      				})
      				.catch(function(e){
      					window.alert(e);
      				})
      			}
              }
          }
          var vue = new Vue(config);
      </script>
    • 修改

      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      <script type="text/javascript" src="vue.js"></script>
      <script type="text/javascript" src="axios.min.js"></script>
      </head>
      <body>
      	<div id="app">
      		<input v-model="newItemName">
      		<button v-on:click="update">修改</button>
      	</div>
      </body>
      <script type="text/javascript">
      var config={
      		el:"#app",
      		data:{
      			oldItemName:null,
      			newItemName:null
      		},
      		mounted:function(){
      			debugger;
      			//接收商品名称
      			//location代表浏览器的地址栏
      			var search=location.search;
      			search=search.substr(1);//去掉问号
      			var array=search.split("=");
      			this.oldItemName=array[1];
      			this.newItemName=this.oldItemName;
      		},
      		methods:{
      			update:function(){
      				debugger;
      				var serverUrl="/update?oldItemName="+this.oldItemName
      						+"&newItemName="+this.newItemName;
      				axios.get(serverUrl)
      				.then(function(response){
      					debugger;
      					var result=response.data;
      					window.alert(result);
      				})
      				.catch(function(){
      					window.alert("错误");
      				});
      			}
      		}
      }
      var vue=new Vue(config);
      </script>
      </html>
    • 显示

  3. 使用jsp显示商品列表

    • 添加依赖

      <dependency>
          <groupId>org.apache.tomcat.embed</groupId>
          <artifactId>tomcat-embed-jasper</artifactId>
      </dependency>
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
      </dependency>
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>jstl</artifactId>
          <version>1.2</version>
      </dependency>
    • 创建Item

      public class Item {
      	String name;
      	int price;
      	public Item(String name, int price) {
      		super();
      		this.name = name;
      		this.price = price;
      	}
      	public String getName() {
      		return name;
      	}
      	public void setName(String name) {
      		this.name = name;
      	}
      	public int getPrice() {
      		return price;
      	}
      	public void setPrice(int price) {
      		this.price = price;
      	}
      }
    • 在src\main下创建文件夹webapp\WEB-INF\jsp。再创建一个index.jsp文件并修改其字符集

      <%@ page language="java" contentType="text/html; charset=UTF-8"
          pageEncoding="UTF-8"%>
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      </head>
      <body>
      jsp
      </body>
      </html>
    • 修改application.properties

      spring.mvc.view.prefix=/WEB-INF/jsp/
      spring.mvc.view.suffix=.jsp
    • 创建controller

      package com.tedu.springmvc10_jsp.controller;
      import java.util.ArrayList;
      import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.servlet.ModelAndView;
      @Controller
      //@RestController返回json或字符串
      //@Controller返回jsp
      public class ItemController {
      	//ModelAndView model业务,View视图 界面。
      	@RequestMapping("/listItem")
      	public ModelAndView listItem() {
      		//返回WEB-INF/jsp/index.jsp
      		ModelAndView modelAndView=new ModelAndView("index");
      		ArrayList<Item> itemList=new ArrayList<Item>();
      		itemList.add(new Item("华为手机", 888888));
      		itemList.add(new Item("小米电视", 99999));
      		modelAndView.addObject("itemList",itemList);
      		return modelAndView;
      		//localhost:1314/listItem
      	}
      }
    • 修改jsp

      <%@ page language="java" contentType="text/html; charset=UTF-8"
      	pageEncoding="UTF-8"%>
      <!-- 相当于import -->
      <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
      <!DOCTYPE html>
      <html>
      <head>
      <meta charset="UTF-8">
      <title>Insert title here</title>
      </head>
      <body>
      	<table border=2>
      	<!-- 遍历itemList 取出来的数据叫item -->
      		<c:forEach items="${itemList }" var="item">
      			<tr>
      				<td>${item.itemName}</td>
      				<td>${item.itemPrice}</td>
      			</tr>
      		</c:forEach>
      	</table>
      </body>
      </html>

    总结:使用vue,浏览器先请求html,网页加载完之后,再用axios发请求,得到json,有数据绑定把数据显示在网页上。使用jsp,浏览器发一次请求,在服务器上生成html,浏览器得到的是html,直接显示在网页上。Vue是前台web与后台数据分离。有助于专业化。后台程序员专门用java做后台开发,前台程序员用vue,js专门做前台。推荐用vue。老项目用jsp。

  4. 请求重定向:redirect

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    @Controller
    public class UserController {
    	@RequestMapping("/login")
    	public String login() {
    		return "redirect:https://passport.jd.com/new/login.aspx";
    		//redirect:请求重定向。状态码:302
    		//forward:请求转发
    	}
  5. 请求转发:forward

    package com.tedu.springmvc11_page.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class UserController {
    	//只接收用户名和密码
    	@RequestMapping("/up")
    	@ResponseBody//?
    	public String userNameAndPwd(String userName,String pwd) {
    		return userName+":"+pwd;
    	}
    	
    	@RequestMapping("/code")
    	public String userNameAndpwdAndCode(String userName
    			,String pwd,String code) {
    		//判断验证码是否正确
    		//使用请求转发技术
    		return "forward:/up";
    		
    	}
    	/**
    	 * 1、重定向浏览器地址栏显示新的地址,转发地址栏不变
    	 * 2、重定向可以重定向别的网站,转发只能转发到本网站中的方法。
    	 */
    }

    总结:==1、重定向浏览器地址栏显示新的地址,转发地址栏不变 2、重定向可以重定向别的网站,转发只能转发到本网站中的方法。==

  6. RESTFul架构支持

    package com.tedu.springmvc11_page.controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class OrderController {
    	//根据日期查询订单
    	// /order/2020-01-01/2020-01-10
    	@RequestMapping("/order/{startDate}/{endDate}")
    	public String searchByDate(@PathVariable String startDate,@PathVariable String endDate) {
    		return "rest ful startDate="+startDate+",endDate="+endDate;
    	}
    }

    浏览器访问的URLhttp://localhost:8080/order/2019-10-01/2020-10-10自动将URL 中模板变量{startTime}和{endTime}绑定到@PathVariable注解的同名参数上,即入参后startTime=“2019-10-01”、endTime=“2020-10-10”。

7、web常见状态码

序号 状态码 解释
1 404 浏览器找不到服务器的资源
2 500 服务器内部错误
3 302 请求重定向
4 200 成功
5 405 找不到get/post方法
6 408 请求超时
7 503 服务器目前无法使用(由于超载或停机维护)
8 504 服务器作为网关或代理,但是没有及时从上游服务器收到请求

四、Spring

1、spring ioc

IoC是“控制反转”,又叫DI即“依赖注入”。所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制

==所谓 IOC ,就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系==

两张图看懂IoC

==没有使用IoC前,我们设计程序的流程==

依赖

==使用IoC思想后,我们设计程序的流程==

注入

  1. 谁控制谁?

    在传统的开发模式下,我们都是采用直接 new 一个对象的方式来创建对象,也就是说你依赖的对象直接由你自己控制,但是有了 IOC 容器后,则直接由 IoC 容器来控制。所以“谁控制谁”,当然是 ==IoC 容器控制对象。==

  2. 控制什么?

    ==控制对象。==

  3. 为何反转?

    没有 IoC 的时候我们都是在自己对象中主动去创建被依赖的对象,这是正转。但是有了 IoC 后,所依赖的对象直接由 IoC 容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。

  4. 哪些方面发生了反转?

    所依赖对象的获取被反转了。

面试考点:

  1. IoC和DI的关系 在平时的java应用开发中,我们要实现某一个功能或者说是完成某个业务逻辑时可能需要多个对象来协作完成,在没有使用Spring的时候,每个对象在需要使用他的合作对象时,自己均要使用像new object() 这样的语法来将合作对象创建出来,这个合作对象是由自己主动创建出来的,创建合作对象的主动权在自己手上,自己需要哪个合作对象,就主动去创建,创建合作对象的主动权和创建时机是由自己把控的,而这样就会使得对象间的耦合度高了,A对象需要使用合作对象B来共同完成一件事,A要使用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是紧密耦合在一起,而使用了Spring之后就不一样了,创建合作对象B的工作是由Spring来做的,Spring创建好B对象,然后存储到一个容器里面,当A对象需要使用B对象时,Spring就从存放对象的那个容器里面取出A要使用的那个B对象,然后交给A对象使用,至于Spring是如何创建那个对象,以及什么时候创建好对象的,A对象不需要关心这些细节问题(你是什么时候生的,怎么生出来的我可不关心,能帮我干活就行),A得到Spring给我们的对象之后,两个人一起协作完成要完成的工作即可。

    所以控制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创建时机是由自己把控的,而现在这种权力转移到第三方,比如转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。

    DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。

    IoC是设计思想,IoC有三个核心:BeanFactory、反射、DI。BeanFactory利用反射实现对象的创建,DI实现对象关系管理。

  2. 什么是自动装配 利用注解方式,我们只需要写@Autowired注解,底层就会去容器中找对应的对象,如果有获取到,反射调用其对应的set方法,设置。而这个调用过程都是自动,我们没有手工去写set方法。所以这个过程也称为自动装配。

2、手写spring ioc

  1. 创建org.spring.ioc包,定义@autowired注解

    package org.spring.ioc;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    //自动装配,给属性自动赋值
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoWired {
    }
  2. 创建com.tedu包 定义UserServiceImpl,UserController两个类

    package com.tedu;
    //业务层类
    public class UserServiceImpl {
    }
    
    package com.tedu;
    import org.spring.ioc.AutoWired;
    public class UserController {
    	//需要业务层对象
    	@AutoWired
    	UserServiceImpl userServiceImpl;	
    }
  3. 再org.spring.ioc中定义一个IOCMain定义容器,给属性自动赋值

    package org.spring.ioc;
    //模拟spring框架
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Set;
    import com.tedu.UserController;
    import com.tedu.UserServiceImpl;
    public class IOCMain {
    //放对象的容器
    	static HashMap<String, Object> container=
    			new HashMap<>();
    	public static void main(String[] args) throws Exception {
    		//加载对象到容器中
    		loadObject();
    		//处理autowired,给属性赋值
    		autoWiredProcess();
    		//测试UserController的userServiceImpl属性是否有值
    		//从容器中找对象
    		UserController userController=
    				(UserController) container.get("userController");
    		//打印对象的属性
    		System.out.println(userController.toString());
    	}
    	private static void autoWiredProcess() throws Exception {
    		//遍历HashMap的key
    		Set<String> keySet=container.keySet();//得到集合
    		for (String name : keySet) {
    			//根据对象得到对象
    			Object object=container.get(name);
    			//根据对象得到类对象
    			Class clazz=object.getClass();
    			//根据类对象得到所有属性
    			Field[] fields=clazz.getDeclaredFields();
    			//遍历所有属性
    			for (Field field : fields) {
    				//判断属性是否加了autoWired
    				AutoWired autoWired=field.getAnnotation(AutoWired.class);
    				//如果加了,从容器中找到对象,赋值
    				if (autoWired!=null) {
    					Object value=container.get(field.getName());
    					field.setAccessible(true);
    					//创建一个对象
    					field.set(object, value);
    				}
    			}
    			
    		}
    	}
    	private static void loadObject() {
    		UserController userController=new UserController();
    		container.put("userController", userController);		
    		UserServiceImpl userServiceImpl=new UserServiceImpl();
    		container.put("userServiceImpl", userServiceImpl);	
    	}
    }

3、静态代理和动态代理

  1. 什么是代理模式?

    定义:为其他对象提供一种代理以控制对这个对象的访问。==通俗理解:明星的经纪人;法人的律师……他们可以全权代理对象的所有事务。==

  2. 解决什么问题(即为什么需要)?

  3. 什么是静态代理?

    静态代理是指预先确定了代理与被代理者的关系

  4. 什么是动态代理模式?

  5. 二者什么关系?

  6. 具体如何实现?

  7. 什么原理?

  8. 如何改进?

实现静态代理

首先我们需要准备一个接口、两个实现类、一个测试类

两个实现类都分别实现同一个接口,但是其中的一个实现类会定义一个类型为另一个实现类的成员属性,然后还得添加一个形参数据类型为接口类的有参构造器,用来实现代理。

我们以方堂镜为法外狂徒张三代理案件为例:抽象接口为“诉讼”,真实类为“法外狂徒张三诉讼”,代理类为“方堂镜代表张三进行诉讼”,测试类为“法院的法庭”。

  1. 抽象接口“Sos”

    public interface Sos{
        void sos();//这个方法进行诉讼的操作
    }
  2. 真实类“ZSos”

    public class ZSos implements Sos{
        @override
        public void sos(){
            sout("我是张三,我是被冤枉的");
        }
    }
  3. 代理类“FSos”

    public class FSos implements Sos{
        Sos zhangSan;
        public FSos(Sos zhangSan){
            this.zhangSan=zhangSan;
        }
        
        @override
        public void sos(){
            zhangSan.sos();
        }
    }
  4. 测试类为“TestSos”。

    public class TestSos{
        main{
            Sos zhangSan=new ZSos();
            Sos fsos=new FSos(zhangSan);
            fsos.sos();
        }
    }

    可以看到,方堂镜全权代理了法外狂徒张三的本次诉讼活动。那使用这种代理模式有什么好处呢,我们为什么不直接让法外狂徒张三直接完成本次诉讼呢?现实中的情况比较复杂,但是我可以简单列出几条:这样方堂镜就可以在提起诉讼等操作之前做一些校验工作,或者记录工作。例如法外狂徒张三提供的资料,方堂镜可以选择的移交给法庭而不是全部等等操作,就是说可以对代理的对做一些控制。例如法外狂徒张三不能出席法庭,方堂镜可以代为出席。。。

动态代理

未理解的代码

private static Object getProxy(UserDAO target) {
    //放的是目标类的所有接口的类对象
	Class[] interfaces=target.getClass().getInterfaces();//获取target的接口信息
	//类加载器
	ClassLoader classLoader=target.getClass().getClassLoader();
	//java生成代理类¥Proxy0{select(){调接口InvocationHandler.invoke}}
	MyHandler myHandler=new MyHandler(target);
	Object proxyObject=
			Proxy.newProxyInstance(classLoader, interfaces, myHandler);
		
	return proxyObject;
}

动态代理是为了解决什么问题?

为了解决真实类与代理类之间的代理关系在编译时就确定的固定缺陷。动态代理会在“法外狂徒张三”开庭时自动为其匹配一位代理律师,而并非要张三在开庭前与方堂镜确定代理关系,这样的模式更具有机动性。

首先准备一个接口,一个真实类,一个动态代理类,一个修改静态工厂方法类,一个测试类。

  1. 抽象接口“MyDAO”

    package testDynamicProxy;
    public interface MyDAO {
    	void select1();
    	void select2(String str);
    }
  2. 真实类“UserDAO”

    package testDynamicProxy;
    public class UserMyDAO implements MyDAO {
    	@Override
    	public void select1() {
    		System.out.println("法外狂徒张三请求出战");	
    	}
    	@Override
    	public void select2(String str) {
    		System.out.println("法外狂徒"+str+"请求出战");	
    	}
    }
  3. 动态代理类“MyHandler”

    package testDynamicProxy;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    public class MyHandler implements InvocationHandler {//实现InvocationHandler接口
    	MyDAO target;//定义抽象接口类型的变量,用来存放子类对象的引用
    	public MyHandler(MyDAO target) {//生成含参构造
    		super();
    		this.target = target;
    	}
        //重写invoke()方法
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		Object result=method.invoke(target, args);
    		return result;//返回由method调用的invoke方法的值
    	}
    }

    ==动态代理类首先需要实现InvocationHandler接口,然后定义一个抽象接口类型的成员遍历,构建一个有参构造器。然后重写invoke方法。在重写的invoke方法中需要返回method.invoke(target,args)方法的返回值。==

  4. 修改静态工厂方法类“MyProxy”

    package testDynamicProxy;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    public class MyProxy {
    	public static Object getProxy(MyDAO target) {
    		InvocationHandler inHandler=new MyHandler(target);
    		ClassLoader loader=target.getClass().getClassLoader();
    		Class[] interfaces=target.getClass().getInterfaces();
            
    		Object object=Proxy.newProxyInstance(loader, interfaces, inHandler);
    		return object;
    	}
    }

    ==修改静态方法工厂类需要定义一个静态的返回值为Object且参数类型为抽象接口(MyDAO target)的方法,在方法中调用动态代理类的对象并将其引用指向InvocationHandler类型的变量,然后获取target的类加载器(target.getClass().getClassLoader()),再获取target的接口(target.getClass().getInterfaces())。最后再调用Proxy类的静态方法newProxyInstance,并将上面的动态代理对象、类加载器、接口传入这个方法中,并将其返回值指向Object类型变量,最后返回这个Object对象。==

  5. 测试类“TestMyProxy”

    package testDynamicProxy;
    public class TestMyProxy {
    	public static void main(String[] args) {
    		UserMyDAO userMyDAO=new UserMyDAO();
    		MyDAO myDAO=(MyDAO) MyProxy.getProxy(userMyDAO);
    		myDAO.select1();
    		myDAO.select2("张老思");
    	}
    }
  6. 运行结果

    image-20200917204711770

4、AOP

强大的面向切面编程。对动态代理的封装,解决无侵入式编程

具体概念参见博客细说Spring——AOP详解(AOP概览)

开始遇见AOP

  1. 在start.spring.io中生成一个spring boot项目,并导入到Eclipse中

    image-20200917214407342

  2. 添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
  3. 新建service包,并创建一个抽象接口Service

    package com.tedu.springaop_test.service;
    public interface UserService {
    	String helloWorld();
    	String thankYou(String userName);
    }
  4. 在service包下创建接口Service的实现类UserServiceImpl

    package com.tedu.springaop_test.service;
    import org.springframework.stereotype.Service;
    @Service
    public class UserServiceImpl implements UserService {
    	@Override
    	public String helloWorld() {
    		// TODO Auto-generated method stub
    		return "Hello World";
    	}
    	@Override
    	public String thankYou(String userName) {
    		// TODO Auto-generated method stub
    		return userName+"Thank you";
    	}
    }

    ==实现类的类名上需要添加@Service注解==

  5. 新建controller包,并在包中新建UserController类

    package com.tedu.springaop_test.controller;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import com.tedu.springaop_test.service.UserService;
    
    @RestController
    public class UserController {
    
    	@Autowired//控制反转
    	UserService userService;
    	
    	@RequestMapping("/helloWorld")
    	public String helloWorld() {
    		String result=userService.helloWorld();
    		return result;
    	}
    	
    	@RequestMapping("/thankYou")
    	public String thankYou(String userName) {
    		String result=userService.thankYou(userName);
    		return result;
    	}
    }

    ==UserController类的作用是什么?为什么要这样?==

  6. 在service包下创建切面类UserAspect,用来监听类中的方法是否执行

    package com.tedu.springaop_test.service;
    
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class UserAspect {
    
    	@Pointcut("execution(public * com.tedu.springaop_test.service.*.helloWorld())")
    	public void helloPointCut() {
    	}
        
    	// helloWorld开始执行时执行
    	@Before("helloPointCut()") // 在目标方法register之前执行
    	public void getStartHello() {
    		System.out.println("helloWorld()开始执行");
    	}
    
    	// helloWorld结束执行时执行
    	@After("helloPointCut()") // 在业务层方法register之后执行
    	public void getEndHello() {
    		System.out.println("helloWorld()结束执行");
    	}
    	
    	@Pointcut("execution(public * com.tedu.springaop_test.service.*.thankYou(..))")
    	public void thankPointCut() {
    
    	}
    
    	// thankYou开始执行时执行
    	@Before("thankPointCut()") // 在目标方法register之前执行
    	public void getStartThank() {
    		System.out.println("thankYou()开始执行");
    	}
    
    	// thankYou结束执行时执行
    	@After("thankPointCut()") // 在业务层方法register之后执行
    	public void getEndTHank() {
    		System.out.println("thankYou()结束执行");
    	}
    }

    ==这里的注解十分重要==

    • @Component:组件,与@Controller,@Service功能一样。框架会自动创建对象

    • @Aspect:这个类是个切面,在执行业务层方法之前或之后执行切面

    • @Pointcut()

      // pointcut:切入点,设置timeAspect执行的时机 // execution:执行。这个参数需要加“()”,并传入参数 // 后面的第一个代表的是类 // 后面的第二个代表的是类中的所有方法 // (..)代表方法的参数可以是任何类型,任何个数 // public * 代表方法的返回值类型是任何类型 // aop框架找@PointCut注解,

    • @Before():在目标方法之前执行

    • @After:在业务层方法之后执行

    使用AOP能得到的效果截图

    1. 网页执行helloWorld方法得到的结果

      网页执行helloWorld方法得到的结果

    2. 服务器控制台打印出来的日志

      服务器控制台打印出来的日志

    3. 浏览器执行thankYou方法得到的结果

      浏览器执行thankYou方法得到的结果

    4. 执行thankYou控制台得到的日志信息

      执行thankYou控制台得到的日志信息

五、MyBatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

学习MyBatis需要配合数据库一起使用

1、创建一个数据库

CREATE DATABASE /*!32312 IF NOT EXISTS*/`mall` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `mall`;

/*Table structure for table `admin` */

DROP TABLE IF EXISTS `admin`;

CREATE TABLE `admin` (
  `admin_id` int(11) NOT NULL AUTO_INCREMENT,
  `admin_name` varchar(100) DEFAULT NULL,
  `admin_password` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`admin_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

/*Data for the table `admin` */

insert  into `admin`(`admin_id`,`admin_name`,`admin_password`) values (1,'root','root');

/*Table structure for table `category` */

DROP TABLE IF EXISTS `category`;

CREATE TABLE `category` (
  `category_id` int(11) NOT NULL AUTO_INCREMENT,
  `category_name` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `category` */

insert  into `category`(`category_id`,`category_name`) values (1,'电脑'),(2,'手机');

/*Table structure for table `item` */

DROP TABLE IF EXISTS `item`;

CREATE TABLE `item` (
  `item_id` int(11) NOT NULL AUTO_INCREMENT,
  `category_id` int(11) DEFAULT NULL,
  `item_name` varchar(500) DEFAULT NULL,
  `item_price` int(11) DEFAULT NULL,
  `item_desc` varchar(5000) DEFAULT NULL,
  `item_image` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Data for the table `item` */

insert  into `item`(`item_id`,`category_id`,`item_name`,`item_price`,`item_desc`,`item_image`) values (2,1,'小新710',5000,'desc','http://localhost:8080/1.jpg'),(3,1,'小新720',7000,'desc','http://localhost:8080/2.jpg'),(4,2,'mate20',3000,'手机描述','http://localhost:8080/3.jpg');

/*Table structure for table `jt_order` */

DROP TABLE IF EXISTS `jt_order`;

CREATE TABLE `jt_order` (
  `order_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `total` int(11) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

/*Data for the table `jt_order` */

insert  into `jt_order`(`order_id`,`user_id`,`total`) values (1,1,900);

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(500) DEFAULT NULL,
  `password` varchar(500) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

/*Data for the table `user` */

insert  into `user`(`user_id`,`username`,`password`) values (1,'admin','admin');

2、通过逆向工程生成sql语句

  1. generator原始.rar

  2. Eclipse中import --> General --> Exsiting project into workspace

  3. 修改根目录下的generatorConfig.xml配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
      PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
      "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
    	<context id="testTables" targetRuntime="MyBatis3">
    
    		<!-- JavaBean 实现 序列化 接口 -->
    		<plugin type="org.mybatis.generator.plugins.SerializablePlugin">
    		</plugin>
    		<!-- genenat entity时,生成toString -->
    		<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
    <!-- 共用5处要修改 -->
    
    		<!--修改1: 数据库连接的信息:驱动类、连接地址、用户名、密码 -->
    		<jdbcConnection driverClass="com.mysql.jdbc.Driver"
    			connectionURL="jdbc:mysql://localhost:3306/mall" userId="root"
    			password="root">
    		</jdbcConnection>
    
    		<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 
    			和 NUMERIC 类型解析为java.math.BigDecimal -->
    		<javaTypeResolver>
    			<property name="forceBigDecimals" value="false" />
    		</javaTypeResolver>
    
    		<!--修改2:  targetProject:生成PO类的位置 -->
    		<javaModelGenerator targetPackage="com.tedu.jtmall.pojo"
    			targetProject=".\src\main\java">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    			<!-- 从数据库返回的值被清理前后的空格 -->
    			<property name="trimStrings" value="true" />
    		</javaModelGenerator>
    
    		<!--修改3:  targetProject:mapper映射文件生成的位置 -->
    		<sqlMapGenerator targetPackage="com.tedu.jtmall.mapper"
    			targetProject=".\src\main\java">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="false" />
    		</sqlMapGenerator>
    		
    		<!-- 修改4: targetPackage:mapper接口生成的位置 -->
    		<javaClientGenerator type="XMLMAPPER"
    			targetPackage="com.tedu.jtmall.mapper" targetProject=".\src\main\java">
    			<!-- enableSubPackages:是否让schema作为包的后缀 -->
    			<property name="enableSubPackages" value="true" />
    		</javaClientGenerator>
    
    		<!--修改5:  指定数据库表 -->		
    		<table tableName="category" />
    		<table tableName="item" />
    <table tableName="user" />
    		<table tableName="jt_order" />
    <table tableName="admin" />
    	</context>
    </generatorConfiguration>
  4. 执行GeneratorApp生成xml和实体类

  5. 查看自动生成的实体类和xml

  6. ==如果报错一定是pom.xml中的mysql版本与当前mysql的版本不符==

3、案例1.1:商品分类CRUD操作

  1. 创建springboot项目。mybatis_01jtmall,添加Spring Web,MyBatis Framework,MySql Driver依赖。包名是com.tedu.jtmall,==包名要和逆向工程生成的包名一致==。

  2. 在刚刚生成的逆向工程中拷贝mapper和 pojo到这个新建项目的mapper和 pojo包中。

  3. 添加包扫描到Application中

    @SpringBootApplication
    //框架为com.tedu.jtmall.mapper包下的接口自动创建代理对象
    @MapperScan("com.tedu.jtmall.mapper")
    public class JtmallApplication {
    	public static void main(String[] args) {
    		SpringApplication.run(JtmallApplication.class, args);
    	}
    }
  4. 拷贝数据库配置。在src\main\resources\下创建文件application.yml,拷贝下面的内容。

    #端口值前必须有空格
    server:
      port: 1234
    #框架里添加了myBatis依赖,spring会启动mybatis,然而myBatis没配置信息,一定会出错
    #打开讲义,copy配置信息
    spring:     
        datasource:        
            driver-class-name: com.mysql.cj.jdbc.Driver        
            url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8
            username: root        
            password: root
    #也许有可能会需要加双引号
        
    mybatis:
      mapperLocations: classpath:com.tedu.jtmall.mapper/*.xml
    
    logging:
      path: ./logs
      level: 
        com.tedu.jtmall.mapper: debug

    ==冒号后面必须要有空格==

  5. 在控制层添加CRUD操作

    package com.tedu.jtmall.controller;
    import java.util.List;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import com.tedu.jtmall.mapper.CategoryMapper;
    import com.tedu.jtmall.pojo.Category;
    
    @RestController
    public class CategoryController {
    	//调用mybatis去操作数据库
    	@Autowired//从spring ioc容器中取对象
    	CategoryMapper categoryMapper;
    	
    	//接收分类名称,调用categoryMapper
    	@RequestMapping("/cat/insert")
    	public String insert(String categoryName) {
    		//创建实体类
    		Category category=new Category();
    		category.setCategoryName(categoryName);
    		//row表示添加了几行
    		int row=categoryMapper.insert(category);
    		return "row="+row;
    	}
        
        @RequestMapping("/cat/insertJson")
    	public String insertJson(@RequestBody Category category) {
    		int rowCount=categoryMapper.insert(category);
    		return "添加成功";
    	}
    	
    	//查询category商品分类表
    	@RequestMapping("/cat/select")
    	public List<Category> select(){
    		//selectByExample()中的参数是用来设置查询条件的。null
    		List<Category> list=categoryMapper.selectByExample(null);
    		return list;
    	}
    	
    	@RequestMapping("/cat/update")
    	//localhost:1234/cat/update?categoryId=6&newCateporyName=车车车
    	public String update(Integer categoryId,String newCateporyName) {
    		//创建对象
    		Category category=new Category();
    		category.setCategoryId(categoryId);
    		category.setCategoryName(newCateporyName);
    		//把对象传给接口,mybatis生成sql
    		int rowCount=categoryMapper.updateByPrimaryKey(category);
    		return "rowCount="+rowCount;
    	}
    	
    	@RequestMapping("/cat/delete")
    	public String delete(Integer categoryId) {
    		int rowCount=categoryMapper.deleteByPrimaryKey(categoryId);
    		return "rowCount="+rowCount;
    	}
    }

4、案例1.2:使用vue完成添加,显示,删除界面

  1. 准备vue.js和axios.min.js文件

  2. 双向绑定

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script type="text/javascript" src="vue.js"></script>
    <script type="text/javascript" src="axios.min.js"></script>
    </head>
    <body>
    	<div id="app">
    		<input v-model="categoryName">
    		<button v-on:click="insert">添加</button>
    
    		<div v-for="category in categoryList">
    			{{category.categoryName}}
    			<span v-on:click="remove(category.categoryId)">删除</span>
    		</div>
    		
    	</div>
    
    </body>
    <script type="text/javascript">
    var config={
    		el:"#app",
    		data:{
    			categoryList:[],
    			categoryName:"请输入数据添加"	
    		},
    		methods:{
    			remove:function(categoryId){
    				debugger;
    				var serverUrl="/cat/delete?categoryId="+categoryId;
    				axios.get(serverUrl)
    				.then(function(res){
    					debugger;
    					var result=res.data;
    					window.alert(result);
    					this.vue.select();	
    				})
    				.catch();
    			},
    			
    			insert:function(){
    				//从data中取值
    				var categoryName=this.categoryName;
    				var serverUrl="/cat/insertJson";
    				//使用post发一个j'son对象给服务器
    				var data={"categoryName":categoryName}//json对象
    				axios.post(serverUrl,data)
    				.then(function(res){
    					var result=res.data;
    					window.alert(result);
    					this.vue.select();	
    				}).catch();
    				
    			},
    			select:function(){
    				//mounted由vue框架自动调用
    				//1.设置服务器url
    				var serverUrl="/cat/select";
    				//2.发请求
    				axios.get(serverUrl).then(function(response){
    					var result=response.data;
    					//3.接收数据,赋值给data中的变量
    					this.vue.categoryList=result;
    				}).catch();
    			}
    
    		},
    		mounted:function(){
    			this.select();	
    		}
    }
    //2、启动框架
    var vue=new Vue(config);
    </script>
    </html>

5、案例2:体验表的关联关系

1、一对一
  1. 创建springboot项目。mybatis_01jtmall,添加Spring Web,MyBatis Framework,MySql Driver依赖。包名是com.tedu.jtmall,==包名要和逆向工程生成的包名一致==。

  2. 复制案例1.2中的application.yml到这个项目的相同路径中

  3. 创建三个包,分别是controller、mapper、pojo。

  4. 在pojo中创建一个User类

    package com.tedu.mybatis02_multiTable.pojo;
    //对应user表
    public class User {
    	Integer userId;
    	//username列
    	String username;
    	public Integer getUserId() {
    		return userId;
    	}
    	public void setUserId(Integer userId) {
    		this.userId = userId;
    	}
    	public String getUsername() {
    		return username;
    	}
    	public void setUsername(String username) {
    		this.username = username;
    	}
    }
  5. 在pojo中创建一个JTOrder类

    package com.tedu.mybatis02_multiTable.pojo;
    //对应jt_order表
    public class JTOrder {
    	//order_id列
    		Integer orderId;
    		//user_id列
    		Integer userId;
    		//用户名在User类
    		User user;//订单表和用户表是一对一关系
    
    		public Integer getOrderId() {
    			return orderId;
    		}
    		public void setOrderId(Integer orderId) {
    			this.orderId = orderId;
    		}
    		public Integer getUserId() {
    			return userId;
    		}
    		public void setUserId(Integer userId) {
    			this.userId = userId;
    		}
    		public User getUser() {
    			return user;
    		}
    		public void setUser(User user) {
    			this.user = user;
    		}
    }
2、一对多

六、配置mysql远程访问

七、前后端结合的项目——京淘商城

1、创建数据库

  1. mysql8.0在建表时不能使用admin这一单词做为表名,否则会出现建表能成功但是死活查询不到的bug

2、逆向工程

  1. 注意pom.xml文件中的mysql版本与自己的mysql版本是否匹配,否则无法逆向成功
  2. 逆向工程自动为我们生成了:
    • 在XXXXmapper.xml文件中生成sql语句
    • 将数据库中的每个表都生成一个实体类放到pojo包中
    • 生成实现增删改查的接口

3、配置yml文档

4、设计Controller,写CRUD语句

  1. 在类上添加注解@RestController
  2. 在类上(或每个方法上)添加注解CrossOrigin
  3. 在类里方法外创建一个动态代理的对象,用@Autowired
  4. 在方法外添加注解@requestMapping()
  5. 创建供前端调用的CRUD方法
    • 新增记录
    • 按主键删除
    • 按主键修改
    • 查询全部
    • 按主键查询
    • 多条件查询

5、设计vue

  1. 进入外部js包

    <script type="text/javascript" src="vue.js"></script><!-- vue框架的js文件 -->
    <script type="text/javascript" src="axios.min.js"></script><!-- 联网所需的文件 -->
    <script type="text/javascript" src="const.js"></script><!-- 存放全局变量 -->
  2. 设计div

    • 首先创建一个div,声明其id属性,例如<div id="app">
    • 在div中可以设计多种标签,在开发过程中常见的标签有<table><input><a><button>等,在这些标签中我们可以使用vue的特有语法(vue指令)对其进行众多牛逼特拉斯的功能。Vue指令大全
  3. 在底部设计config和创建Vue对象

    • el:DOM元素挂载点,可以是例如div的id属性。例如el:"#app"
    • data:存放变量的对象。里面的对象以key:value的格式存在
    • methods:声明函数的对象,里面存放可被调用的方法,格式为methodName:function(){}。在这个项目的开发工程中,方法们的内容为:
      • 定义访问的地址
      • 联网
        • 联网成功后
        • 联网失败后
    • mounted:页面加载就自动执行的方法的调用。在mounted中使用this.方法名的格式调用methods中的方法,可直接在页面加载时就执行。

    config中的代码为:

    var config = {
    	el: "#app",
    	data: {
    		categoryList: [],
            itemList: []
    	},
    	methods: {
    		findItemByCategoryId: function(categoryId) {
    			//debugger;
    			var serverUrl = serverHost + "/item/findByCategoryId?categoryId=" + categoryId;
    			`axios.get(serverUrl)
    			.then(function(res) {
    				var list = res.data;
    				this.vue.itemList = list;
    			})
    			.catch()
    		},
    		findCategory: function() {
    			var serverUrl = serverHost + "/category/selectAll";
    			axios.get(serverUrl)
    			.then(function(res) {
    				var list = res.data;
    				this.vue.categoryList = list;
    			})
    			.catch()
    		}
    	},
    	mounted: function() {
    		//debugger;
    		this.findCategory();
    		this.findItemByCategoryId(1);
    	}
    }
  4. ==mounted里面的this代表的是vue,在then里面的this代表的是window==

九、手写springmvc

  1. HttpServletRequest
  2. HttpServletResponse
  3. RestController
  4. Controller
  5. RequestMapping
  6. UserController
  7. WebServer
  8. DispatcherServlet
  9. ControllerDefinition
  10. WebApplicationContext
  11. HttpThread
  12. Order
  13. OrderController

1、获取请求信息

2、调用Controller

3、返回信息给浏览器

4、把对象转成json

十、手写IoC

十一、手写AOP