Fork me on GitHub

SpringMVC-05-乱码问题

SpringMVC-05-乱码问题

1. 过滤器解决

测试步骤:

  1. 我们可以在首页编写一个提交的表单
1
2
3
4
<form action="/e/t" method="post">
<input type="text" name="name">
<input type="submit">
</form>
  1. 后台编写对应的处理类
1
2
3
4
5
6
7
8
@Controller
public class Encoding {
@RequestMapping("/e/t")
public String test(Model model,String name){
model.addAttribute("msg",name); //获取表单提交的值
return "test"; //跳转到test页面显示输入的值
}
}
  1. 输入中文测试,发现乱码

  • 不得不说,乱码问题是在我们开发中十分常见的问题,也是让我们程序猿比较头大的问题!

  • 以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置 .

  • 修改了xml文件需要重启服务器!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

// /*的作用是处理所有的jsp中的请求
  • /*的作用是处理包括jsp的请求

2. 自定义过滤器

  • 有些极端情况下.这个过滤器对get的支持不好 .

  • 处理方法 :

  1. 修改tomcat配置文件 : 设置编码!
1
2
3
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
  1. 自定义过滤器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
* 解决get和post请求 全部乱码的过滤器
*/
public class GenericEncodingFilter implements Filter {

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 1. 处理response的字符编码
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");

// 2.转型为与协议相关对象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 3. 对request包装增强
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

}

// 自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {

private HttpServletRequest request;
//1. 是否编码的标记
private boolean hasEncode;
//2. 定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
public MyRequest(HttpServletRequest request) {
super(request);// super必须写
this.request = request;
}

// 3. 对需要增强方法 进行覆盖
@Override
public Map getParameterMap() {
// 先获得请求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post请求
try {
// 处理post乱码
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get请求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 确保get手动编码逻辑只运行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 处理get乱码
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}

// 取一个值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回参数的第一个值
}

// 取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
  • 这个也是我在网上找的一些大神写的,一般情况下,SpringMVC默认的乱码处理就已经能够很好的解决了!

  • 然后在web.xml中配置这个过滤器即可!

  • 乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8!

3. HttpServletRequestWrapper

1
2
老大给了一个很实际的需求:有段程序,使用Http的方式与合作商交互,而且是明文传输数据。我方的代码已经打包放在服务器上运行了很长时间,这时合作商突然要求修改数据传输的方式,要求加密后再传输,而我方的原有的代码不能改变,以防止引发其它问题。 
问:如何在不修改我方现有的代码的前提下,满足合作商的要求?
  • 可能大家都想到了,只要加上一个过滤器Filter不就可以了吗?事实就是这样的,采用Filter+HttpServletRequestWrapper就可以解决这个问题。
  • 首先:在filter中拦截到加密后的请求,将参数解密,然后组装成一个新的明文请求串。
  • 然后:重写HttpServletRequestWrapper中的getInputStream()方法,让其返回过滤器解析后的明文串即可。

3.1 代码示例

  • 具体代码解释如下。

    首先我写了两个一摸一样的servlet,一个用来直接接收合作商的明文请求并打印;一个用来接收Filter处理后的合作商的请求并打印(Filter中将合作商加密后的参数解密再传给这个Servlet)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@WebServlet("/SiServlet")  
public class SiServlet extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public SiServlet() {
super();
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String bizBindMsg = IOUtils.toString(request.getInputStream(), "UTF-8");
bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");
System.out.println("SiServlet接收到请求为: " + bizBindMsg);

response.getWriter().write("==========success=========");
}
}



@WebServlet("/SiServletNormal")
public class SiServletNormal extends HttpServlet {
private static final long serialVersionUID = 1L;

/**
* @see HttpServlet#HttpServlet()
*/
public SiServletNormal() {
super();
}

/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}

/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String bizBindMsg = IOUtils.toString(request.getInputStream(), "UTF-8");
bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");
System.out.println("SiServletNormal接收到请求为: " + bizBindMsg);

response.getWriter()
.write("==========SiServletNormal Success=========");
}
}
  • 然后使用HttpClient模拟了一下合作商发送明文和密文请求的过程,加密使用Base64简单模拟一下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class AdcClient {  
private HttpPost httpPost = null;
private HttpClient client = null;
private List<NameValuePair> pairs = null;

public AdcClient() {
httpPost = new HttpPost("http://localhost:8080/filtertest/SiServlet");
client = new DefaultHttpClient();
}

/**
* 发送明文消息
*
*/
public void sendMsg() {

try {
httpPost = new HttpPost(
"http://localhost:8080/filtertest/SiServletNormal");

pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair(("param1"), "obama没加密"));
pairs.add(new BasicNameValuePair(("param2"), "男没加密"));
pairs.add(new BasicNameValuePair(("param3"), "汉没加密"));
pairs.add(new BasicNameValuePair(("param4"), "山东没加密"));

httpPost.setEntity(new UrlEncodedFormEntity(pairs, "UTF-8"));
// httpPost.setHeader("Cookie", "TOKEN=1234567890");
HttpResponse response = client.execute(httpPost);

HttpEntity entity = response.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(
entity.getContent()));
String line = null;
StringBuffer result = new StringBuffer();
while ((line = br.readLine()) != null) {
result.append(line);
line = br.readLine();
}

System.out.println("来自SiServletNormal的响应为:" + result.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 发送加密后的消息
*/
public void sendEncryptMsg() {
try {
pairs = new ArrayList<NameValuePair>();
pairs.add(new BasicNameValuePair(("param1"), Base64EnDecrypt
.base64Encode("obama")));
pairs.add(new BasicNameValuePair(("param2"), Base64EnDecrypt
.base64Encode("男")));
pairs.add(new BasicNameValuePair(("param3"), Base64EnDecrypt
.base64Encode("汉")));
pairs.add(new BasicNameValuePair(("param4"), Base64EnDecrypt
.base64Encode("山东")));

HttpEntity reqEntity = new UrlEncodedFormEntity(pairs, "UTF-8");
httpPost.setEntity(reqEntity);
// httpPost.setHeader("Cookie", "TOKEN=1234567890");
HttpResponse response = client.execute(httpPost);

/**
* 获取响应信息
*/
HttpEntity entity = response.getEntity();
BufferedReader br = new BufferedReader(new InputStreamReader(
entity.getContent()));
String line = null;
StringBuffer result = new StringBuffer();
while ((line = br.readLine()) != null) {
result.append(line);
line = br.readLine();
}

System.out.println("来自SiServlet的响应为:" + result.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* @param args
* @throws UnsupportedEncodingException
*/
public static void main(String[] args) throws UnsupportedEncodingException {
new AdcClient().sendMsg();

new AdcClient().sendEncryptMsg();
}
}
  • 重点是下面的这个HttpServletRequestWrapper,我重写了它的getInputStream()方法,这个方法返回包含明文的ServletInputStream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class MyRequestWrapper extends HttpServletRequestWrapper {  
private HttpServletRequest request;

public MyRequestWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}

/**
* 先解密,获取明文;然后将明文转化为字节数组;然后再去读取字节数组中的内容
*/
@Override
public ServletInputStream getInputStream() {
String bizBindMsg = null;
ServletInputStream stream = null;

try {
stream = request.getInputStream();
bizBindMsg = IOUtils.toString(stream, "UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
try {
bizBindMsg = URLDecoder.decode(bizBindMsg.toString(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("MyRequestWrapper接收到的请求为: " + bizBindMsg);

/**
* 获取加密的值进行解密
*/
final StringBuffer reqStr = new StringBuffer();
reqStr.append("param1=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(
bizBindMsg.indexOf("param1=") + 7,
bizBindMsg.indexOf("param2="))));
reqStr.append("&");
reqStr.append("param2=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(
bizBindMsg.indexOf("param2=") + 7,
bizBindMsg.indexOf("param3="))));
reqStr.append("&");
reqStr.append("param3=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(
bizBindMsg.indexOf("param3=") + 7,
bizBindMsg.indexOf("param4="))));
reqStr.append("&");
reqStr.append("param4=").append(
Base64EnDecrypt.base64Decode(bizBindMsg.substring(bizBindMsg
.indexOf("param4=") + 7)));

System.out.println("********MyRequestWrapper接收到的解密后的请求为*********");
System.out.println(reqStr.toString());

/**
* 将解密后的明文串放到buffer数组中
*/
byte[] buffer = null;
try {
buffer = reqStr.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
final ByteArrayInputStream bais = new ByteArrayInputStream(buffer);

ServletInputStream newStream = new ServletInputStream() {

@Override
public int read() throws IOException {
return bais.read();
}
};
return newStream;
}
}
  • 最后是简单的Filter,在这里将加密后的ServletRequest重新包装,交给SiServlet进行处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class EncryptFilter implements Filter {  

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

chain.doFilter(new MyRequestWrapper((HttpServletRequest) request),
response);
}

@Override
public void init(FilterConfig arg0) throws ServletException {

}

}
  • web.xml 中的配置
1
2
3
4
5
6
7
8
<filter>  
<filter-name>encryptFilter</filter-name>
<filter-class>com.test.filter.EncryptFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encryptFilter</filter-name>
<url-pattern>/SiServlet</url-pattern>
</filter-mapping> s

参考文档http://tomcat.apache.org/tomcat-5.5-doc/servletapi/index.html

SpringMVC-04-数据处理及回显

SpringMVC-04-数据处理及回显

1. 处理提交数据

1、提交的域名称和处理方法的参数名一致

  • 提交数据 :http://localhost/:8080/hello?name=zhuuu

  • 处理方法 :

1
2
3
4
5
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
  • 后台输出:zhuuu

2、提交的域名称和处理方法的参数名不一致

  • 提交数据 : http://localhost:8080/hello?username=zhuuu
  • 记得这是一个好习惯,可以标注这是一个参数
  • @RequestParam : 可以将前端传进来的参数进行改名字
1
2
3
4
5
6
@RequestMapping("/hello")
// 将前端提交的username 转换成 name
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}

3、提交的是一个对象

  • 要求提交的表单域和对象的属性名一致 , 参数使用对象即可
    • 接收前端用户传送的参数,判断参数的名字,参数就可以直接使用对象
  1. 实体类
1
2
3
4
5
6
7
8
public class User {
private int id;
private String name;
private int age;
//构造
//get/set
//tostring()
}
  1. 提交数据 : http://localhost:8080/mvc04/user?name=zhuuu&id=1&age=15
  2. 处理方法
1
2
3
4
5
@RequestMapping("/user")
public String user(User user){
System.out.println(user);
return "hello";
}
  • 后台输出:User(id=1,name=”zhuuu”,age=15)

  • 说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。

2. 数据显示到前端

第一种 : 通过ModelAndView

  • 我们前面一直都是如此 . 就不过多解释
1
2
3
4
5
6
7
8
9
10
public class ControllerTest1 implements Controller {

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}

第二种 : 通过ModelMap

  • ModelMap
1
2
3
4
5
6
7
8
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}

第三种 : 通过Model

  • Model
1
2
3
4
5
6
7
8
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
//封装要显示到视图中的数据
//相当于req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}

2.1 model/modelAndView/modelMap 对比

1
2
3
4
5
Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
  • 通常现在实现:返回的数据可以用JSON实现

SpringMVC-整合SSM

整合SSM:书籍管理系统

1. 环境要求

环境:

  • IDEA
  • MySQL 5.7.19
  • Tomcat 9
  • Maven 3.6

要求:

  • 需要熟练掌握MySQL数据库,Spring,JavaWeb及MyBatis知识,简单的前端知识;

1.1 数据库环境

创建一个存放书籍数据的数据库表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CREATE DATABASE `ssmbuild`;

USE `ssmbuild`;

DROP TABLE IF EXISTS `books`;

CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');
阅读更多...

SpringMVC-03-结果跳转方式

SpringMVC-03-结果跳转方式

前言

  • 以下三种方式都可以实现页面的跳转

1. ModelAndView

  • 设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .
  • 页面 : {视图解析器前缀} + viewName +{视图解析器后缀}
    • ViewResolver : 处理视图跳转
1
2
3
4
5
6
7
8
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
  • 对应的Controller类:
1
2
3
4
5
6
7
8
9
10
public class ControllerTest1 implements Controller {

public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
//返回一个模型视图对象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
阅读更多...

SpringMVC-06-Restful风格

SpringMVC-06-Restful风格

1. 概念

  • Restful就是一个资源定位及资源操作的风格
  • 不是标准也不是协议,只是一种风格。
  • 基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
  • 本质:通过一个url 拿到一个 JSON格式的字符串
    • 作用:更安全,你就算看到我url ,你也不知道我要干什么

2. @RequestMapping 实现Restful

  • 资源:互联网所有的事物都可以被抽象为资源
  • 资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
  • 分别对应 添加、 删除、修改、查询。
阅读更多...

SpringMVC-02-HelloSpringMVC

SpringMVC-02-HelloSpringMVC

1. 配置版实现(不使用)

  1. 新建一个Moudle , 添加web的支持!
  2. 确定导入了SpringMVC 的依赖!
  3. 配置web.xml , 注册DispatcherServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--配置DispatchServlet:这个是SpringMVC的核心,请求分发器,前端控制器-->

<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--DispatcherServlet要绑定Spring的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--启动级别:初始化启动-->
<load-on-startup>1</load-on-startup>
</servlet>

<!--在Springmvc中 / 和 /*的区别
/: 只匹配所有的请求,不会匹配JSP页面
/*:匹配所有的请求,包括jsp页面-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>
  1. 编写SpringMVC 的 配置文件!
    • 名称:springmvc-servlet.xml : [servletname]-servlet.xml说明,这里的名称要求是按照官方来的
1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
  1. 添加处理器映射
1
2
<!--处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
  1. 添加处理器适配器
1
2
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
  1. 添加视图解析器
1
2
3
4
5
6
<!--视图解析器 以后模板引擎会使用:Thymeleaf Freemarker-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<!-- 前缀和后缀-->
<property name="prefix" value="/WEB-INF/JSP/"/>
<property name="suffix" value=".jsp"/>
</bean>
  1. 编写我们要操作业务的Controller,要么实现Controller接口,要么增加注解;(需要返回一个ModelandView,封装数据,转发视图)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.zhuuu.Controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloController implements Controller {
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {

ModelAndView mv = new ModelAndView();

//业务代码:封装数据
String result = "HelloSpringMVC";
mv.addObject("msg",result);

//视图跳转
mv.setViewName("test");

//返回model and view
return mv;
}
}
  1. 将自己的类交给SpringIOC容器,注册bean
1
2
<!--BeanNameUrlHandlerMapping:bean-->
<bean id="/hello" class="com.zhuuu.Controller.HelloController"/>
  1. 编写需要跳转的页面
1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>

${msg}

</body>
</html>
  1. 配置Tomcat 启动测试!

1.1 404 error 问题

可能遇到的问题:访问出现404,排查步骤:

  1. 查看控制台输出,看一下是不是缺少了什么jar包。
  2. 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!
  3. 重启Tomcat 即可解决!

2. 注解版实现(使用)

  • 第一步:新建一个Moudle , 添加web支持!

  • 第二步:由于Maven可能存在资源过滤的问题,我们将配置完善

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
  • 第三步:在pom.xml文件引入相关的依赖

    • 主要有Spring框架核心库、Spring MVC、servlet , JSTL等。我们在父依赖中已经引入了!
  • 第四步: 配置web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!--1.注册servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 启动顺序,数字越小,启动越早 -->
<load-on-startup>1</load-on-startup>
</servlet>

<!--所有请求都会被springmvc拦截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

第五步 : 添加Spring MVC配置文件

  • IOC的注解生效
  • 静态资源过滤 :HTML . JS . CSS. 图片 , 视频 …..
  • MVC的注解驱动
  • 配置视图解析器

  • resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="com.zhuuu.controller"/>
<!-- 让Spring MVC不处理静态资源 .css .js .html .mp3 .mp4-->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />

<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>

</beans>
  • 在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。

第六步 : 创建Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.zhuuu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller // 直接注入到ioc容器中
public class HelloController{
@RequestMapping("/hello") // 1. url怎么请求过来
public String hello(Model model){
model.addAttribute("msg","hello,Spring MVC annatation"); // 2. 返回数据
return "hello"; // 会被视图解析器处理 /WEB-INFO/hello.jsp // 3. 返回视图,被视图解析器处理
}
}
  • @Controller : 为了让Spring IOC容器初始化时达到自动扫描的目的

    • @Controller : 这个类会被视图解析器解析
    • @RestController:这个类不会被视图解析器解析,只会返回一个字符串(JSON)
  • @RequestMapping :是为了映射请求路径

第七步:创建视图层

  • WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;

  • 可以通过EL表示取出Model中存放的值,或者对象;

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>

${msg}

</body>
</html>

第八步:配置Tomcat运行

OK,运行成功!

2.1 注意事项

1
2
3
4
5
**/ 和 /\* 的区别:**
< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;
即:.jsp 不会进入spring的 DispatcherServlet类 。
< url-pattern > /* </ url-pattern > 会匹配 *.jsp,
会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。

3. @RequestMapping

  • 在Spring MVC 中使用 @RequestMapping 来映射请求,也就是通过它来指定控制器可以处理哪些URL请求,相当于Servlet中在web.xml中配置
1
2
3
4
5
6
7
8
<servlet>
<servlet-name>servletName</servlet-name>
<servlet-class>ServletClass</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletName</servlet-name>
<url-pattern>url</url-pattern>
</servlet-mapping>
  • 让我们先看一下RequestMapping注解类的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
String[] value() default {};
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}

1)在@Target中有两个属性,分别为 ElementType.METHODElementType.TYPE,也就是说 @RequestMapping 可以在方法和类的声明中使用

2)可以看到注解中的属性除了 name() 返回的字符串,其它的方法均返回数组,也就是可以定义多个属性值,例如 value() 和 path() 都可以同时定义多个字符串值来接收多个URL请求


  • 测试 @RequestMapping 的 method 属性
  1. @RequestMapping 中的 method 主要用来定义接收浏览器发来的何种请求。在Spring中,使用枚举类
  2. Http规范定义了多种请求资源的方式,最基本的有四种,分别为:GET(查)、POST(增)、PUT(改)、DELETE(删),而URL则用于定位网络上的资源相当于地址的作用,配合四种请求方式,可以实现对URL对应的资源的增删改查操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// @RequestMapping(value="/login",method=RequestMethod.GET) 来指定 login()方法 仅处理通过 GET 方式发来的请求

@Controller
@RequestMapping(path = "/user")
public class UserController {

@RequestMapping(path = "/login", method=RequestMethod.GET)
public String login() {
return "success";
}
}

//这时,如果浏览器发来的请求不是GET的话,将收到浏览器返回的错误提示,也就是得通过链接的方式而不是表单的方式:
//由于在 RequestMapping 注解类中 method() 方法返回的是 RequestMethod 数组,所以可以给 method 同时指定多个请求方式,例如:

@Controller
@RequestMapping(path = "/user")
public class UserController {
// 该方法将同时接收通过GET和POST方式发来的请求
@RequestMapping(path = "/login", method={RequestMethod.POST,RequestMethod.GET})
public String login() {
return "success";
}
}

  • 测试 @RequestMapping 的 params 属性,该属性表示请求参数,也就是追加在URL上的键值对,多个请求参数以&隔开,例如:

http://localhost/SpringMVC/user/login?username=kolbe&password=123456

  1. 则这个请求的参数为username=kolbe以及password=123456,@RequestMapping 中可以使用 params 来限制请求参数,来实现进一步的过滤请求,举个例子:
1
2
3
4
5
6
7
8
9
10
11
12
@Controller
@RequestMapping(path = "/user")
public class UserController {

// 该方法将接收 /user/login 发来的请求,且请求参数必须为 username=kolbe&password=123456
@RequestMapping(path = "/login", params={"username=kolbe","password=123456"})
public String login() {
return "success";
}
}

//该例中则表示 UserController 中的 login() 方法仅处理 /user/login 发来的请求,且必须带有 username=kolbe&password=123456 的请求参数,否则浏览器将返回HTTP 404的错误

  • @RequestMapping 的 headers 属性,该属性表示请求头
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
用于HTTP协义交互的信息被称为HTTP报文,客户端发送的HTTP报文被称为请求报文,服务器发回给客户端的HTTP报文称为响应报文,报文由报文头部和报文体组成。

请求头部(Request Headers):请求头包含许多有关客户端环境和请求正文的信息,例如浏览器支持的语言、请求的服务器地址、客户端的操作系统等。

响应头部(Rsponse Headers):响应头也包含许多有用的信息,包括服务器类型、日期、响应内容的类型及编码,响应内容的长度等等。

//如果你安装的是Chrome浏览器,可以通过在网页中 右击鼠标---->审查元素---->Network---->Name中点击网页---->右侧查看Headers即可,如果Name中没有出现网页,可以刷新一下即可,下边是我电脑中的一个请求头部示例:

Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Cookie:JSESSIONID=210075B5E521CWE3CDE938076295A57A
Host:localhost:8080
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93
  1. 回规正题,通过 @RequestMapping 中的 headers 属性,可以限制客户端发来的请求
1
2
3
4
5
6
7
8
9
@Controller
@RequestMapping(path = "/user")
public class UserController {
// 表示只接收本机发来的请求
@RequestMapping(path = "/login", headers="Host=localhost:8080")
public String login() {
return "success";
}
}

  • 带占位符的URL
1
2
3
4
5
6
7
8
9
10
@Controller
@RequestMapping(path = "/user")
public class UserController {
// value : 占位符
// @PathVariable : 占位具体的变量
@RequestMapping(value="/{id}", method=RequestMethod.GET)
public String show(@PathVariable("id") Integer id) {
return "success";
}
}

  • 采用 REST 风格的 URL 请求

REST 风格的 URL 请求

1
2
3
4
5
6
7
8
 请求路径        请求方法           作用
-/user/1 HTTP GET 得到id为1的user

-/user/1 HTTP DELETE 删除id为1的user

-/user/1 HTTP PUT 更新id为1的user

-/user HTTP POST 新增user
  1. 由于浏览器表单只支持 GET 和 POST 请求,
  2. 为了实现 DELETE 和 PUT 请求,Spring 为我们提供了一个过滤器org.springframework.web.filter.HiddenHttpMethodFilter,可以为我们将 GET 和 POST 请求通过过滤器转化成 DELETE 和 PUT 请求。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package cn.kolbe.spring.mvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(path = "/user")
public class UserController {

@RequestMapping(value="/{id}", method=RequestMethod.GET)
public String show(@PathVariable("id") Integer id) {
System.out.println("查看id为:" + id + "的user");
return "success";
}


@RequestMapping(value="/{id}", method=RequestMethod.PUT)
public String update(@PathVariable("id") Integer id) {
System.out.println("更新id为:" + id + "的user");
return "success";
}

@RequestMapping(value="/{id}", method=RequestMethod.DELETE)
public String destroy(@PathVariable("id") Integer id) {
System.out.println("删除id为:" + id + "的user");
return "success";
}

@RequestMapping(value="", method=RequestMethod.POST)
public String create() {
System.out.println("新建user");
return "success";
}
}

4. 小结

实现步骤其实非常简单:

  1. 新建一个web项目
  2. 导入相关jar包
  3. 编写web.xml,注册DispatchterServlet
  4. 编写SpringMVC配置文件
  5. 接下来就要使去创建对应的控制类,controller
  6. 最后完善前端试图和controller之间的对应
  7. 测试运行调试

使用springMVC必须配置的三大件:

  • 处理器映射器、处理器适配器、视图解析器

  • 通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

SpringMVC-01-MVC架构

SpringMVC-01-MVC架构

1. 什么是MVC架构?

  • MVC是模型(model),视图(view),控制器(controller)的简写,是一种软件设计规范
  • 是将业务逻辑代码,数据,显示分离的方法来组织代码
  • MVC主要的作用是降低了视图与业务逻辑之间的双向耦合
  • MVC不是一种设置模式,MVC是一种架构模式,当然不同的MVC存在差异。

  • Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

  • View(视图):负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。

  • Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。

注意:⚠️

  • 前端 数据传输 实体类之间的关系(thymeleaf -> dto -> pojo
    • 也就是所谓的BO,VO,DAO,DTO之间的联系
  • 举例子说明:
    • pojo中的User有十来个字段,而VO(视图层对象)只需要传输其中一两个字段即可
    • VO其实还是实体类,只不过拆掉了传输时候不需要的东西
    • dto 数据传输的对象

以前 : 最经典的MVC就是:JSP+Servlet+javabean的模式

阅读更多...

SpringMVC-1.5-运行原理

SpringMVC-1.5-运行原理

前言

  • 首先,来看一下 SpringMVC 的整个请求流程,如下图:

SpringMVC 整体流程步骤解析:

1,用户去单击了某个请求路径,发起了一个 request 请求,这个请求会被DispatcherServlet 前端控制器处理。

2,前端控制器DispatcherServlet 去请求处理器映射器 HandlerMapping 去查找Handler,可以依据注解或者XML配置去查找。

3,处理器映射器HandlerMapping 根据配置找到相应的 Handler,返回给前端控制器 DispatcherServlet,这个Handler 可能包含 NInterceptor拦截器。

4,前端控制器 DispatcherServlet请求处理器适配器HandlerAdapter 去执行相应的 Handler,也就是 Controller

5,处理器适配器 HandlerAdapter 执行 Handler

6,Handler执行完毕后会返回给处理器适配器HandlerAdapter一个ModelAndView对象,ModelAndViewSpringMVC底层对象,包括 Model数据模型和 View视图信息。

7,处理器适配器HandlerAdapter接收到Handler返回的 ModelAndView 后,将其返回给前端控制器 DispatcherServlet

8,前端控制器 DispatcherServlet 接收到 ModelAndView 后,会请求视图解析器 ViewResolver 对视图进行解析。

9,视图解析器 ViewResolver根据View信息匹配到相应的视图结果,反馈给前端控制器 DispatcherServlet

10,前端控制器DispatcherServlet收到 View 具体视图后,进行视图渲染,将Model中的模型数据填充到View视图中的request域,生成最终的视图 View

11,前端控制器DispatcherServlet向用户返回请求结果。

阅读更多...

Java-基础-重写和重载的区别

Java-基础-重写和重载的区别

1. 重载和重写区别

1.1 重载

  • 重载(Overload)是让类以统一的方式处理不同类型数据的一种手段,实质表现就是多个具有不同的参数个数或者类型的同名函数

  • 同时存在于同一个类中,是一个类中多态性的一种表现(调用方法时通过传递不同参数个数和参数类型来决定具体使用哪个方法的多态性)。

规则

  • 必须有不同的参数列表(参数的个数不同,或者参数的类型不同)
  • 方法名必须相同
  • 返回值类型是相同的
  • 方法体可以是不同的

mark

阅读更多...

Java-基础-常量变量

Java-基础-常量变量

1. 常量

有final修饰的量是常量

1. 整型常量

整型常量是整数类型的数据,有二进制、八进制、十进制和十六进制4种表示形式具体表示形式如下。

二进制:由数字0和1组成的数字序列。在JDK7.0中允许使用字面值来表示二进制数,前面要以0b或0B开头,目的是为了和十进制进行区分,如0b01101100、0B10110101。

八进制:以0开头并且其后由0~7范围内(包括0和7)的整数组成的数字序列,如0342。

十进制:由数字0~9范围内(包括0和9)的整数组成的数字序列。如198。

十六进制:以0x或者0X开头并且其后由09、AF(包括0和9、A和F)组成的数字序列,如0x25AF。

阅读更多...
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信