SpringMVC-Spring-Mybatis

SSM框架

1.1框架介绍
Jdbc是一个操作数据库的框架
Vue框架,也可以叫平台,提供了联网,ui,后台管理,权限等。
框架提供了很多类给我们用,框架更像一个平台,提供了环境,如淘宝。
课程学习三大框架springmvc,spring,mybatis

1.2Springmvc,spring,mybatis介绍

大框架由模块(小框架)组成,每个模块作者使用了最好的小框架来实现
如springmvc中把对象转成json,最早使用fastjson,现在使用jackson
如mybatis中连接池使用过c3po,druid, HiKari
长江后浪推前浪。

1.3springboot介绍
springboot是用来整合框架的

1.4Maven

1.5项目介绍
使用vue,springboot,springMVC,spring,mybatis完成一个商城,实现用户的注册,登录,商品分类,商品显示,后台管理。
2课程安排
Day01:Maven,springboot
Day02,03: springmvc ,
Day04: Spring,
Day05,06: mybatis
Day07,08:商城项目vue+ssm
Day09,10:项目峰会。

第三个月继续讲springboot,ssm。
3开发工具
3.1.1eclipse自动补全设置
最简单的修改方式是:Windows——>Preferences——>Java–>Editor–>Content Asist,在Auto activation triggers for Java后面的文本框里只有一个“.”。现在你将其改为“.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”即可
3.1.2Idea
有的电脑运行idea特别慢
3.1.3使用
因为课程中用到maven,springboot,三大框架。Idea在创建项目,排错,快捷键时操作与eclipse不一样。
前7天必须用eclipse,后面做项目必须用idea和hbuilder。与讲师所用开发工具必须保持一致。

4项目管理:Maven+SpringBoot
4.1Maven基础概念介绍
手动拷贝jar存在的安全,维护等问题。
如果mysql驱动被别人修改。数据库中的所有数据会被别人看到,案例csdn 600w
Baidu酒店 数据库泄露
4.1.1背景

现在几乎Maven已经成为了所有Java开源项目的标配,springcloud,springboot,springmvc,spring,mybatis等知名的开源项目都使用Maven进行管理。国内也有越来越多的知名的软件公司开始使用Maven管理他们的项目,如阿里巴巴和淘宝。

Maven——已经成为Java社区事实标准的项目管理工具,能帮你从琐碎的手工劳动中解脱出来,帮你规范整个组织的构建系统。不仅如此,它还有依赖管理、自动生成项目站点等超酷的特性。每天都有数以万计的开发者在访问中央仓库以获取他们需要的依赖。
Maven是跨平台的项目管理工具。作为Apache组织中的一个颇为成功的开源项目,主要服务于基于java平台的项目构建、依赖管理和项目信息管理。无论是小型的开源类库项目,还是大型的企业级应用;无论是传统的瀑布式开发,还是流行的敏捷模式,Maven都能大显身手。
4.1.2远程仓库介绍
4.1.2.1www.maven.org介绍
www.maven.org是maven的中央仓库

4.1.2.2maven.aliyun.com介绍
是国内的maven中央仓库,访问速度快。

4.1.2.3mvnrepository.com介绍
搜索mysql框架,查看下载量

查看分类排名

4.1.3Maven作用

4.1.4安装和配置
官网地址:http://maven.apache.org/download.html
课前资料提供的是最新版,已经配置好阿里云服务器,所有同学使用课前资料中的\software\apache-maven-3.6.3-bin.rar
4.1.4.1检查JAVA_HOME设置
在dos下查看 java版本

如果不显示java版本信息,参考下面文章设置java环境变量
https://blog.csdn.net/Luojun13Class/article/details/82860733?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

4.1.4.2解压maven,修改conf/setting.xml中的localRepository

课前资料提供的maven已经配置好了本地仓库位置和阿里云服务器地址
打开conf/settings.xml修改localRepository的值为自己电脑上的文件夹

4.1.4.3设置环境变量path
把maven/bin目录加入到系统环境变量path中,这样在任何地方就可以使用mvn命令
在dos下使用echo %path%查看path以前的设置
在path中追加maven信息,操作如下:
资源管理器中依次点击“此电脑”右键—“属性”—“高级系统设置”—“高级”—“环境变量”在系统变量下(不是用户变量)找到path,双击path,弹出编辑环境变量对话框,单击“新建”按钮,再通过“浏览”按钮选中maven的bin目录,依次单击“确定”即可完成环境变量设置

使用echo查看path设置

4.1.4.4检查安装是否正确
mvn -version

4.1.5思考题
如果国外的maven服务器不能访问了,国内程序员到那下jar包。
4.2Eclipse中设置maven

4.3创建Maven项目
4.3.1创建maven项目
需求:创建maven01_test项目
项目所在公司 groupid:com.tedu
项目名是artifact id:maven01_test

version: chrome 84.0.747
747:修改了一个bug
0:次版本
84:主版本

起始版本:0.0.1

方式一:

选择项目模块maven-archetype-quickstart,模块也叫骨架,如下图

4.3.2判断项目是否成功

4.3.3Pom.xml内容介绍
Xml是一种类似html的标记语言,里面有很多标记。

4.0.0

com.tedu
maven01_test
0.0.1-SNAPSHOT
jar

maven01_test
http://maven.apache.org

<!—所有的依赖必须放在dependecies里面,依赖的jar下载到了maven的本地仓库里–>

junit junit 3.8.1 test

查看本地仓库下多了以下文件

4.3.4maven项目的目录结构
src存放代码
test存放测试代码
pom.xml放依赖信息
4.3.5添加依赖
4.3.5.1添加mysql
在https://mvnrepository.com/上查找mysql坐标信息

坐标是用来确定jar的位置,包含包名,项目名,版本。

添加到pom.xml的dependencies中

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.21</version>
	</dependency>

</dependencies>

查看下载的jar的位置,位置中包括groupid,artifactid,version

4.3.5.2添加jackson
在mvnrepository.com的json libraries分类中找到jackson

com.fasterxml.jackson.core jackson-databind 2.11.0 查看下载的jar位置

4.3.5.3使用jackson

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;
}

}

public class App
{
public static void main( String[] args )
{
try {
//1,创建商品分类对象
ItemCategory itemCategory=new ItemCategory();
itemCategory.setId(900);
itemCategory.setName(“手机”);
//2,创建把商品分类对象转成json的对象
ObjectMapper objectMapper=new ObjectMapper();
//3,把商品分类对象转成json
String json=objectMapper.writeValueAsString(itemCategory);
//4,打印出来
System.out.println(json);
// json字符串可以返回给vue用,vue用v-for显示在界面上
} catch (Exception e) {
//发邮件
//发短信
}
}
}
4.4如何保证每位学生跟上
课上学生跟不上时,看新写的讲义来不及。
要看提交下发的讲义。照着讲义去做。

4.5如何掌握课堂内容
照着讲义复习。
4.6小结
软件开发就是使用第三方的jar来组装一个软件。像盖房子。
发邮件从网上下载一个jar,发短信也需要一个jar
需要的jar文件是从maven服务器上下载的。

4.7Eclipse中更改工作区,重新配置maven
Maven的设置非常重要,后面的课程都是基于maven的,通过更改工作区再次练习maven设置。
1,创建新文件夹为maven的本地仓库, 修改maven/conf/settings.xml中的localRepository
2,Eclipse fileswitch workspaceother更改工作区后需要重新设置maven,重新设置自动提示。
3,参考“创建Maven项目”一节创建maven项目

4.8常见问题
4.8.1常见错误:创建项目时报错
方法1:
Maven是将骨架存放在下面目录中,如果骨架出错,就将本地仓库中的org目录清空,重新创建maven项目,Maven会重新下载。

4.8.2导入项目后乱码
修改项目字符集会导致乱码如下图:

4.8.3执行maven命令时内存溢出
在使用maven时,如果报内存溢出,如使用 mvn site会耗费大量内存,则修改默认配置。
D:\javaenv\apache-maven-3.0.5\bin\mvn.bat
在@REM set MAVEN_OPTS=……后加入
set MAVEN_OPTS= -Xms128m -Xmx512m
4.8.4版权导致jar远程仓库中没有
例如oracle、全文检索的IKAnalyzer分词器、Dubbox等。

4.8.4.1通过命令拷贝jar
oracle驱动先去官网下载,下载下来后,需要安装到maven本地仓库,然后再pom中添加依赖.

1下载oracle驱动包

ojdbc6-11.2.0.3.jar
2命令行安装到maven仓库
mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.3.0 -Dpackaging=jar -Dfile=E:\oracle-lib\ojdbc6-11.2.0.3.jar
3添加依赖

com.oracle ojdbc6 11.2.0.3

4.8.4.2手动创建文件夹拷贝jar
按maven坐标手动创建目录结构,将jar单独下载,放入其中。
4.8.5下载中断
远程仓库为国外网站,全球都到哪里下载。常会因为网络故障导致jar下载不完全。
遇到这样的情况:
可以等待网络比较好的时候再下载
可以拷贝别人的仓库
如果只是个别jar包,可以到jar的官网下载后,然后创建包名,项目名,版本文件夹,把jar放到对应的文件夹中。
最恶劣的一种情况,下载出异常,也就是pom.xml会提示jar包有问题,可到maven本地仓库,jar也存在。这时可以打开jar包,看是否能打开。如果打不开,则删除,触发maven重新下载。
4.8.6使用别人的仓库
个别电脑上jar下载失败,可以拷贝别人的仓库。
步骤如下:
1,解压别人的仓库
2,修改maven/conf/settings.xml中的localRepository
4.8.7使用最新版本eclipse和maven
网盘中提供的maven是最新的。maven官网地址:http://maven.apache.org/download.html

网盘中提供的eclipse是最新的。
\sofware\eclipse-inst-win64.exe

4.9多模块项目
4.9.1分析

多项目开发有助于团队合作,独立开发,多个人同时开发。
企业中一个软件分多个项目,多个同时开发,用maven把多个项目合并在一起,发布给用户用。
版本冲突如何解决呢?
如果使用了低版本,高版本中的方法还能用吗?
4.9.2用户模块
4.9.2.1创建quick start项目

4.9.2.2添加依赖

com.fasterxml.jackson.core
jackson-databind
2.11.0

4.9.2.3创建User
package com.tedu.jdmall_user;

public class User {

}
4.9.2.4生成jar
选中项目右键 run asmaven install
Jar文件可以提供给别人使用。

4.9.3商品模块
4.9.3.1创建quick start项目
4.9.3.2添加依赖

com.fasterxml.jackson.core
jackson-databind
2.10.5

4.9.3.3创建Item
package com.tedu.jdmall_item;

public class Item {

}

4.9.3.4生成jar
选中项目右键 run asmaven install

4.9.4整合项目
4.9.4.1创建maven02_www项目
4.9.4.2添加依赖
打开pom.xmlDependenciesAdd 在enter groupid下面的输入框中输入关键字。

4.9.4.3测试依赖是否成功
public class App
{
public static void main( String[] args )
{
//测试依赖user模块是否成功
User user=new User();
System.out.println(user);

	//测试依赖商品模块是否成功
	Item item=new Item();
	System.out.println(item);

}

}

4.10依赖冲突
4.10.1查看依赖的包

com.tedu
maven02_user
0.0.1-SNAPSHOT

com.tedu maven02_item 0.0.1-SNAPSHOT

4.10.2调整依赖的顺序

com.tedu
maven02_item
0.0.1-SNAPSHOT

com.tedu maven02_user 0.0.1-SNAPSHOT

4.10.3结论:最先优先
如果合并后的项目,依赖了低版本,某个模块用了高版本,高版本中的新的方法就用不了,会出错。如果依赖了高版本,高版本会删除低版本中的方法。
必须统一版本。升级版本时,公司统一都升级。
4.11 Parent统一版本
4.11.1分析

创建项目maven02_parent
4.11.2修改pom.xml,设置打包为pom
修改packaging的值为pom,表是这个项目是用来管理依赖。

在pom.xml中添加jackson依赖。

4.0.0

<groupId>com.tedu</groupId>
<artifactId>maveno2_parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>

<name>maveno2_parent</name>
<url>http://maven.apache.org</url>

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>3.8.1</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>2.11.0</version>
	</dependency>

</dependencies>

将项目打包设置成pom会报错,必须选中项目右键 mavenupdate project

Run as maven install

4.11.3修改maven02_user和maven02_item继承之maven02_parent

Pom.xml中生成parent标签
删除原先的jackson的依赖


4.0.0

maven02_user
jar

maven02_user
http://maven.apache.org

junit junit 3.8.1 test com.tedu maveno2_parent 0.0.1-SNAPSHOT

Run as maven install
4.11.4查看maven02_www依赖

4.11.5Maven经验
每个公司都会定义一个parent,在parent中定义所有的依赖,每个项目都会继承之parent,确保依赖的版本是一致的。
4.12 排错
4.12.1使用debug找错

使用断点目的是排错和跟踪程序的流程。
使用步骤
1,添加断点
2,以断点方式运行
3,F6跟到下一行,F5跟进去 ,F7退出方法,F8当前断点不跟了
4.12.2查看异常信息
4.13 SpringBoot介绍
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。Spring Boot 现在已经成为Java 开发领域的一颗璀璨明珠,它本身是包容万象的,可以跟各种技术集成。成为SpringBoot全家桶。
Springboot是用来整合框架的,相当于车架

4.14 创建SpringBoot项目
4.14.1生成项目
Spring官网https://start.spring.io 可以生成项目。
创建项目sp01_test
Snapshot是快照版本,可以认为是个临时版本,不是正式版本,每次编译时,maven都会去服务器上下载最新的。
M2是增加重要功能的版本,不是正式版本。

下图中的java版本要选择你电脑上存在的8

4.14.2导入生成的项目
将网站生成的sp01_test.zip拷贝到工作区中,解压
Fileimport

4.14.3Pom.xml中parent介绍

org.springframework.boot
spring-boot-starter-parent
2.3.3.RELEASE

学生第一次创建springboot项目,需要几分钟时间下载

Java版本改成11会报错。修改java版本要执行mavenupdate Project
4.14.4项目结构介绍
目录介绍
和使用vue-cli生成vue项目结构原理类似

4.14.5创建网页
Html要放在src\main\resources\static文件夹中。

@springbootApplication作用
http://localhost:8080/index.html

http是协议用来传输网页,还有ftp,smtp
😕/是固定的
Localhost代表本机
8080是端口号,一台服务器有多个端口号,可以在appliction.properties中修改,
一个端口号只能被一个程序使用,vue-element的端口号是9537,mysql的端口号是3306
端口号前必须有:是小写的
4.14.6修改端口号
打开src\main\resources\下的application.properties修改端口号

一个程序可以占用多个端口号

http://localhost:8080/index.html
http://localhost:8081/index.html

端口号占用会报以下信息
Description:

Web server failed to start. Port 8081 was already in use.

4.14.7创建控制器controller返回json

package com.tedu.mall;

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;
}

}

//加了@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;
}

}

http://localhost:8080/cat
4.14.8常见错误
没加@restController或@requestMapping

4.15 发邮件
4.15.1邮件应用场景
注册时激活帐号
下订单
网站活动

4.15.2开启pop3
进入qq邮箱,设置—》帐户开启pop3/smtp服务

4.15.3查看邮箱的授权码

4.15.4生成项目
https://start.spring.io/ 创建项目sp02_mail
4.15.5选择支持的依赖
添加web和mail依赖

4.15.6导入生成的项目
将网站生成的zip拷贝到工作区中,解压

4.15.7设置application.properties
打开src\main\resources\下的application.properties拷贝下面的内容,并修改spring.mail.password的值为你的授权码

spring.mail.host=smtp.qq.com
#改成自己的邮箱
spring.mail.username=2040738672@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

4.15.8MailController代码
创建controller包

@RestController
public class MailController {

//加上autowired之后,框架创建一个JavaMailSender,赋值给javaMailSender
@Autowired
JavaMailSender javaMailSender;

public void sendMail(String subject,String text ) {
	//创建一封邮件
	SimpleMailMessage mailMessage=new SimpleMailMessage();
	//设置收件人
	mailMessage.setTo("2040738672@qq.com");
	//设置发件人
	mailMessage.setFrom("2040738672@qq.com");
	//设置标题
	mailMessage.setSubject(subject);
	//设置正文
	mailMessage.setText(text);
	//发送邮件
	javaMailSender.send(mailMessage);
}

@RequestMapping("/send1")
public String send1() {
	sendMail("明天入职", "明天入职");
	return "成功";
}

}

http://localhost:8080/send1

@RequestMapping("/send2")
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 “操作成功!”;
}
http://localhost:8080/send2
4.15.9小结
Springboot整合了邮件发送框架和web框架

4.16发短信
4.16.1短信应用场景
异常通知
注册码
重要操作(银行转帐)的验证码
4.16.2创建项目
在start.spring.io上创建项目sp03_sms添加web依赖
将网站生成的zip拷贝到工作区中,解压
在eclipse中导入项目
创建controller包,创建SmsController类
浏览器中测试controller
4.16.3注册帐号
注册网址是https://www.yuntongxun.com/

4.16.4身份认证
帐号—》认证信息,需要提供照片,课后完成。

没有认证只能给指定手机发短信
4.16.5添加测试号码
管理—》测试号码

4.16.6查看模板
管理—》短信管理—》短信模板

4.16.7关闭鉴权ip
管理—》控制台首页

4.16.8查看帮助文档
首页—》文档帮助—》短信快速入门SDK参考(新版)

4.16.9从帮助文档中拷贝发短信框架的坐标信息

com.cloopen
java-sms-sdk
1.0.3

从讲义或帮助文档中拷贝代码,修改accountSId,accountToken,appId,to,templateId,datas ,reqId
@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 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.16.10查看短信下行记录
管理—》短信管理—》短信记录—》下行短信记录

4.16.11小结
Springboot整合了短信发送框架和web框架
通过调用第三方的服务实现了发邮件和发短信,支付也可以通过调用微信,支付宝接口实现。

4.17 直接运行jar启动web服务
4.17.1pom.xml
直接运行jar,需要pom中添加spring-boot-maven-plugin依赖。
Start.spring.io生成的项目中已经添加好了。

4.0.0

org.springframework.boot
spring-boot-starter-parent
1.5.4.RELEASE

com.tedu
hello
0.0.1-SNAPSHOT


org.springframework.boot
spring-boot-starter-web





org.springframework.boot
spring-boot-maven-plugin




4.17.2生成jar包
选中sp01_test项目右键 run as maven install
4.17.3执行jar包
资源管理器中找到项目的target文件夹,在地址栏中输入cmd,进入dos.
进入到jar所在路径,然后执行java直接运行jar文件,启动web服务。
命令如下:
D:\java\ws\stu\hello\target>D:\java\env\jdk1.8\bin\java -jar hello-0.0.1-SNAPSHOT.jar

5三大框架:控制层框架SpringMVC
5.1Springmvc
是springweb,实现url映射,接收参数,返回数据等。
5.1.1表现层的三大任务

URL到controller的映射
http请求参数绑定
http响应的生成和输出

5.1.2性能超群
简单易用性能佳

5.2接收参数
在start.sping.io上创建项目,依赖web框架

创建项目springmvc01_base
5.2.1接收参数

public class User {
String username;
String password;

public String getUsername() {
	return username;
}

public void setUsername(String username) {
	this.username = username;
}

public String getPassword() {
	return password;
}

public void setPassword(String password) {
	this.password = password;
}

}

@RestController
public class UserController {

@RequestMapping("/register1")
//http://localhost:8080/register1?username=root&password=root
//springmvc框架会自动把url中数据赋值给对应的参数
public String register1(String username, String password) {
	return username + "," + password;
}

@RequestMapping("/register2")
//http://localhost:8080/register2?username=root&password=root
//springmvc框架会自动把url中数据赋值user对象的属性
public String register2(User user) {
	return user.getUsername() + "," + user.getPassword();
}

}

接收参数的应用场景
1,baidu接收关键字,返回搜索内容。
2,京东,淘宝接收关键字,返回商品。
3,微信接收添加好友的手机号,返回用户。
网站提供服务的基本流程是接收数据,返回结果。谁负责显示结果?谁负责存储数据。

参数的类型要申明为包装类型

@RestController
public class ItemController {

//http://localhost:8080/delete1?id=2
//http://localhost:8080/delete1
@RequestMapping("/delete1")
public String delete1(Integer id) {
	return "id="+id;
}
//http://localhost:8080/delete2?id=2
//http://localhost:8080/delete2	
@RequestMapping("/delete2")
public String delete2(int id) {
	return "id="+id;
}	

}
5.2.2测试controller是不是单例
//http://localhost:8080/delete1?id=2
//http://localhost:8080/delete1?id=1
@RequestMapping("/delete1")
public String delete1(Integer id) {
return “id=”+id+" "+this;
}

5.3Request,response对象

5.3.1Request

@RequestMapping("/register3")
public String register3(HttpServletRequest request) {
	//requeset用来接收数据
	//register(String username) 框架底层用的是request,前面写的框架就用到

//工作中用register(String username)
String username=request.getParameter(“username”);
String password=request.getParameter(“password”);
String ip=request.getRemoteAddr();

	return "request username="+username
			+",password="+password
			+",ip="+ip;
}

http://localhost:8080/register3?username=admin&password=123
5.3.2Response
@RequestMapping("/register4")
public void register4(HttpServletRequest request,HttpServletResponse response) throws Throwable{
//requeset用来接收数据,功能强大
//register(String username) 框架底层用的是request
String username=request.getParameter(“username”);
String password=request.getParameter(“password”);
String ip=request.getRemoteAddr();

	String returnContent="request response username="+username
			+",password="+password
			+",ip="+ip;
	//register(){return }框架的底层用的是response

//告诉浏览器返回数据的类型是html
response.setContentType(“text/html”);
//告诉浏览器返回数据的字符集是utf-8
response.setCharacterEncoding(“UTF-8”);
PrintWriter printWriter=response.getWriter();
printWriter.println(returnContent);
}

http://localhost:8080/register4?username=admin&password=123
5.4注解和反射
5.4.1为什么要手写框架
别人不让用的时候,可以自己实现
大公司会自己写框架或修改框架
小公司面试时会问到框架原理
springMvc框架中用到了注解和反射
5.4.2注解
5.4.2.1需求
框架自动创建对象

生活中的案例:
注解相当于生活中标签
1,网红 定义标签,贴标签,处理标签
2,超市中退货,定义标签,贴标签,处理标签

5.4.2.2实现

创建java项目springmvc02_annotation

package org.springboot;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//1,定义注解
@Target(ElementType.TYPE)//指定注解加在类上
@Retention(RetentionPolicy.RUNTIME)//在运行时有效
public @interface RestController {

}

//项目中的类,实现登录,注册
//2,使用注解
//@RestController
public class UserController {
public void login() {
}

public void regiser() {
}

}

package org.springboot;

import com.tedu.mall.UserController;

//模拟springboot的入口类
public class Main {

public static void main(String[] args) {
	//3,判断类有没有加@restController注解
	//得到类对象
	//ArrayList list得到集合对象
	//String str 得字符串对象
	//Class clazz得到类对象
	
	//类对象clazz有注解信息			
	Class clazz=UserController.class;
	RestController restController=(RestController) clazz.getAnnotation(RestController.class);

	if (restController!=null) {

//自动创建对象
UserController userController=new UserController();
System.out.println(userController);
}
}

}

去掉UserController上的注解,springboot就不会创建对象

5.4.3反射
5.4.3.1需求
创建很多对象,给私有属性赋值
5.4.3.2实现
创建项目 springmvc03_reflect

package com.tedu.mall;

public class UserController {
private int accessCount;

@Override
public String toString() {
	return "UserController [accessCount=" + accessCount + "]";
}

}

package org.sprigboot;

import java.lang.reflect.Field;

public class Main {

public static void main(String[] args) throws Throwable {

//通过java.io.File能得到包下文件名,也就是类名
String[] classNameArray = { “com.tedu.mall.UserController”, “com.tedu.mall.OrderController” };
for (String className : classNameArray) {
// 通过类名加载类
//clazz是类对象 ,类对象包含类的属性和方法信息。
Class clazz = Class.forName(className);
// 用反射创建对象
Object object = clazz.newInstance();
System.out.println(object);
// 给私有属性赋值
Field field = clazz.getDeclaredField(“accessCount”);
field.setAccessible(true);
field.set(object, 800);
System.out.println(object);
}
}

}

5.4.3.3自动执行方法
/调用方法
//getMethods()得到父类和本类的方法
//Method[] methods=clazz.getMethods();
//getDeclaredMethods,只能得到本类的申明的方法
Method[] methods=clazz.getDeclaredMethods();
for (Method method :methods) {
System.out.println(method.getName());
if (method.getName().equals(“toString”)) {
System.out.println("-----用invoke调用toString()--------");
String str=(String) method.invoke(object);
System.out.println(str);
}
}
5.5Springmvc原理
5.5.1演果演示,阅读代码
运行springmvc04_framework
5.5.2分析

实现用到了很多技术,分析时部分学生听不懂,如果不分析,上来就写某个类,学生也不知道这个类要做什么,等我们一行一行把框架实现了,再来分析总结,学生就能听懂,如果还不懂,后面还有复习。

5.5.3实现
创建项目springmvc04_framework

//加上这个注解spingmvc会自动创建对象
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RestController {

}

package org.springmvc;

//加上这个注解的方法,可以通过浏览器执行执行
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {

//使用时可以通过RequestMapping(value=/regiser)给value属性赋值。
//value是个属性,定义时必须加()
String value();

}

@RestController
public class UserController {

@RequestMapping("/login")
public String login() {
	return "登录成功";
}

@RequestMapping("/register")
public String register() {
	return "注册成功";
}

}

//封装了url,类名和方法名
public class ControllerDefinition {
//浏览器中输入/register,执行UserController类 的register方法
String url;// /register /login
String controllerName;// UserController
String methodName;// register,login
public ControllerDefinition(String url, String controllerName, String methodName) {
super();
this.url = url;
this.controllerName = controllerName;
this.methodName = methodName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getControllerName() {
return controllerName;
}
public void setControllerName(String controllerName) {
this.controllerName = controllerName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
}

public class SpringMvcMain {

// /register,{/register,UserController,register,false}
// /login,{/login,UserController,login,false}
public static HashMap<String, ControllerDefinition> UrlMapping = new HashMap<>();

public static void main(String[] args) throws Throwable {

	loadObject();
	
	requestProcess("/login");
	requestProcess("/register");
	
}

private static void requestProcess(String url) throws Throwable {
	ControllerDefinition controllerDefinition=UrlMapping.get(url);
	String controllerName=controllerDefinition.getControllerName();
	String methodName=controllerDefinition.getMethodName();
	
	//用反射加载类
	Class clazz=Class.forName(controllerName);
	//创建对象
	Object object=clazz.newInstance();
	//得到方法
	Method method=clazz.getDeclaredMethod(methodName);
	//执行方法
	Object result=method.invoke(object);
	System.out.println("url="+url+",结果"+result);
	
}

private static void loadObject() {
	//通过java.io.File能得到包下文件名,也就是类名
	Class clazz = Class.forName("com.tedu.controller.UserController");
	// 判断这个类有没有加@restController
	RestController restController = (RestController) clazz.getAnnotation(RestController.class);
	if (restController != null) {
		// 得到所有方法
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			// 判断方法有没有加requestMapping注解
			RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
			if (requestMapping != null) {
				// 得到url
				String url = requestMapping.value();
				// 创建ControllerDefinition
				String methodName = method.getName();

				ControllerDefinition controllerDefinition = new ControllerDefinition(url, clazz.getName(),
						methodName);
				// 把controllerDefinition放到UrlMapping
				UrlMapping.put(url, controllerDefinition);
			}
		}
	}
	
	
}

}

5.5.4思考题
如果有多个controller,程序该如何修改?

5.6Cookie
查看jd,baidu的cookie
在chrome浏览器中单击网站前的图标,可以查看cookie.

网站是如何记着你的,根据你以前的访问生成特定的内容。

5.6.1设置cookie
@RestController
public class UserController {
@RequestMapping("/login")
public String login(String username, HttpServletResponse response) {
//创建cookie
Cookie cookie = new Cookie(“username”, username);
//把cookie返回给浏览器
response.addCookie(cookie);
return “设置cookie”;
}
}

F12打开调试面板networkresponse headers中能查看到服务器返回的cookie

5.6.2读取cookie
F12打开调试面板networkrequest headers中能查看到浏览器发给服务器的cookie

@RestController
public class OrderController {

@RequestMapping("/getOrder")
public String getOrder(HttpServletRequest request) {
	//读取所有cookie
	Cookie[] cookies = request.getCookies();
	String string = "";
	if (cookies != null) {
		//遍历cookie
		for (Cookie cookie : cookies) {
			//取cookie名
			String cookieName = cookie.getName();
			//取cookie值
			String cookieValue = cookie.getValue();
			string = string + cookieName + cookieValue;
		}
	}
	return string;
}

}
5.6.3小结
Cookie是用来识别用户的。
如何防止借钱时被骗?

没有设置有效期的cookie,浏览器关闭了,cookie也就没有了
5.6.4不登录设置cookie
需求:用户不登录情况下,记录用户访问了那些网页。

@RestController
public class IndexController {

@RequestMapping("/index")
public String index(HttpServletResponse response) {
	Cookie cookie=new Cookie("tempId", "0001");
	cookie.setMaxAge(60*3);//设置有效期为3分钟
	response.addCookie(cookie);//把cookie放到响应头中
	return "这是首页";
}

}

5.7Session
5.7.1需求
需求:商城购物车
5.7.2添加购物车
创建项目springmvc06_session

@RestController
public class CartController {

@RequestMapping("/insert")
public String insert(String itemName, HttpSession session) {
	//session内部是有hashmap,用来存放数据
	
	//找到购物车
	ArrayList<String> cart = (ArrayList<String>) session.getAttribute("cart");
	//判断购物车是否为空
	if (cart == null) {
		//创建购物车
		cart = new ArrayList();
		
		//保存购物车
		session.setAttribute("cart", cart);
	}
	//向购物车添置商品
	cart.add(itemName);
	return itemName + "添加到购物车";
}

}

向购物车添加商品
http://localhost:8080/insert?itemName=mobile1
http://localhost:8080/insert?itemName=mobile2

5.7.3查看购物车
@RequestMapping("/list")

public String list(HttpServletRequest request, HttpSession session) {
	String string = "";
	//找到购物车
	ArrayList<String> cart = (ArrayList<String>) session.getAttribute("cart");
	//判断购物车是否为空
	if (cart != null) {
		//遍历商品
		for (String itemName : cart) {
			string = string + "itemName:" + itemName;
		}
	}
	return string;
}

http://localhost:8080/list
5.7.4查看cookie
在服务器使用session后,服务器会自动向浏览器写入JSESSIONID cookie,值是一个随机数,每个用户都不一样。

@RequestMapping("/list")
public String list(HttpServletRequest request, HttpSession session) {
String string = “”;
Cookie[] cookies = request.getCookies();

	if (cookies != null) {
		for (Cookie cookie : cookies) {
			String cookieName = cookie.getName();
			String cookieValue = cookie.getValue();
			string = string + cookieName + "=" + cookieValue;
		}
	}
	// 找到购物车
	ArrayList<String> cart = (ArrayList<String>) session.getAttribute("cart");
	// 判断购物车是否为空
	if (cart != null) {
		// 遍历商品
		for (String itemName : cart) {
			string = string + "itemName:" + itemName;
		}
	}
	return string;
}

5.7.5分析
使用chrome,360分别访问会发现每个用户的jsessionId是不一样的

5.7.6Cookie与sessoin的关系
Session的数据是存在服务器内存中,服务器重启数据就没了。
Session是用临时cookie实现的,浏览器一关数据就没有了。如何实现在浏览器中添加购物车,在手机端查看呢?解决思路把购物商品放到数据库中,通过用户名从数据库查找购物商品。

Cookie的数据是存在浏览器中的

5.7.7删除cookie,禁用cookie

Chrome设置高级下载内容隐私设置和安全性Cookie 及其他网站数据选择“仅将本地数据保留到您退出浏览器为止”或者“阻止网站设置任何数据”

禁用后,无法通过session获取数据。
开启后,可通过session获取数据。

如果禁用cookie后,如何使用session?

5.8拦截器
5.8.1使用拦截器统计controller执行时间
在springmvc06_session的案例上增加功能
5.8.1.1需求
计算每个方法执行的时间帮助我们找出执行慢的代码。
身份验证确保没有登录的用户不能访问敏感数据。

功能分核心业务功能和扩展功能。
核心业务功能如登录,添加购物车,查询订单。
扩展功能如计算时间,身份验证,扩展功能放在Interceptor中,访问controller时自动执行。不修改controller代码就实现了扩展功能。这种方式也叫无侵入性。像保安检查快递人员体温

5.8.1.2TimeInterceptor
public class TimeInterceptor implements HandlerInterceptor{

long startTime;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {
	startTime=System.nanoTime();
	return HandlerInterceptor.super.preHandle(request, response, handler);
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
		ModelAndView modelAndView) throws Exception {
	long endTime=System.nanoTime();
	String url=request.getRequestURI();
	long useTime=endTime-startTime;
	System.out.println("url="+url+",useTime="+useTime);
}

}
5.8.1.3拦截配置
@Component//这是个组件,框架会自动创建对象
public class WebConfig implements WebMvcConfigurer{

@Override
public void addInterceptors(InterceptorRegistry registry) {
	TimeInterceptor timeInterceptor=new TimeInterceptor();
	registry.addInterceptor(timeInterceptor).addPathPatterns("/**");
	
	WebMvcConfigurer.super.addInterceptors(registry);
}

}

易犯错误
不加component

5.8.2使用拦截器完成身份验证
5.8.2.1添加UserController
@RestController
public class UserController {
@RequestMapping("/login")
public String login(String username,HttpSession session) {
//map
//111:mapusename:admin
//222:mapusename:root
session.setAttribute(“username”, username);
return “login”;
}

}
5.8.2.2添加OrderController
@RestController
public class OrderController {

@RequestMapping("/getOrder")
public String getOrder(HttpServletRequest request, HttpSession session) {
	String string = "";
	
	String username = (String) session.getAttribute("username");
	string = string + "  username=" + username;
	return string;
}

}
5.8.2.3Prehandle返回false
public class AuthInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
String url=request.getRequestURI();
System.out.println(url+" AuthInterceptor.preHandle()");

	return true;// false 不执行controller,postHandle()
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
		ModelAndView modelAndView) throws Exception {
	System.out.println("AuthInterceptor.postHandle()");

	HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}

}
5.8.2.4WebConfig添加authInterceptor

@Component//这是个组件,框架会自动创建对象
public class WebConfig implements WebMvcConfigurer{

@Override
public void addInterceptors(InterceptorRegistry registry) {
	TimeInterceptor timeInterceptor=new TimeInterceptor();
	registry.addInterceptor(timeInterceptor).addPathPatterns("/**");
	
	AuthInterceptor authInterceptor=new AuthInterceptor();		registry.addInterceptor(authInterceptor).addPathPatterns("/getOrder","/list");
	
	WebMvcConfigurer.super.addInterceptors(registry);
}

}

5.8.2.5添加登录界面
在src\main\resources\static下创建login.html

Insert title here username:
passowrd:

public class AuthInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
String url=request.getRequestURI();
System.out.println(url+" AuthInterceptor.preHandle()");

	//判断用户有没有登录
	HttpSession session=request.getSession();
	String username=(String) session.getAttribute("username");
    if (StringUtils.isEmpty(username)) {
    	response.sendRedirect("/login.html");

return false;// false 不执行controller,postHandle()
}
return true;// false 不执行controller,postHandle()
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println(“AuthInterceptor.postHandle()”);

	HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}

}

没有登录时访问/getOrder会跳到login.html,登录后,可以直接访问/getOrder。移除JSESSIONID cookie后,还要再次登录。
5.8.2.6小结
功能分核心业务功能和扩展功能。
核心业务功能如登录,添加购物车,查询订单。
扩展功能如计算时间,身份验证,日志,扩展功能放在Interceptor中,访问controller时自动执行。不修改controller代码就实现了扩展功能。这种方式也叫无侵入性。像保安检查快递人员体温

5.9案例:使用vue显示商品列表
5.9.1需求

5.9.2分析

5.9.3实现
创建项目springmvc07_itemlist

5.9.3.1后端
@RestController
public class ItemController {
@RequestMapping("/findAll")
public List findAll() {
List items = new ArrayList();
Item item1 = new Item(“手机”, 3000);
Item item2 = new Item(“电脑”, 9000);
items.add(item1);
items.add(item2);
return items;
}

}

5.9.3.2前端

Insert title here
名称价格
{{item.name}}{{item.price}}

5.10商城前后台
5.10.1需求

5.10.2实现
创建项目springmvc08_mallproject
5.10.2.1创建Itemcontroller
@RestController
public class ItemController {
// 以后用数据库存放商品
static ArrayList itemList = new ArrayList();

//添加商品
@RequestMapping("/item/insert")
public String insert(String itemName) {
itemList.add(itemName);
return itemName + “添加成功”;
}

// 查询商品
@RequestMapping("/item/selectAll")
public ArrayList<String> selectAll() {
	return itemList;
}

// 删除商品
@RequestMapping("/item/delete")
public String delete(String itemName) {
	itemList.remove(itemName);
	return itemName + "删除成功";
}

// 修改商品
@RequestMapping("/item/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 oldItemName + "修改成功";
}

}
5.10.2.2后台管理添加

Insert title here

后台管理系统

添加
</div>

}
var vue=new Vue(config);

5.10.2.3后台管理查询 Insert title here
添加
名称删除修改
{{itemName}}删除修改

}
var vue=new Vue(config);

5.10.2.4后台管理删除 Insert title here
添加
名称删除修改
{{itemName}}删除修改

}
var vue=new Vue(config);

5.10.2.5后台管理修改

Admin.html

修改

Update.html显示要修改的商品

Insert title here
修改
</div>
修改商品 Insert title here
修改
</div>

5.10.2.6前后显示商品
下载图片放在src\main\resources\static文件夹中

Insert title here

网站首页

		<tr v-for="itemName in itemList">
			<td>
			<img src="img1.jpg"> <br />
			 {{itemName}}
			 </td>

		</tr>
	</table>
</div>

}
var vue=new Vue(config);

5.10.2.7查看服务器返回内容为json
第一次请求,服务器返回html.

第二次请求服务器返回的是json

5.11案例:使用jsp显示商品列表

Jsp:java server page用java来显示数据。

创建项目springmvc09_jsp
5.11.1添加依赖

org.apache.tomcat.embed
tomcat-embed-jasper

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
</dependency>

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
 </dependency>

5.11.2创建Item
创建controller包,在controller包下创建Item.java
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;
}

}

5.11.3创建jsp
在src\main下创建文件夹webapp\WEB-INF\jsp

右键创建list.jsp,修改字符集为UTF-8
<%@ page language=“java” contentType=“text/html; charset=UTF-8”
pageEncoding=“UTF-8”%>

Insert title here jsp

5.11.4修改application.properties
设置jsp的前缀和后缀。强制把jsp放在/WEB-INF/jsp文件夹中。设置后在controller通过文件名list就可以访问/WEB-INF/jsp/list.jsp。

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

5.11.5创建controller
//@restController返回json
@Controller//返回jsp界面
public class ItemJspController {
@RequestMapping("/listItem")
public ModelAndView listItem() {
//返回web-inf/jsp/list.jsp,View指的是界面,就是jsp
ModelAndView modelAndView=new ModelAndView(“list”);
return modelAndView;
}
}

浏览器调试窗口中查看返回为html
5.11.6Jstl
//@restController返回json
@Controller//返回jsp界面
public class ItemJspController {
@RequestMapping("/listItem")
public ModelAndView listItem() {

	ModelAndView modelAndView=new ModelAndView("list");
	List<Item> itemList = new ArrayList<Item>();
	Item item1 = new Item("手机", 3000);
	Item item2 = new Item("电脑", 9000);
	itemList.add(item1);
	itemList.add(item2);
	
	modelAndView.addObject("itemList", itemList);

	return modelAndView;	
}}

JSTL(Java server pages standarded tag library,即JSP标准标签库),使用jstl可以很方便的在jsp中显示java数据,拷贝taglib标签。Jsp修改后要重启服务器,才生效
<%@ page language=“java” contentType=“text/html; charset=UTF-8”
pageEncoding=“UTF-8”%>

<%@ taglib prefix=“c” uri=“http://java.sun.com/jsp/jstl/core”%>

Insert title here jsp2
名称价格
${item.name}${item.price}
5.11.7查看服务器返回内容为html

5.11.8Controller返回json
添加responsebody
@Controller
public class ItemJspController {

@RequestMapping("/listItem2")
//restController=controller+responsebody
@ResponseBody//如果类名前加restController,方法返回json就不用@responseBody
public List<Item> listItem2() {		
	
	List<Item> items = new ArrayList<Item>();
	Item item1 = new Item("手机", 3000);
	Item item2 = new Item("电脑", 9000);
	items.add(item1);
	items.add(item2);
	
	return items;
}	

}
5.11.9小结
使用vue,浏览器先请求html,网页加载完之后,再用axios发请求,得到json,有数据绑定把数据显示在网页上。
使用jsp,浏览器发一次请求,在服务器上生成html,浏览器得到的是html,直接显示在网页上。
Vue是前台web与后台数据分离。有助于专业化。后台程序员专门用java做后台开发,前台程序员用vue,js专门做前台。推荐用vue。
老项目用jsp。
5.12拓展:跳转
5.12.1状态码介绍
查看200,404

5.12.2redirect 重定向
创建项目springmvc10_page
适用于原先的网页不继续提供服务了
案例:www.jd.com有个登录页,后来有多个网站,需要统一登录。
@Controller
public class UserController {
//重定向,原先的登录地址
@RequestMapping("/login")
public String login() {
return “redirect:https://passport.jd.com/new/login.aspx”;
}
}
http://localhost:8080/login

转发后浏览器的地址栏变为转发后的地址。
在chrome的调试窗口中network中查看status code

5.12.3forward 转发
转发后浏览器地址栏还是原来的地址。所以转发前请求的参数在转发后仍然可以读取到。
如下例子:
//第一输入用户名和密码
@RequestMapping("/loginByName")
@ResponseBody
public String loginByNamePwd(String username,String password) {

	return "login";
}

//再次登录时,还要输入验证码
@RequestMapping("/loginByCode")
public String loginByCode(String username,String password,String code) {
	//判断验证码是否正确
	return "forward:/loginByName";
}

使用断点跟踪会发现username传到loginByNamePwd方法中了。

5.12.4小结
重定向redirect和转发forward的区别
1,重定向浏览器地址栏显示新的地址,转发地址栏不变
2,重定向可以重定向别的网站,转发只能转发到本网站中的方法。
5.12.5RESTFul架构支持
RESTFul要求通过url传输数据

https://blog.csdn.net/qq_34337272/article/details/108347545
https://www.zhihu.com/question/328810338/answer/720393487
普通传递参数的方法是 url?itemName=手机
5.12.5.1接收单个参数
@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上
形成RUSTFul形式,这将是未来的发展趋势。它更加简洁,安全。强化URL GET访问方式。
controller中代码
@RestController
public class OrderCotroller {
@RequestMapping("/order/{startTime}/{endTime}")
public String searchByDate(@PathVariable String startTime,
@PathVariable String endTime) {
return “restful startTime=”+startTime+",endtime="+endtime;
}
}

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

6三大框架:业务层框架Spring+IoC+DI
6.1初识Spring框架
业务层主要处理业务逻辑如
注册时在业务层判断用户名是否存在。
获取商品价格时,判断用户是否是会员,商品有没有参加店铺活动,有没有参加商城活动。

6.1.1Spring的野心
springMVC作为WEB框架深受企业爱戴,它会自己管理Controller,来创建其实例。Mybatis作为持久层优秀的框架,它也自己管理持久对象。可以看到,各个诸侯都自己管理对象,而要想让它们对象复用,那真是繁琐。
如何破局呢?要想发号施令其它人听,最好的解决办法就是扼住他们的咽喉。在java的世界里最重要的无疑就是对象的生命周期管理。于是spring以此为切入点,实现自己的统治。宣布所有对象由我来管理,springMVC你不再管理对象,由我来管理,你要用从我这拿。Mybatis你也不再管理对象,由我来管理,你要用从我这拿。你说管就管吗?这两个征战数年战功赫赫的大将军会听一个初出茅庐乳臭未干野小子的话?他们当然不会听,spring的话可以不听,但他们都要听开发者的。开发一个完整的系统有四个核心,WEB层支持、业务逻辑层、持久层支持、事务支持。而这就是它们的软肋,这就是它们的命门所在,它们只能完成一部分工作,不是一个整体解决方案。而spring并没有抹杀它们,而是依然高官厚禄,承认它们的市场地位,还赠与一个事务管理。一边打压一边拉拢,它们两位看看大势已去,只能俯首称臣。于是兵不血刃,一场变革悄然兴起,一个经典的三层框架诞生SSM (SpringMVC+Spring+Mybatis)。
故事很传奇,听的人很开心。可spring真就这么简单吗?如果这样想,你就大错特错了。例如:spring怎么来实现对象的管辖?怎么让不同技术之间能简单的互相配合?这才是spring的决胜之处。
为实现这些spring是煞费苦心,创新的形成了一套新的理论体系,可谓前无古人后无来者。其中最核心的是:IoC控制反转、DI依赖注入、SpringAOP面向切面编程、事务控制。
6.1.2框架组成
Spring官网: http://spring.io
Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。Spring框架的不光是技术牛,而是它的核心思想更牛,它不重复发明轮子,而是“拿来主义”,把业界做的最好的技术黏合起来形成一个强大的企业级的应用框架。

6.2用springboot方式实现ioc
6.2.1需求

6.2.2分析步骤
Start.spring.io生成项目springioc01_userService
eclipse导入项目
创建controller,service包
创建UserService,UserServiceImpl
创建UserController
浏览器测试
6.2.3实现
6.2.3.1创建UserService,UserServiceImpl
package com.tedu.springioc01.service;

public interface UserService {
public String register();
}

package com.tedu.springioc01.service;

import org.springframework.stereotype.Service;

//这是一个业务层类,由框架创建对象
@Service
public class UserServiceImpl implements UserService{

@Override
public String register() {
	//判断用户名是否注册过
	return "注册成功";
	
}

}
6.2.3.2创建UserController
@RestController
public class UserController {

//由框架给userService赋值,不用程序员创建
@Autowired
UserService userService;

@RequestMapping("/register")
public String register() {
	String result=userService.register();
	return result;
}

}
6.2.3.3浏览器测试
http://localhost:8080/register
debug跟踪流程

6.2.4小结
这就是spring框架的IoC,控制反转。之前我们自己new出新类。new UserService(),现在由容器提供。
在java范畴中万物皆Object,在Spring中万物皆Bean。Bean是Spring的核心、基础、根源。
上面的方式很多同学会觉得不以为然,感觉还没有我们传统方式实现方便呢,new个对象不就可以了。凭空多个什么IoC,什么DI乱七八糟的感觉。是那样吗?不光大家这样觉得,业界开始时也对它嗤之以鼻,但随着Spring3.0的推出,随着注解方式的成为市场主流编程方式,它的力量开始展现,让我们这些俗人开始仰慕!这其中也包括我。
要不断的去适应ioc方式

6.3Spring框架原理
6.3.1分析

6.3.2实现

6.3.2.1注解
@Target(ElementType.FIELD)//注解加在属性上
@Retention(RetentionPolicy.RUNTIME)//运行时有效
public @interface Autowired {

}
6.3.2.2Service

package com.tedu.mall;

public class UserService {

}
6.3.2.3Controller
package com.tedu.mall;

import org.spring.ioc.Autowired;

public class UserController {
@Autowired
UserService userService;

}
6.3.2.4框架
package org.spring.ioc;

public class SpringIOCMain {
static HashMap<String, Object> container = new HashMap<String, Object>();

public static void main(String[] args) throws Throwable {
	loadObject();
	autowiredProcess();

	UserController userController = (UserController) container.get("userController");
	System.out.println(userController);
}
private static void loadObject() {
	UserController userController = new UserController();
	container.put("userController", userController);

	UserService userService = new UserService();
	container.put("userService", userService);
}
private static void autowiredProcess() throws Throwable {
	// 遍历容器的所有元素
	for (String name : container.keySet()) {
		// 取到每个对象
		Object object = container.get(name);

		// 判断每个属性有没有加autowired
		Class clazz = object.getClass();
		Field[] fields = clazz.getDeclaredFields();

		for (Field field : fields) {

			Autowired autowired = field.getAnnotation(Autowired.class);
			if (autowired != null) {
				// 从容器中取个对象给属性赋值
				String fieldName = field.getName();
				Object value = container.get(fieldName);

				field.setAccessible(true);
				field.set(object, value);
			}
		}

	}

}	

}

6.4面试题

6.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实现对象关系管理。
6.4.2什么是自动装配
利用注解方式,我们只需要写@Autowired注解,底层就会去容器中找对应的对象,如果有获取到,反射调用其对应的set方法,设置。而这个调用过程都是自动,我们没有手工去写set方法。所以这个过程也称为自动装配。
7三大框架:业务层框架Spring+动态代理+AOP
7.1静态代理
7.1.1分析

7.1.2实现
项目是springAop01_staticProxy

public interface IDAO {
public void insert();
}

//目标类
public class UserDAO implements IDAO {
@Override
public void insert() {
System.out.println(“目标类UserDAO 的核心代码 insert”);
}
}

//目标类
public class OrderDAO implements IDAO {
@Override
public void insert() {
System.out.println(“目标类OrderDAO 的核心代码 insert”);
}
}

//代理类的方法必须和目标类的方法一致
public class Proxy implements IDAO {
IDAO target;

public Proxy(IDAO target) {
	this.target = target;
}

@Override
public void insert() {
	long startTime = System.nanoTime();
	target.insert();
	long endTime = System.nanoTime();
	System.out.println("insert占用的时间=" + (endTime - startTime));

}

}

public class TestProxy {
public static void main(String[] args){
//创建目标对象
IDAO userDAO=new UserDAO();
//得到代理对象
Proxy userDAOproxy=new Proxy(userDAO);
//调用代理的方法
userDAOproxy.insert();

    IDAO orderDAO=new OrderDAO();
    Proxy orderDAOProxy=new Proxy(orderDAO);
    orderDAOProxy.insert();
    
}

}
7.1.3小结
假如有1000个类,不用代理,需要在每个类的每个方法中得时间,使用代理后,1000个类不做任何修改,只需要在代理中完成。 代理是java中的一个强大功能。
增加身份验证是在目标类中完成还是在代理类中完成呢?
增加日志是在目标类中完成还是在代理类中完成呢?

使用了静态代理后,增加扩展功能需要修改目标类吗?实现了无侵入性编码吗?

7.2复习用反射获取方法

创建项目springAop02_method
public interface IDAO {
public void save();

public void select();

}

public interface IMail {
public void sendMail();
}

public class UserDAO implements IDAO, IMail {

@Override
public void sendMail() {
	// TODO Auto-generated method stub

}

@Override
public void save() {
	// TODO Auto-generated method stub

}

@Override
public void select() {
	// TODO Auto-generated method stub

}

}

public class TestMethod {

public static void main(String[] args) {
	//生成代理类,需要得到接口中的方法 
	UserDAO userDAO=new UserDAO();
	Class clazz=userDAO.getClass();//得到UserDAO的类对象
	
	//得到类的所有接口的类对象
	//类对象中包含的有方法信息,接口有方法,接口也有类对象。
	Class[] interfaces=clazz.getInterfaces();
	for (Class c:interfaces) {
		String interfaceName=c.getName();
		System.out.println(interfaceName);
		//得接口的方法
		Method[] methods=c.getDeclaredMethods();
		for (Method method:methods) {
			System.out.println("   "+method.getName());
		}
	}	

}

}

7.3复习类加载器

public class UserDAOProxy {

}

public class TestApp {

public static void main(String[] args) throws Throwable{
	TestApp testApp=new TestApp();
	Class clazz=testApp.getClass();//得到类对象
	ClassLoader classLoader=clazz.getClassLoader();//得到类加载器
	
	System.out.println(classLoader);
	
	//用类加载器加载文件不用指定盘符
	//config.txt必须放在src目录中,不能放在包下
	InputStream inputStream=classLoader.getResourceAsStream("config.txt");
	int length=inputStream.available();
	byte[] data=new byte[length];
	inputStream.read(data);
	inputStream.close();
	String className=new String(data);
	System.out.println(className);
	
	//用类加载器加载类
	//得到类对象有4种方式 1,对象.getClass() 2,classLoader.loadClass 3,class.forName()
	Class proxyClazz=classLoader.loadClass(className);
	Object object=proxyClazz.newInstance();
	System.out.println(object);
	//类加载器主要用在框架中
	
}

}

7.4Jdk动态代理实现
静态代理是程序员为每个接口写一个代理类,接口如果很多,代理类也很多。
动态代理是用java生成代理类
7.4.1实现动态代理
public interface IDAO {
public void select();
public int insert(String username,String password);

}

//目标类
public class UserDAO implements IDAO{

@Override
public void select() {
	//System.out.println("log");
	System.out.println("select");
	
}

@Override
public int insert(String username, String password) {
	System.out.println("insert username="+username+" passowrd="+password);
	return 1;
}

}

public class AppMain {

public static void main(String[] args) {

	// 创建目标对象
	UserDAO target = new UserDAO();

	// 根据目标得到代理对象
	Object prxoyObject = getProxy(target);
	//测试代理对象
	Class proxyClazz=prxoyObject.getClass();
	System.out.println(proxyClazz.getName());
	Method[] methods=proxyClazz.getDeclaredMethods();
	for (Method method:methods) {
		System.out.println(method.getName());
	}
	
	
	
	// 类型转换
	IDAO idao = (IDAO) prxoyObject;
	// 调用代理对象的方法,框架会调用invocationHanlder的实现类的invoke
	// invoke中执行要重用的代码,再执行目标对象的方法
	idao.select();
	int rowCount = idao.insert("root", "root");
	System.out.println("rowCount=" + rowCount);
}

// 使用jdk的动态代理框架生成代理对象
// target指向的是userdao
public static Object getProxy(IDAO target) {
	// Class是个类 ,类的对象放的是类或接口的方法信息
	// 得到userdao的方法信息,有select(),insert(),
	// 动态类就有select(invocationHandler接口),insert()
	Class[] methodInfo = target.getClass().getInterfaces();
	ClassLoader classLoader = target.getClass().getClassLoader();
	TimeInvocationHandler handler = new TimeInvocationHandler(target);
	// 生成代理对象,

	// 每1个参数是类加载器
	// 第2个参数是方法信息
	Object proxyObject = Proxy.newProxyInstance(classLoader, methodInfo, handler);

	return proxyObject;
}

static class TimeInvocationHandler implements InvocationHandler {

	IDAO target;// userdao

	public TimeInvocationHandler(IDAO target) {
		super();
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		long startTime = System.nanoTime();

		// 调用目标方法
		// 以前userdao.select()
		Object result = method.invoke(target, args);

		long endTime = System.nanoTime();
		System.out.println("time=" + (endTime - startTime));
		return result;
	}

}

}

7.4.2测试代理对象
//测试代理对象
Class proxyClazz=prxoyObject.getClass();
System.out.println(proxyClazz.getName());
Method[] methods=proxyClazz.getDeclaredMethods();
for (Method method:methods) {
System.out.println(method.getName());
}
7.4.3测试invoke参数
用断点查看invoke方法args参数
7.4.4小结
下面的过程有点绕哦,多练习多思考!
实现InvocationHandler接口
调用Proxy.newProxyInstance创建代理对象
通过代理对象调用方法时,会自动的回调上面的InvocationHandler实现类的invoke方法

动态代理和静态代理都是代理,实现了无侵入性编码,增加功能时,不用修改目标类代码。

7.5AOP面向切面编程
7.5.1概念
Spring核心特征中除了IoC控制反转、DI依赖注入,还有一个核心就是强大的面向切面编程AOP(Aspect Oriented Programming)的实现。
AOP是对动态代理的封装。

7.6SpringAOP
7.6.1需求
统计userService执行的时间

7.6.2分析
7.6.3创建springboot项目
官网:https://start.spring.io/

创建项目springaop05_time

7.6.4Pom.xml中添加aop依赖
注意需要自己手动加上,整合aspectJ

org.springframework.boot
spring-boot-starter-aop

7.6.5 Service
public interface UserService {
public String register();
}

@Service//UserServiceImpl由容器管理,容器会自动创建对象
public class UserServiceImpl implements UserService{

@Override
public String register() {
	
	return "注册成功";
}

}

7.6.6Controller.java
@RestController
public class UserController {

@Autowired
UserService userService;

@RequestMapping("/register")
public String register() {
	String resultResult=userService.register();
	return resultResult;
}

}

7.6.7浏览器测试

7.6.8TimeAspect.java
package com.tedu.springaop05_time.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 TimeAspect {
long startTime;

//第一个 * 表示返回数据类型可以是任意类型
//第二个 * 表示是任意类
//第三个 * 表示是任意方法
// … 表示是任意参数
@Pointcut(“execution( public * com.tedu.springaop05_time.controller..(…))”)
public void aopPointCut() {
}

//通过aopPointCut()找到execution
@Before(“aopPointCut()”)
public void getStartTime() {
startTime = System.nanoTime();
}

@After("aopPointCut()")
public void getEndTime() {
	long endTime = System.nanoTime();
	System.out.println("time2=" + (endTime - startTime));
}

}

Pointcut中的括号很容易写错,参考下图写正确。

7.6.9RunApp.java
@SpringBootApplication
public class Springaop05TimeApplication {

public static void main(String[] args) {
	SpringApplication.run(Springaop05TimeApplication.class, args);
}

}
7.6.10小结
给UserServiceimpl增加功能,需要修改UserServiceImpl代码吗?
AOP切面就是对动态代理的封装,让我们容易使用动态代理,自动执行的代码,放扩展代码,是个新名词
静态代理,动态代理,AOP都实现了无侵入式编码,增加功能不用修改业务代码。工作中推荐用AOP。
7.7Spring小结
Spring主要作用有两个,
1,管理service层对象。
2,AOP,对动态代理做个封装。

8三大框架:持久层框架MyBatis
8.1传统框架的缺点
使用jdbc需要程序员创建连接,手写sql,处理结果集,使用了mybatis框架后,创建连接,结果集处理都由框架来完成。

8.2mybatis介绍
mybatis它是轻量级持久层框架,由ibatis演化而来。它自动连接数据库,将数据库的结果集封装到对象中POJO。
8.3创建数据库

记事本打开课前资料中\code\mall.sql,拷贝到查询编辑器中,右键执行查询Execute Current Query

选中服务器右键刷新。

出现mall数据库有5张表。

/*
SQLyog 企业版 - MySQL GUI v8.14
MySQL - 5.7.17-log : Database - mall


*/

/*!40101 SET NAMES utf8 */;

/!40101 SET SQL_MODE=’’/;

/!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 /;
/
!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 /;
/
!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE=‘NO_AUTO_VALUE_ON_ZERO’ /;
/
!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 /;
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’);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE /;
/
!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS /;
/
!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS /;
/
!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
8.4逆向工程
通过逆向工程生成sql语句。

8.4.1解压code\generator原始.rar
8.4.2修改根目录下的generatorConfig.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
	<!-- JavaBean 实现 序列化 接口 -->
	<plugin type="org.mybatis.generator.plugins.SerializablePlugin">
	</plugin>
	<!-- genenat entity时,生成toString -->
	<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
	<!--修改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" />
</context>
8.4.3执行GeneratorApp生成xml和实体类

执行GeneratorApp后,选中项目右键refresh,查看生成的xml和类如下图

8.4.4查看生成的sql和接口
8.4.4.1查看CategoryMapper接口
8.4.4.2打开categoryMapper.xml查看生成的sql。
8.4.4.3理解如何通过接口找到sql
接口和xml有以下要求:
1, mapper.xml的namespace必须和接口的名称一样
2、 mapper.xml中定义的每个sql的id和Mapper接口方法名一样
3、 mapper.xml中定义的每个sql 的parameterType的类型和Mapper接口方法的输入参数类型一样
4、 mapper.xml中定义的每个sql的resultType的类型和Mapper接口方法的输出参数类型和一样。

8.4.4.4打开实体类Category

8.5商品分类CRUD操作
8.5.1创建项目
项目是mybatis_01jtmall,添加Spring Web,MyBatis Framework,MySql Driver依赖。
包名是com.tedu.jtmall,包名要和逆向工程生成的包名一致。

8.5.2在资源管理器拷贝mapper和 pojo
Idea中mapper.xml要放在resources目录中,与eclipse不一样

8.5.3添加包扫描
当我们从容器中获取mapper.java的对象时,得到的是代理对象,代理对象是由框架创建的,mapper接口没有加注解,只能通过MapperScan告诉框架给Mapper接口创建代理对象。
@SpringBootApplication
//框架为com.tedu.jtmall.mapper包下的接口自动创建代理对象
@MapperScan(“com.tedu.jtmall.mapper”)
public class JtmallApplication {

public static void main(String[] args) {
	SpringApplication.run(JtmallApplication.class, args);
}

}
8.5.4拷贝数据库配置
在src\main\resources\下创建文件application.yml,拷贝下面的内容。
server:
port: 8080

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
配置信息可以放在application.properties或application.yml中
8.5.5添加数据
分析xml

@RestController
public class CategoryController {
@Autowired
CategoryMapper categoryMapper;

@RequestMapping("/cat/insert")
public String insert(String categoryName) {
	// 创建实体类
	Category category = new Category();
	category.setCategoryName(categoryName);
	// 调用接口的代理对象,框架会生成insert语句
	int rowCount = categoryMapper.insert(category);
	return "rowCount=" + rowCount;
}

}
http://localhost:8080/cat/insert?categoryName=mobile

分析sql执行过程
根据接口名和方法名找到sql语句

接口和xml有以下要求:
1, mapper.xml的namespace必须和接口的名称一样
2、 mapper.xml中定义的每个sql的id和Mapper接口方法名一样
3、 mapper.xml中定义的每个sql 的parameterType的类型和Mapper接口方法的输入参数类型一样
4、 mapper.xml中定义的每个sql的resultType的类型和Mapper接口方法的输出参数类型和一样。

8.5.6源码分析
8.5.6.1查看pom.xml发现mybatis版本是3.5.5
8.5.6.2Controller中加断点查看代理
8.5.6.3在MapperProxy中查看断点
Ctrl+shift+t查看mapperProxy源码

8.5.7执行过程

8.5.8查询所有数据
分析xml
@RequestMapping("/cat/findAll")
public List findAll() {
// 调用接口的代理对象,框架会生成select语句
List list = categoryMapper.selectByExample(null);
return list;
}

http://localhost:8080/cat/findAll
8.5.9安装jsonview
参考课前资料中的software\jsonview\chrome中安装jsonView.doc安装jsonview
8.5.10查询一个数据
@RequestMapping("/cat/findById")
public Category findById(Integer categoryId) {
// 调用接口的代理对象,框架会生成select where语句
Category category = categoryMapper.selectByPrimaryKey(categoryId);
return category;
}

http://localhost:8080/cat/findById?categoryId=2
8.5.11修改
@RequestMapping("/cat/update")
public String update(Integer categoryId, String categoryName) {

	// 创建实体类
	Category category = new Category();
	category.setCategoryId(categoryId);
	category.setCategoryName(categoryName);
	// 调用update方法
	// 生成update category set categoryName=categoryName
	// where categoryId=
	int rowCount = categoryMapper.updateByPrimaryKey(category);
	return "rowCount=" + rowCount;
}

http://localhost:8080/cat/update?categoryId=4&categoryName=aaa
8.5.12删除
@RequestMapping("/cat/delete")
public String delete(Integer categoryId) {
// 生成delete where categoryId
int rowCount = categoryMapper.deleteByPrimaryKey(categoryId);
return “rowCount=” + rowCount;
}

http://localhost:8080/cat/delete?categoryId=4
8.6使用vue完成添加,显示,删除界面
8.6.1界面分析

8.6.2实现
8.6.2.1拷贝vue.js, axios.min.js

8.6.2.2双向绑定

Insert title here
添加
浏览器console中修改值,界面也变。

8.6.2.3查询所有分类

添加
{{category.categoryName}} 删除

8.6.2.4添加数据
@RequestMapping("/cat/insert2")
//浏览器端发送json,服务器端必须通过对象来接收,还要加上@RequestBody
//使用post发送json比用get发数据功能强大
public String insert2(@RequestBody Category category) {
// 创建实体类
// Category category = new Category();
// category.setCategoryName(categoryName);
// 调用接口的代理对象,框架会生成insert语句
int rowCount = categoryMapper.insert(category);
return “rowCount=” + rowCount;
}

添加
{{category.categoryName}} 删除
8.6.2.5查看发送的json数据

8.6.2.6删除

添加
{{category.categoryName}} 删除

9三大框架:持久层框架MyBatis
9.1关联关系

Mybatis表现关联关系只有两种association(一对一)、collection(一对多),表现很简洁。
注意:关联出的结果集不能有同名字段,否则mybatis无法正确映射。
如果同名,Mybatis会自动第一次出现的值再次放到对象的属性中,这样业务逻辑就将错误。

9.1.1一对一
9.1.1.1需求:
用户和订单信息一对一关联;查询用户和订单的扩展信息。
User表

jt_Order表

SELECT order_id ,total,user.user_id ,username FROM jt_order,USER
WHERE jt_order.order_id=1 AND user.user_id=jt_order.user_id

9.1.1.2分析

9.1.1.3创建项目
创建项目mybaits02_multiTable

9.1.1.4创建User类
在动态sql的项目中添加User类

public class User {
int userId;
String username;

public int getUserId() {
	return userId;
}
public void setUserId(int userId) {
	this.userId = userId;
}
public String getUsername() {
	return username;
}
public void setUsername(String username) {
	this.username = username;
}

}
9.1.1.5创建Order类

public class JtOrder {
int orderId;
int userId;
int total;
User user;//一个订单属于一个用户,

public User getUser() {
	return user;
}
public void setUser(User user) {
	this.user = user;
}
public int getOrderId() {
	return orderId;
}
public void setOrderId(int orderId) {
	this.orderId = orderId;
}
public int getUserId() {
	return userId;
}
public void setUserId(int userId) {
	this.userId = userId;
}
public int getTotal() {
	return total;
}
public void setTotal(int total) {
	this.total = total;
}

}

9.1.1.6创建jtOrderMapper
package com.tedu.multitable.mapper;

import com.tedu.multitable.pojo.JtOrder;

public interface JtOrderMapper {

//查询订单和订单用户信息
public JtOrder select(Integer orderId);

}
9.1.1.7增加mapperScan
@SpringBootApplication
@MapperScan(“com.tedu.multitable.mapper”)
public class MultiTableApplication {

public static void main(String[] args) {
	SpringApplication.run(MultiTableApplication.class, args);
}

}
9.1.1.8创建jtOrderMapper.xml

resultMap的属性:
属性 作用
type 配置查询出的结果集要封装到哪个实体对象中
id 主键;
property POJO对象的属性;
column 查询结果集对应的字段名
result 普通字段
property POJO对象的属性
column 查询结果集对应的字段名
extends 可以继承,这样可以复用,方便维护
association 对一关联,内部结构和单个对象的resultMap结构相同,可以复制
collection 对多关联,内部结构和单个对象的resultMap结构相同,可以复制

在com.tedu.mutlitable.mapper包中创建JtOrderMapper.xml,拷贝dtd url,不拷贝dtd url没有标签提示。

</resultMap>
<select id="select" parameterType="Integer" resultMap="orderUserMap">
	SELECT order_id ,total,user.user_id ,username FROM jt_order,USER
	WHERE jt_order.order_id=#{orderId} AND user.user_id=jt_order.user_id
</select>
9.1.1.9拷贝数据库配置 拷贝application.yml,修改MapperLocation。 9.1.1.10调用 @RestController public class OrderController {
@Autowired
JtOrderMapper jtOrderMapper;

@RequestMapping("/order/select")
public JtOrder select() {
	JtOrder jtOrder = jtOrderMapper.select(1);
	return jtOrder;
}

}

http://localhost:8080/order/select
9.1.1.11常见错误
没写resultMap
9.1.2一对多
9.1.2.1需求:
商品分类表

商品详情表

SELECT category.category_id,category.category_name,item_name
FROM category,item
WHERE category.category_id=1 AND item.category_id=category.category_id

9.1.2.2分析

9.1.2.3创建category类
public class Category {
int categoryId;
String categoryName;
List itemList;
public int getCategoryId() {
return categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public List getItemList() {
return itemList;
}
public void setItemList(List itemList) {
this.itemList = itemList;
}

}
9.1.2.4创建Item类
package com.tedu.multitable.pojo;

//商品
public class Item {
String itemName;

public String getItemName() {
	return itemName;
}

public void setItemName(String itemName) {
	this.itemName = itemName;
}

}
9.1.2.5创建CategoryMapper
package com.tedu.multitable.mapper;

import com.tedu.multitable.pojo.Category;
//操作分类表的接口
public interface CategoryMapper {
//根据id查询某个分类
public Category select(Integer categoryId);
}
9.1.2.6创建ItemMapper.xml

</resultMap>
<!-- parameterType 参数类型  resultMap定义结果集中的列对应那个实体类的那个属性上 -->
<select id="select" parameterType="Integer" resultMap="categoryItemMap">
	SELECT category.category_id,category_name,item_name FROM category,item
	WHERE category.category_id=#{categoryId}
</select>
9.1.2.7调用 @RestController public class CategoryController {
@Autowired
CategoryMapper categoryMapper;

@RequestMapping("/cat/select")
public Category select() {
	Category category=categoryMapper.select(1);
	return category;
}

}

http://localhost:8080/cat/select

9.2参数占位符
mybatis本质就是拼接SQL语句。
9.2.1#{name}
使用的jdbc的ParpareStatment。#{} 很赞,防止SQL注入;如果参数是一个字符串类型。chen 拼接SQL语句时会根据类型,自动加相关符号。例如字符串类型’chen’。
9.2.2${orderby}
${} 原样输出,很危险,有SQL注入风险。
9.3复杂搜索案例
9.3.1Baidu高级搜索

9.3.2github.com/search/advanced

9.4动态SQL语句

9.4.1创建新项目
创建项目mybatis03_dynamicSql

9.4.2测试sql
select
item_id AS itemId,
category_id AS categoryId,
item_name AS itemName
from item
9.4.3创建Item
package com.tedu.dynamicSql.pojo;

//商品
public class Item {
String itemName;
Integer itemId;

public Integer getItemId() {
	return itemId;
}

public void setItemId(Integer itemId) {
	this.itemId = itemId;
}

public String getItemName() {
	return itemName;
}

public void setItemName(String itemName) {
	this.itemName = itemName;
}

}
9.4.4手写Mapper接口

package com.tedu.dynamicSql.mapper;

public interface ItemMapper {

public List<Item> findAll(Item item);

}
9.4.5添加mapperscan
@SpringBootApplication
@MapperScan(“com.tedu.dynamicSql.mapper”)
public class DynamicSqlApplication {

public static void main(String[] args) {
	SpringApplication.run(DynamicSqlApplication.class, args);
}

}
9.4.6手写mapper.xml

<select id="findAll" resultType="com.tedu.dynamicSql.pojo.Item"
	parameterType="com.tedu.dynamicSql.pojo.Item">
	<!-- select * from item查不到列的数据,必须加列名,在sqlyog中测试sql -->
	select item_id AS itemId,category_id AS categoryId,item_name AS itemName,item_price AS
	itemPrice from item;
</select>

9.4.7拷贝application.yml
修改mapperLocations
server:
port: 8080

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.dynamicSql.mapper/*.xml

logging:
path: ./logs
level:
com.tedu.dynamicSql.mapper: debug

9.4.8创建controller,测试
@RestController
public class ItemController {

@Autowired
ItemMapper itemMapper;

@RequestMapping("/findAll")
public List<Item> findAll(Item item){
	List<Item> itemList=itemMapper.findAll(item);
	return itemList;
}

}
http://localhost:8080/findAll

9.4.9动态sql


select category_id AS categoryId,item_name AS itemName,item_price AS
itemPrice from item
where

		<!--#{categoryId}是占位符,值来自参数 -->
		<if test="categoryId != null">
			category_id=#{categoryId}
		</if>

		<if test="itemName != null ">
			<!--'%#{itemName}%' -->
			and item_name like '%'+#{itemName}+'%'
		</if>

	where
</select>

http://localhost:8080/findAll?categoryId=1&itemName=新
会报错,需要使用sql函数CONCAT

SELECT CONCAT(’%’,‘a’,’%’)


and item_name like concat(’%’,#{itemName},’%’)

http://localhost:8080/findAll?itemName=新
会报错,生成sql如下
select category_id AS categoryId,item_name AS itemName,item_price AS itemPrice from item where and item_name like concat(’%’,?,’%’)

http://localhost:8080/findAll 会报错,生成sql如下
select category_id AS categoryId,item_name AS itemName,item_price AS itemPrice from item where

category_id=#{categoryId}
		<if test="itemName != null ">
			<!--'%#{itemName}%' -->
			and item_name like concat('%',#{itemName},'%')
		</if>
	</where>

9.4.10常见错误
找不到sql语句
Invalid bound statement (not found):
原因是
.xml文件名不对和namespace,id不正确。
9.5集合参数
9.5.1测试sql
select item_id as itemId,category_id AS categoryId,item_name AS itemName,item_price AS
itemPrice from item where item_Id in (2,3)
9.5.2List集合
public interface ItemMapper {

public List<Item> findAll(Item item);
public List<Item> findByList(List<Integer> list);

}


select item_id as itemId,category_id AS categoryId,item_name AS
itemName,item_price AS
itemPrice from item

item_id in

#{id}


@RequestMapping("/findByList")
public List findByList() {

	ArrayList<Integer> idList= new ArrayList<Integer>();
	idList.add(2);
	idList.add(3);
	
	List<Item> itemList = itemMapper.findByList(idList);

	return itemList;
}

http://localhost:8080/findByList

9.5.3查看逆向工程生成的动态sql









and ${criterion.condition}


and ${criterion.condition} #{criterion.value}


and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}


and ${criterion.condition}

#{listItem}









9.6拓展:优化,使代码更加简洁
前面讲了CRUD的全部测试,下面是对CRUD示例的优化:
9.6.1
可以在配置文件中调用的地方都进行include引用
id,name,birthday,address

<select id="list" resultType="user">
	select <include refid="cols"/> from user
</select>

9.6.2SQL中有特殊字符
当SQL中有特殊字符,mybatis不能正常解析时,用CDATA括起来就解决了
<![CDATA[ and age<=#{age} ]]>
9.7总结
9.7.1MVC和SSM的关系
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码

Controller在小型项目中作用不明显,controller相当于厨师长,分配谁来做菜。
10项目实战:Lombok+MybatisPlus+整合SSM
10.1MybatisPlus支持
10.1.1概念

Mybatis直接在映射文件xml中通过SQL语句操作数据库,这种方式非常灵活,特别对于多表联查极度复杂的SQL,但日常表的CRUD,特别表字段比较多的情况下,需要写大量的xml文件,很是麻烦,当字段修改时,非常繁琐。mybatis-plus(MP)就很好的解决了这个问题,单表CRUD操作无需写SQL语句,底层自动产生。
10.1.2导包
pom.xml中在mybatis后增加mybatisplus

com.baomidou mybatisplus-spring-boot-starter 1.0.5 com.baomidou mybatis-plus 2.3 10.1.3注解 MP引入了全新的注解,通过这些注解最终自动生成SQL语句 @TableName #类注解,表示类名和表名的对应 @TableField #属性注解,驼峰和下划线之间的映射 10.1.4继承 MybatisPlus实现了统一的基础SQL增删改查的方法,封装在BaseMapper接口中。 public interface BaseMapper {} 我们的代码如何使用呢?继承它就可以直接调用这些方法了 public interface CategoryMapper extends BaseMapper{} 10.2 查询商品分类 10.2.1创建项目

导入项目
拷贝配置文件,修改配置文件
10.2.2pom.xml
在mybatis依赖后加入下面的依赖:

	<dependency>
		<groupId>com.baomidou</groupId>
		<artifactId>mybatisplus-spring-boot-starter</artifactId>
		<version>1.0.5</version>
	</dependency>
	<!-- MP 核心库 -->
	<dependency>
		<groupId>com.baomidou</groupId>
		<artifactId>mybatis-plus</artifactId>
		<version>2.3</version>
	</dependency>

10.2.3Category.java
增加MybatisPlus注解即可
@TableName(“category”)//实体类对应的表名
public class Category {
@TableField(“category_id”)//属性对应的列名
int categoryId;

@TableField("category_name")
String categoryName;

public int getCategoryId() {
	return categoryId;
}

public void setCategoryId(int categoryId) {
	this.categoryId = categoryId;
}

public String getCategoryName() {
	return categoryName;
}

public void setCategoryName(String categoryName) {
	this.categoryName = categoryName;
}

}
10.2.4CategoryMapper.java
package com.tedu.mybatis04_plus.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.tedu.mybatis04_plus.pojo.Category;

public interface CategoryMapper extends BaseMapper {

}
10.2.5设置mapperScan()
@SpringBootApplication
@MapperScan(“com.tedu.mybatis04_plus.mapper”)
public class Mybatis04PlusApplication {

public static void main(String[] args) {
	SpringApplication.run(Mybatis04PlusApplication.class, args);
}

}
10.2.6拷贝数据库配置
拷贝application.yml
10.2.7CategoryController.java
@RestController
public class CategoryController {

@Autowired
CategoryMapper categoryMapper;

@RequestMapping("/selectList")
public List<Category> selectList() {
	EntityWrapper<Category> wrapper=new EntityWrapper<Category>();
	wrapper.orderBy("category_id desc");

	List<Category> categoryList=categoryMapper.selectList(wrapper);
	return categoryList;
}

}
10.3Hikari数据源支持
10.3.1概念
HikariCP是数据库连接池,号称史上最快的,bug最少的,而且目前经过实践的考验,确实是这样的,SpringBoot2.0也已经采用HikariCP作为默认连接池配置,而之前采用的是阿里的druid。

测试结果:
性能表现:hikariCP>druid>tomcat-jdbc>dbcp>c3p0
hikariCP在并发较高的情况下,性能基本上没有下降
c3p0连接池已经被淘汰,性能较差
10.3.2application.yml
SpringBoot默认数据源已经默认集成性能极高的Hikari数据源

可以在application.yml中增加type指定连接池。默认就是hikari
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
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
10.4Lombok支持
10.4.1介绍
Lombok 是一个Java实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO)。它通过注解实现增加get,set方法。

官网:https://projectlombok.org/
10.4.2安装
官网:https://projectlombok.org
最新版的idea不支持lombok。
安装前关闭eclipse,查看eclipse所在文件夹。
资源管理器中找到lombok所在文件夹,在地址栏输入cmd,进入dos,执行java -jar lombok.jar,如下图。

关闭弹出的警告窗口,点击 Specify location…,如下图

选择eclipse的安装目录,如下图

点击Install / Update,如下图

点击Quit Installer,完成安装,如下图

安装完成后,eclipse根目录下的eclipse.ini会被修改,增加了 一 行

-javaagent:D:\first\eclipse202006\eclipse\lombok.jar

如果安装完后,eclipse不能正常启动,可删除这一行。

10.4.3pom.xml中加入依赖

org.projectlombok
lombok
true

10.4.4创建实体类
@Data
public class User {
String username;

}
10.4.5测试lombok为实体类增加了set,get方法
public class LombokApplication {

public static void main(String[] args) {
	SpringApplication.run(LombokApplication.class, args);
	User user = new User();
	user.setUsername("admin");
	System.out.println(user.getUsername());
	
	System.out.println(user.toString());
	Class clazz=User.class;
	Method[] methods=clazz.getDeclaredMethods();
	for (Method method:methods) {
		System.out.println(method.getName());
	}
}

}
11Idea创建maven项目
11.1Idea设置
11.1.1代码提示 不区分大小写
Filesettings进入设置界面
Editor–>General–>code completions

进入图中的界面,不要选中Match case,再点击OK即可

11.1.2鼠标移入时显示doc

11.1.3快捷键与eclipse 一样

11.2配置maven
FileSettings,在输入框输入maven,

11.3创建maven 项目
步骤1:Filenew Project 创建一个project 项目名为 idea_maven,

步骤2:先选择maven,再选上Create from archtype,再选上org.apache.maven.archtypes:maven-archtype-quickstart

步骤3:输入group id,artifactId

步骤4:选择你自己的本地仓库的位置

步骤5:设置sources目录
不设置sources目录,无法在包下创建class,
选中项目右键 open module settings打开设置窗口,先选左边的modulessourcesjava 右键sources.

步骤6:打开App.java,右键run,run窗口中输出程序运行结果。

11.4创建springboot springmvc项目
11.4.1Start.spring.io生成项目
添加web依赖
11.4.2打开项目
Ideaopen直接打开生成的项目
11.4.3创建controller
创建controller包,创建TestController类

11.4.4测试环境是否正常
浏览器能访问http://localhost:8080/test
说明环境正常。

12项目实战:Vue+SSM+分类+商品
12.1效果演示
12.2分析

12.3逆向工程
Eclipse中打开逆向工程,修改配置文件中的包名为com.tedu.project_server,生成接口和mapper.xml

12.4创建springboot项目
12.4.1Start.spring.io生成项目,
12.4.2打开项目
Ideaopen直接打开生成的项目projectmall_server

12.4.3拷贝mapper.xml和接口

12.4.4拷贝application.yml,修改mapperLocations。
12.4.5添加扫描
@SpringBootApplication
@MapperScan(“com.tedu.projectmall.mapper”)
public class ProjectmallApplication {

public static void main(String[] args) {
SpringApplication.run(ProjectmallApplication.class, args);
}

}

12.4.6创建TestController
测试环境有没有问题,如果controller能正常访问,环境就没有问题。
@RestController
public class TestController {

@RequestMapping("/test")
public String test(){
    return  "test";
}

}

http://localhost:8080/test

有的idea全局设置了maven后,打不开springboot项目,只能取消maven全局设置。
打开项目后,重新设置maven,重新import

12.4.7访问数据库

http://localhost:8080/test?itemId=2
12.5后台服务
12.5.1用户注册
UserControlelr
@Autowired
UserMapper userMapper;

@RequestMapping("/user/register")
@CrossOrigin
public String register(User user) {

//创建example
UserExample userExample = new UserExample();
UserExample.Criteria criteria = userExample.or();
//设置查询条件where username='admin'
criteria.andUsernameEqualTo(user.getUsername());
//执行查询
List<User> userList = userMapper.selectByExample(userExample);
if (userList != null && userList.size() == 0) {//没有查到用户
    //保存用户
    int rowCount = userMapper.insert(user);
    if (rowCount >= 1) {
        return "注册成功";
    } else {
        return "注册失败";
    }
} else {
    return "用户名已经存在";
}

}

http://localhost:8080/user/register?username=a&password=a
12.5.2用户登录
UserController
@RequestMapping("/user/login")
public User login(User user){
UserExample userExample=new UserExample();
UserExample.Criteria criteria=userExample.or();
criteria.andUsernameEqualTo(user.getUsername());
criteria.andPasswordEqualTo(user.getPassword());
List userList=userMapper.selectByExample(userExample);
if (userList!=null && userList.size()>=1){
//用户的密码能返回给浏览器?设置密码为空
return userList.get(0);
}
return null;
}

http://localhost:8080/user/login?username=a&password=a
12.5.3查询所有分类
CategoryController
@RestController
public class CategoryController {
@Autowired
CategoryMapper categoryMapper;

@RequestMapping("/category/selectAll")
public List<Category> selectAll(){
    return  categoryMapper.selectByExample(null);
}

}

http://localhost:8080/cat/findAll
12.5.4查询某个分类下的商品
ItemController
@RestController
public class ItemController {
@Autowired
ItemMapper itemMapper;
@RequestMapping("/item/findByCategoryId")
public List findByCategoryId(Integer categoryId)
{
ItemExample itemExample=new ItemExample();
ItemExample.Criteria criteria=itemExample.or();
criteria.andCategoryIdEqualTo(categoryId);
return itemMapper.selectByExample(itemExample);
}
}

http://localhost:8080/item/findByCategoryId?categoryId=1
12.5.5查询商品详情
ItemController
//根据商品id查找商品
@RequestMapping("/item/findByItemId")
public Item findItemByItemId(Integer itemId){
Item item=itemMapper.selectByPrimaryKey(itemId);
return item;
}

http://localhost:8080/item/findItemByItemId?itemId=2

12.5.6管理员登录
AdminController
//管理员登录服务
@RestController
public class AdminController {

@Autowired
AdminMapper adminMapper;
//管理员登录
@RequestMapping("/admin/login")

@CrossOrigin
public String login(String adminName,String adminPassword){
//创建查询条件
AdminExample example=new AdminExample();
AdminExample.Criteria criteria=example.or();
//设置查询条件
criteria.andAdminNameEqualTo(adminName);
criteria.andAdminPasswordEqualTo(adminPassword);
//执行查询
List adminList=adminMapper.selectByExample(example);
//判断查询结果
if (adminList!=null && adminList.size()>=1){
return “管理员登录成功”;
}else {
return “管理员登录失败”;
}

}

}

http://localhost:8080/admin/login?adminName=root&adminPassword=root
12.5.7添加商品
ItemController
@RestController
public class ItemController {
@Autowired
ItemMapper itemMapper;

//添加商品
@RequestMapping("/item/insert")
@CrossOrigin
public String insert(Item item){
int rowCount=itemMapper.insert(item);
if (rowCount>=1){
return “添加成功”;
}else {
return “添加失败”;
}
}
}

http://localhost:8080/item/insert?itemName=商品&categoryId=2&itemPrice=20&itemDesc=最好的商品&itemImage=2.png
12.5.8查询商品
ItemController
//查询商品
@RequestMapping("/item/selectAll")
@CrossOrigin
public List selectAll(){
return itemMapper.selectByExample(null);
}

http://localhost:8080/item/selectAll
12.5.9删除商品
ItemController

//删除商品
@RequestMapping("/delete")
@CrossOrigin
public String insert(Integer itemId){
int rowCount=itemMapper.deleteByPrimaryKey(itemId);
if (rowCount>=1){
return “删除成功”;
}else {
return “删除失败”;
}
}

http://localhost:8080/item/delete?itemId=8
12.5.10修改商品
ItemController

//修改商品
@RequestMapping("/item/update")
@CrossOrigin
public String update(Item item){
int rowCount=itemMapper.updateByPrimaryKeySelective(item);
if (rowCount>=1){
return “修改成功”;
}else {
return “修改失败”;
}
}

http://localhost:8080/item/insert?itemId=7&itemName=商品&categoryId=2&itemPrice=20&itemDesc=最好的商品&itemImage=2.png
12.5.11给controller添加requestMapping
@RestController
@RequestMapping("/item")
public class ItemController {

}
12.6前台主站
Hbuilder创建项目projectmall_web
12.6.1用户注册

Register.html编码基本步骤如下:
Data中添加username
Methods中添加register()
Body中添加input

注册
        用户名:<input v-model="username"> <br /> 
        密码:<input v-model="password">  <br /> 
        确认密码:<input v-model="confirmPassword"> <br /> 
        <button v-on:click="register">注册</button>           
  </div>
浏览器中报以下错误 access to XMLHttpRequest at 'http://localhost:8080/user/register?username=1&password1&confirmPassword=1' from origin 'http://127.0.0.1:8848' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

浏览器不允许A网站用javascript或vue访问B网站的服务器。
例如用户同时访问我的网站和支付宝网站。我的网站如果能访问支付宝网站就不安全。
域名或端口号不一样就是不同的网站。
Hbuilder使用的端口号和服务器端使用的端口号不一样。

解决方法
@RequestMapping("/user/register")
@CrossOrigin
public String register(User user){

int rowCount=userMapper.insert(user);
if (rowCount>=1){
    return "注册成功";
}else {
    return "注册失败";
}

}

12.6.2用户登录
login.html编码基本步骤如下:
Data中添加username
Methods中添加login()
Body中添加input

登录
        用户名:<input v-model="username"> <br /> 
        密码:<input v-model="password">  <br /> 
        
        <button v-on:click="login">登录</button>          
  </div>

12.6.3详情页
const.js中的内容
var serverAddress=http://localhost:8080

detail.html先显示商品名称,再显示商品图片。基本步骤如下:
data中添加itemName.
Mounted中联网。
Body中添加标签显示商品信息

详情



{{item.itemName}}
{{item.itemPrice}}

{{item.itemDesc}}


商品描述

商品评论

12.6.3.1显示图片

分析图片应该放在服务器上,
查看京东的图片地址https://img14.360buyimg.com/ceco/s150x150_jfs/t1/124856/10/2312/133126/5ec41bf5Edbbeda2a/6a1932c98c0fa915.jpg!q70.jpg.webp

拷贝图片到src\main\resources\static文件夹中

修改数据库图片地址

12.6.4首页
12.6.4.1界面分析

12.6.4.2显示分类

首页
     <div>
        <table width="100%">
           <tr>
              <td v-for="category in categoryList" >{{category.categoryName}}</td>
           </tr>
        </table>
     </div>

     


  </div>

12.6.4.3显示某个分类下的商品
methods中添加方法findItemByCategoryId。
mounted中调用方法。
Data中添加itemList。
body中增加table显示商品。

首页
     <div>
        <table width="100%">
           <tr>
              <td v-for="category in categoryList" >{{category.categoryName}}</td>
           </tr>
        </table>
     </div>

     <div>
        <!-- 商品信息 -->
        <table width="100%">
           <tr v-for="item in itemList">
     
     
              <td>                    
                    <!--不用加{{}}-->
                    <img v-bind:src="item.itemImage"><br />
                    {{item.itemName}}<br />
                    {{item.itemPrice}}<br />
                 
              </td>
     
           </tr>
        </table>
     
     </div>


  </div>

12.6.4.4单击分类

{{category.categoryName}}

12.6.4.5跳到详情页

12.7后台管理
12.7.1管理员登录
hbuilder创建项目projectmall_admin
拷贝vue.js,axios.min.js,const.js

登录

管理员登录

        用户名:<input v-model="username"> <br /> 
        密码:<input v-model="password" type="password">  <br /> 
        
        <button v-on:click="login">登录</button>          
  </div>

12.7.2显示商品分类

后台管理
{{category.categoryName}}

12.7.3添加商品

后台管理
商品分类: {{category.categoryName}}
商品名称:
商品描述:
商品价格:
商品图片:
添加
  </div>

12.7.4显示商品

后台管理
商品分类: {{category.categoryName}}
商品名称:
商品描述:
商品价格:
商品图片:
添加




{{item.itemName}}
{{item.itemPrice}}
商品名称分类商品价格商品描述商品图片删除修改
{{item.itemName}}{{item.categoryId}}{{item.itemPrice}}{{item.itemDesc}}删除修改

12.7.5删除商品

后台管理
商品分类: {{category.categoryName}}
商品名称:
商品描述:
商品价格:
商品图片:
添加
     <br /><br />
     <table border="2">
        <tr>
           <td>商品名称</td>
           <td>分类</td>
           <td>商品价格</td>
           <td>商品描述</td>
           <td>商品图片</td>
           <td>删除</td>
           <td>修改</td>
        </tr>
        <tr v-for="item in itemList">
           <td>{{item.itemName}}</td>
           <td>{{item.categoryId}}</td>
           <td>{{item.itemPrice}}</td>
           <td>{{item.itemDesc}}</td>
           <td><img v-bind:src="item.itemImage" width="100"></td>
           <td v-on:click="deleteItem(item.itemId)">删除</td>
           <td>修改</td>
        </tr>
     </table>
  </div>

12.7.6显示修改商品

后台管理
商品分类: {{category.categoryName}}
商品名称:
商品描述:
商品价格:
商品图片:
修改
  </div>

浏览器传个商品编号测试。
http://127.0.0.1:8848/projectmall_admin/update.html?itemId=7

adminIndex.html

修改

在修改界面,会自动显示商品的分类名称。
12.7.7修改商品

update: function() {
var url = serverAddress + “/item/update?itemId=”+this.itemId
+"&itemName=" + this.itemName +
“&categoryId=” + this.categoryId +
“&itemPrice=” + this.itemPrice +
“&itemDesc=” + this.itemDesc +
“&itemImage=” + this.itemImage;
axios.get(url)
.then(function(res) {
debugger;
console.log(res);
window.alert(res.data);
})
.catch();
},

12.7.8后台主界面
在主界面adminIndex.html添加超链接,链接到不同的管理界

商品管理用户管理订单管理短信管理

12.7.9登录成功后跳到后台主界面
login: function() {
debugger;
if (this.username == “” || this.password == “”) {
window.alert(“用户名或密码不能为空”)
}
var serverUrl = serverAddress + “/admin/login?” +
“adminName=” + this.username +
“&adminPassword=” + this.password;

console.log(serverUrl);
axios.get(serverUrl)
.then(function(res) {
console.log(res);

     if (res.data == "管理员登录成功") {
         //让浏览器去访问adminIndex.html
        location.href = "adminIndex.html";
     } else {
        window.alert(res.data);
     }
  })
  .catch(function(e) {
     console.log(e);
  });
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值