1. Spring

1.1. 作用

框架:一套已经由他人编写完成的代码,用于解决特定的问题,可能会改变原有编程方式,或框架有它自身的运行流程。通常表现为某一个或多个jar包。

Spring框架的主要作用就是:创建和管理对象。

1.2. 知识点

1.2.1. 配置节点(*)

1.2.2. 单例:scope属性

1.2.3. 懒加载:lazy-init属性

1.2.4. 生命周期方法:init-method和destroy-method属性

1.2.5. 注入属性的值:节点(*)

1.2.6. Spring表达式

1.2.7. 注入集合类型的值:List、Set、Map、Perperties(*)

1.2.8. 自动装配:byType、byName(*,理解)

1.2.9. 注解(*)

@Componenet、@Controller、@Service、@Repository、@Named
@Scope
@Lazy
@PostConstruct、@PreDestroy
@Autowired、@Resource
@Value
(以上部分注解来自JavaEE,需要添加Tomcat运行环境后才可以使用)

1.2.10. 组件扫描(*)

<component-scan base-package="cn" />

2. SpringMVC

2.1. 作用

SpringMVC解决了V与C的交互问题。

在传统的基于Servlet的编程中,最大的问题在于每个Servlet只用于处理1种请求,例如某Servlet只处理注册请求,而登录功能则需要另一个Servlet,同理,退出登录、修改资料、修改密码等等功能都需要有对应的Servlet,随着项目的功能的增加,所需的Servlet的数量就会越来越多,也会导致:项目运行时,有大量的Servlet占用了服务器的内存空间!

所以,在SpringMVC中,只使用了1个DispatcherServlet去接收所有的请求,然后进行分发到不同的Controller中的某个方法,从而,减少Servlet对象的数量!

SpringMVC还提高了编码效率。

2.2. 开发流程

2.2.1. 创建项目

创建Maven Project,勾选Create a simple project,然后,Group Id通常填当前项目的根包,例如cn.tedu.springArtifact Id可以使用项目名称,例如SPRINGMVC-01-HELLOWORLDPackaging选择war,然后点击Finish,以创建项目。

默认创建的项目中并不包含web.xml文件,所以,必须生成该文件,避免项目报错。

pom.xml中添加spring-mvc的依赖代码:

<dependencies><!-- SpringMVC --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>3.2.8.RELEASE</version></dependency></dependencies>

然后,添加Tomcat运行环境。

然后,复制此前项目中的Spring配置文件spring-mvc.xml到当前项目中,并删除已有的配置代码,除非你确定那些代码是你需要的。

所以,以后每次创建新的项目后,都应该:1) 生成web.xml,2) 添加依赖,3) 添加Tomcat运行环境,4) 复制Spring配置文件。

2.2.2. 配置web.xml

因为在SpringMVC中,使用了DispatcherServlet接收所有的请求,然后再分发出去,所以,首先必须配置DispatcherServlet,使得它能初始阶段就运行,并接收所有请求!

即使使用了框架,核心技术其实是没有发生变化的,所以,既然要配置DispatcherServlet,就必须在web.xml中添加配置:

<!-- 配置是哪个Servlet -->
<servlet><servlet-name>SpringMVC</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 当Servlet被初始化时加载Spring配置文件 --><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><!-- 当Tomcat启动时就初始化当前Servlet --><load-on-startup>1</load-on-startup>
</servlet><!-- 配置Servlet对应的请求路径 -->
<servlet-mapping><servlet-name>SpringMVC</servlet-name><url-pattern>*.do</url-pattern>
</servlet-mapping>

以上代码中,以后可能调整的有:<param-value>classpath:springmvc.xml</param-value>中Spring配置文件的文件名;<url-pattern>*.do</url-pattern>中接收的请求的名称。

当以上配置完成后,应该:当Tomcat启动时,就会初始化DispatcherServlet,用于接收*.do请求,当DispatcherServlet被初始化时,就会加载Spring的配置文件!

2.2.3. 设计目标

希望发出的请求路径是/login.do,并且,显示出webapp/WEB-INF/login.jsp

2.2.4. 控制器

在改进的做法中,不必再配置HandlerMapping

处理请求的一定是控制器,所以,创建控制器类:cn.tedu.spring.controller.UserController,即希望所有关于用户的操作的请求,都交给这个控制器来处理!控制器类都应该是整个项目的关键组件,是应该交给Spring进行管理的,所以,使用@Controller注解这个类,并且,在Spring的配置文件中,添加组件扫描。

注意:这种做法不需要实现Controller接口!并且,处理请求的方法可以自行定义返回值类型、方法名称、参数列表!

此次的目标是显示登录页面,所以,方法名可以是showLogin,由于不需要参数,所以,方法的参数列表为空,处理结束时,不需要转发任何数据,只需要确定视图组件名称即可,所以,返回值类型设计为String

public String showLogin() {return "login"; // WEB-INF/login.jsp
}

最后,为了确保/login.do请求能够被这个方法来处理,需要为这个方法添加注解:

@RequestMapping("login.do")
public String showLogin() {return "login"; // WEB-INF/login.jsp
}

添加注解后,就产生了请求路径与方法的映射关系。即:当接收到login.do请求后,就会调用以上showLogin()方法!

2.2.5. 视图解析器

在Spring的配置中添加关于视图解析器的配置:

<!-- 配置视图解析器:根据视图名称得到视图资源 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!-- 配置前缀 --><property name="prefix" value="/WEB-INF/" /><!-- 配置后缀 --><property name="suffix" value=".jsp" />
</bean>

2.2.6. 创建视图组件

由于视图解析器配置的前缀是/WEB-INF/,后缀是.jsp,而控制器返回的视图名称是login,所以,此次响应的视图组件应该是/WEB-INF/login.jsp,所以,在/WEB-INF/下创建名为login.jsp的文件即可完成响应。

2.2.7. 常见问题

问题描述:

警告: No mapping found for HTTP request with URI [/SPRINGMVC-02-REQUESTMAPPING/login1.do] in DispatcherServlet with name 'SpringMVC'

问题原因:没有找到请求路径对应的映射,无法处理这个URI对应的请求!

解决方法:

  • 请检查控制器类是否被Spring管理,即:在Spring配置中是否开启了组件扫描,且扫描的位置是否是控制器类的包名或其父级包名!并且,控制器类是否添加了@Controller注解!

  • 请检查是否正确的配置了@RequestMapping,即:在@RequestMapping中填写的请求路径,与浏览器地址栏中输入的路径需要保持完全一致,包括.do后缀!

  • 请检查web.xml中配置的DispatcherServlet接收的请求映射,应该是*.do!

3. 关于@RequestMapping注解

3.1. 基本使用

使用@RequestMapping可以配置请求路径与处理请求的方法之间的映射关系!

使用@RequestMapping可以修饰方法,也可以修饰类!即:在类的声明之前也可以添加该注解,例如:

@Controller
@RequestMapping("user")
public class UserController {@RequestMapping("login.do")public String showLogin() {return "login";}}

则访问时的请求路径应该是:user/login.do

所以,同样是使用@RequestMapping注解,在类之前加的注解并不能取代方法之前的注解!如果在类和方法之前都添加了该注解,其实是组合使用的!

通常,建议在每个控制器类之前都使用@RequestMapping进行注解!

在配置注解时,其中的路径可以添加/,也可以不添加,也就是:

/user        /login.do
/user       login.do
user        /login.do
user        login.do

以上4种做法都是可以正确运行的!推荐使用第1种,也就是类和方法之前的注解都添加/

3.2. 练习

请设计代码,实现对/user/reg.do的处理,最终显示/WEB-INF/reg.jsp页面。

请设计代码,实现对/product/list.do的处理,最终显示/WEB-INF/product_list.jsp页面。

页面内容不作要求!

4. 接收请求参数

4.1. 设计目标

reg.jsp中添加用户名密码的输入框,当点击提交按钮后,以POST的方式将请求提交到/user/handle_reg.do,并且 ,控制器接收到请求后,能够获取用户填写的用户名和密码。

4.2. 【不推荐】通过HttpServletRequest参数获取请求参数

在处理请求的方法中,添加HttpServletRequest类型的参数,然后调用参数对象的String getParameter(String)方法即可获取请求参数:

@RequestMapping("/handle_reg.do")
public String handleReg(HttpServletRequest request) {System.out.println("UserController.handleReg() > 准备接收请求参数...");// getParameter()的参数必须与jsp页面中控件的name保持一致String username = request.getParameter("username");String password = request.getParameter("password");System.out.println("username=" + username);System.out.println("password=" + password);return ""; // 暂不关心返回的视图名,后续运行会出现404
}

4.3. 练习

在界面上添加:要求输入年龄AGE,并且,在控制器接收它的值,要求是Integer类型的!

4.4. 【推荐】直接在处理请求的方法中声明所需的参数

将所需的参数列表直接添加在处理请求的方法中即可:

public String handleReg(String username, String password, Integer age) {System.out.println("UserController.handleReg() > 准备接收请求参数...");// getParameter()的参数必须与jsp页面中控件的name保持一致System.out.println("username=" + username);System.out.println("password=" + password);System.out.println("age=" + age);return "a"; // 暂不关心返回的视图名,后续运行会出现404
}

使用这种做法,无须调用HttpServletRequest对象的getParameter()方法,甚至都不需要考虑类型转换问题,例如所需的ageInteger类的,则直接声明参数Integer age即可,SpringMVC框架会自动的转换类型!

注意:使用这种做法,必须前后端使用的名称保持一致,例如前端页面中标签的name值是username,则后端控制器的方法中的参数名称也必须是username,如果不一致,则后端控制器获取到的值将是null值!

注意:如果前端页面没有输入值,则控制器接收到的是空字符串,即"",如果前端页面根本就没有提交这个名称的参数,则控制器接收到的是null!

由于前后端可能是不同的开发团队完成的,则很可能出现名称不一致的问题,针对这种问题,可以在参数之前添加@RequestParam("前端提交的参数名")来解决:

@RequestMapping("/handle_reg.do")
public String handleReg(@RequestParam("username") String name, String password, Integer age) {System.out.println("UserController.handleReg() > 准备接收请求参数...");// getParameter()的参数必须与jsp页面中控件的name保持一致System.out.println("name=" + name);System.out.println("password=" + password);System.out.println("age=" + age);return "a"; // 暂不关心返回的视图名,后续运行会出现404
}

注意:当添加了@RequestParam()注解后,该参数默认是必须提交的,如果没有提交,则报错,该问题后续在@RequestParam专题中详解。

其它

1. 编码规范

代码的可维护性是评价项目的非常重要的指标之一!其中,代码的编码规范就是最基础的表现!

首先,命名必须规范:

  • 包(package)名必须全部使用小写字母,例如cn.tedu.spring.Controller这样的包名就是不对的,其中的Controller子包的首字母不应该大写!

  • 所有的类、接口的首字母必须大写,如果名称由多个单词组成,第2个单词开始,每个单词的首字母也必须是大写的,例如:NullPointerExceptionFileNotFoundExceptionArrayIndexOutOfBoundsException……

  • 所有的属性、方法、局部变量、参数的名称都必须是首字母小写,如果名称由多个单词组成,第2个单词开始,每个单词的首字母也必须是大写的

  • 所有的常量的名称中,每个字母都必须是大写的,如果由多个单词组成,则各单词之间使用下划线分隔,例如:state_ok应该写成STATE_OK

  • 绝大部分的属性应该使用名词,例如:nameage……

  • 绝大部分的方法应该使用动词作为前缀,例如:setNamegetName

  • 不遵守以上命名原则的属性或方法应该是疑问类型的属性或方法,例如:isAlivehasNext,即使用ishas作为前缀

代码的版式也非常重要:

  • 在类中,每个方法之间、属性与方法之间,应该添加1行空白

如果对代码的正确版式不太了解,可以使用Eclipse的Source菜单中的Format指令,使Eclipse来帮助排版!

其它……

2. 请求类型的GET与POST

发出请求

只能通过表单(Form)或Javascript代码发出POST请求,而发出GET请求的方式则更多,除了这2种做法以外,还可以通过改变浏览器的地址栏中的URL来发出GET请求!所以,发GET请求比发POST请求更加简单!

传输特点

GET请求的参数会暴露在URL中,不适合传输较私密的数据,例如密码,并且,受到URL长度的限制,GET请求不适合传递大量的数据,例如发邮件、上传文件都不可以使用GET请求!

POST请求的参数不会暴露在URL中,可以传输任何数据(传输过程没有额外的加密处理),并且,传输数据没有长度限制!

相比之下,POST请求更加有优势,但是,POST请求的发出限制略高,不便于刷新,并且,无法分享页面!

3. 查看项目的错误

如果项目结构(Eclipse的左侧)提示了任何错误,都应该在Problems面板中查看详情,有些错误可能并不体现在某个文件或代码中。

如果Eclipse默认没有显示该面板,可以点击Window菜单中的Show View项打开,如果在Show View下仍没有Problems,可以选择Other项再查找。

4. 切换JDK/JRE版本

移除旧版本

Libraries下,对旧版的JDK/JRE点右键,选择Build Path中的Remove ...选项,即可移除。

添加新版本

对项目名称点右键,选择Build Path中的Configure,在弹出的对话框中,选择Libraries选项卡,然后点击右侧的Add Library按钮,在弹出的新对话框中选择JRE System Library并点击Next,通常在下个界面中第3项即是当前系统安装的JDK/JRE版本!

如果在这个面板中没有所需的JDK/JRE版本,则点击Installed JREs按钮并浏览到本机中安装JDK/JRE的目录进行选择即可。

作业

设计登录页面,要求提交用户名、密码到/user/handle_reg.do页面,并且,在服务端器的控制器中,接收到填写的用户名和密码的值!

day02

【续】4. 接收请求参数

4.5. 【推荐】使用自定义的数据类型接收请求参数

可以自定义某个数据类型,对应所有的请求参数:

public class User {private String username;private String password;private Integer age;// ...
}

然后,在处理请求的方法中,直接将这个类型作为参数即可:

@RequestMapping("/handle_reg.do")
public String handleReg(User user) {System.out.println(user);return null;
}

注意:使用这种做法也必须保证提交的参数名称,与自定义类型中的属性名称,必须保持一致!如果没有保持一致,则获取到的对象的那些属性将是null值!

4.6 小结

在SpringMVC中,处理请求时,有3种方式可以接收请求参数,实际使用的有:直接声明同名参数、使用自定义数据类型,如果有必要的话,这2种方式可以组合使用!

当使用同名参数接收请求参数时,如果名称不一致,可以使用@RequestParam()注解来确定请求参数的名称。

###########################

1. 关于@RequestParam注解

1.1. 基础使用

使用@RequestParam注解,可以解决提交的请求参数与处理请求的方法参数名称不一致的问题,例如:

@RequestMapping("/handle_login.do")
public String handleLogin(@RequestParam("uname") String username,String password) {System.out.println("username=" + username);System.out.println("password=" + password);return null;
}

使用@RequestParam注解后,默认情况下,对应的请求参数是必须的,如果提交的请求不包含这项参数,则会提示:

HTTP Status 400 - Required String parameter 'uname' is not present

1.2. 注解的配置

使用@RequestParam注解请求参数名称时的完整语法是:

@RequestParam(value="uname")

@RequestParam注解中,还有另一个属性required,取值为boolean类型,例如:

@RequestParam(value="uname", required=false)

可以发现,使用@RequestParam时,其required属性的默认值是true

除此以外,该注解还有一个String类型的属性defaultValue

@RequestParam(value="uname", required=false, defaultValue="hello")

这项defaultValue表示的是默认值,即没有提交这项请求参数时,将按照默认值来处理,如果提交了这项请求参数,则这项配置没有意义。

从4.2版本后,该注解还可以使用name属性,意义与value属性相同。

/*** The name of the request parameter to bind to.* @since 4.2*/
@AliasFor("value")
String name() default "";

2. 转发数据

2.1. 【不推荐】通过HttpServletRequest参数转发数据

当需要转发数据时,可以在处理请求的方法中添加HttpServletRequest对象作为参数,然后调用该参数对象的setAttribute(String, Object)方法封装即可:

@RequestMapping("/handle_reg.do")
public String handleReg(User user, HttpServletRequest request) {System.out.println(user);// 暂视为所有用户名注册时都是失败的,原因:用户名已经被占用// 封装错误信息,以准备转发String message = "您输入的用户名(" + user.getUsername() + ")已经被占用!";request.setAttribute("message", message);// 执行转发return "error";
}

2.2. 【不推荐】使用ModelAndView

在处理请求时,使用ModelAndView作为方法的返回值。

ModelAndView中,Model表示需要转发的数据,其类型是Map<String, ?>类型,View表示负责响应的视图组件,可以使用String viewName表示。

@RequestMapping("/handle_reg.do")
public ModelAndView handleReg(String username) {// 暂视为所有用户名注册时都是失败的,原因:用户名已经被占用String message = "MODEL:您输入的用户名(" + username + ")已经被占用!";// 封装转发的数据Map<String, Object> model = new HashMap<String, Object>();model.put("message", message);// 准备返回值ModelAndView mav= new ModelAndView("error", model);// 执行转发return mav;
}

2.3. 【推荐】使用ModelMap

ModelMap是SpringMVC中提供的用于封装转发数据的类型,其本质就是一个Map,你可以像使用一般的Map类似的方式去使用它,同时,当你需要封装转发数据时,使用与HttpServletRequest相同的方式去使用即可!

@RequestMapping("/handle_reg.do")
public String handleReg(String username,ModelMap modelMap) {// 暂视为所有用户名注册时都是失败的,原因:用户名已经被占用String message = "MODELMAP:您输入的用户名(" + username + ")已经被占用!";// 封装需要转发的数据modelMap.addAttribute("message", message);// 执行转发return "error";
}

3 重定向

在处理请求的方法中,返回值类型依然使用String,并且,返回的字符串使用redirect:作为前缀,然后写出目标位置的相对路径或绝对路径。

// 重定向
@RequestMapping("/handle_reg.do")
public String handleReg() {// 假设注册一定成功,并到登录页// 当前位置:      /user/handle_reg.do// 重定向到登录,即:/user/login.doreturn "redirect:login.do";
}

4. 处理Session

ModelMap的使用方式是一样的!即:把HttpSession作为参数添加到处理请求的方法中,然后,这整个方法中就可以使用Session。

5. 关于@RequestMapping

使用@RequestMapping主要用于配置所处理的请求的路径,完整配置应该是:

@RequestMapping(value="/handle_reg.do")

除此以外,还可以添加配置method属性,以限制请求的类型,例如:

@RequestMapping(value="/handle_reg.do", method=RequestMethod.POST)

如果以上路径配置了限制为POST请求,然后,实际发出的是GET请求,则会提示405错误:

HTTP Status 405 - Request method 'GET' not supported

通常,对于GET类型的请求并不配置method,而POST类型的请求应该配置method。但是,出于规范的考虑,无论什么请求,都应该显式的限制请求类型,除非你确定这些请求类型都是允许的。

6. 阶段小结

SpringMVC主要解决了如何接收请求,并给予的响应的问题。

在处理请求时,请求的参数(含多个请求参数构成的对象类型)、HttpServletRequestHttpServletResponseModelMapHttpSession都可以根据需要,自行添加为处理请求的方法的参数,然后,在方法内部直接使用即可!

处理请求的方法返回值可以是String,默认的响应方式是转发,如果需要重定向,则返回的字符串应该是redirect:相对路径或绝对路径

使用@RequestMapping注解可以配置请求路径,甚至限制请求类型。

使用@RequestParam注解可以确定请求参数的名称、是否必须、默认值。

7. 阶段案例

7.1. 案例目标

用户可以在注册页面中填写:用户名、密码、年龄,提交后,暂且将admin用户名视为已注册,即当用户提交的用户名是admin时,提示错误,否则,视为注册成功!

如果注册成功,则跳转到登录页,在处理登录时,如果用户名是root且密码是1234,视为登录成功,否则,视为登录失败!

如果登录成功,将用户名存储在Session中,并跳转到主页,要求在主页中显示当前登录的用户名。

以上请求的路径分别是:

  • 显示注册页面:/user/reg.do
  • 显示登录页面:/user/login.do
  • 处理注册请求:/user/handle_reg.do
  • 处理登录请求:/user/handle_login.do
  • 显示主页:/main/index.do

7.2. 开发步骤

7.2.1. 创建项目

创建Maven Project,Group Id为cn.tedu.spring,Artifact Id为SPRINGMVC-03-SAMPLE,Packaging为war,然后,执行固定的5项任务:生成web.xml,复制此前项目中pom.xml中依赖,添加Tomcat Runtime,复制springmvc.xml,复制此前项目中web.xml中关于DispatcherServlet的配置。

7.2.2. 分析案例

一次只解决一个问题。

先下手为强。

大致的开发步骤应该分为:

  • 显示注册页面

  • 处理注册数据

  • 显示登录页面

  • 处理登录数据

  • 显示主页

7.2.3. 显示注册页面

设计请求:

请求路径:/user/reg.do
请求方式:GET
请求参数:无
响应方式:转发:reg.jsp

创建cn.tedu.spring.controller.UserController类,使用@Controller@RequestMapping("/user")注解。

然后,在类中添加处理请求的方法:

@RequestMapping("/reg.do")
public String showReg() {return "reg";
}

检查springmvc.xml的配置是否符合当前项目,例如组件扫描的包,视图解析器的前缀与后缀的配置。

然后,在webapp/WEB-INF下创建reg.jsp页面,在页面中添加用户名、密码、年龄的输入框和提交按钮,且表单以POST方式提交到handle_reg.do

7.2.4. 处理注册数据

设计请求:

请求路径:/user/handle_reg.do
请求方式:POST
请求参数:User(username, password, age),ModelMap
响应方式:成功时重定向到/user/login.do,失败时转发到error.jsp

先创建cn.tedu.spring.entity.User类,并声明以上3个属性,参考Java Bean设计规范。

然后,在UserController中添加处理请求的方法:

@RequestMapping("/handle_reg.do")
public String handleReg(User user, ModelMap modelMap) {// 判断用户名是否是"admin"// -- 是:视为失败,用户名被占用// -- -- 向ModelMap对象中封装需要转发的数据:错误信息// -- -- 执行转发// -- 否:视为成功// -- -- 重定向到/user/login.do
}

最后,创建error.jsp,并在页面中通过EL表达式显示错误信息。

其它

1. 关于构造方法

每个类都有构造方法,用于确定对象的创建过程,当需要创建对象时,使用例如new Object();这样的语法,就是在调用构造方法。

在编写类的时候,可以不需要显式的编写构造方法,当没有显式的编写时,编译器将自动添加公有的、无参数的构造方法!如果已经显式的编写了任何构造方法,编译器将不会自动添加构造方法!

构造方法也是方法,可以重载。

每次创建对象时,都会默认先调用其父类的构造方法!即:

public class User {public User() {super(); // 调用其父类的构造方法}
}

且:使用super关键字调用父类的构造方法的语句,必须编写在子类的构造方法中,必须是子类构造方法中的第1条有效代码!

抽象类的构造方法不允许直接调用,即不允许使用new语法调用抽象类的构造方法来直接创建抽象类的对象!

通常,会在以下情景中自定义构造方法:

  1. 更便捷的创建对象,会自定义若干个带参数的构造方法,使得创建对象时就可以直接确定某些属性的值;

  2. 需要确定创建对象时,就执行的任务,或确定某些属性的值;

  3. 需要限制创建对象,可能将构造方法的访问权限设置得更加严格!

2. 常用快捷键

  • 代码提示 Alt + /
  • 代码排版 Ctrl + Shift + F / Ctrl + A -> Ctrl + I
  • 上下移动 Alt + 上/下
  • 上下复制 Ctrl + Alt + 上/下
  • 删除代码行 Ctrl + D
  • 重命名 Ctrl + 2, R
  • 导包 Ctrl + Shift + I
  • 整理包 Ctrl + Shift + O
  • 添加/去除注释 Ctrl + /

由于使用的系统不同,可能某些系统上快捷键不同,或会发生冲突,如果无法调整,则应该使用Source菜单中对应的功能。

3. 转发与重定向

转发的本质是客户端只发出了1次请求,而重定向的话,客户端发出了2次请求。

转发是发生在服务端内部的,对于客户端而言,并不知道服务端内部有转发的过程,即使是控制器(无论是SpringMVC中的Controller还是传统的Servlet)转发到了JSP,对于客户端而言,也并不知晓。

在执行转发时,URL是不会发生变化的,而重定向会发生变化!

如果需要将较多的数据进行传递,可以使用转发!而重定向则不适合大量数据的传递,因为重定向对应的是2次请求,而2次请求之间数据并不共享!

如果需要地址栏发生变化,与当前页面显示的内容匹配,则必须使用重定向!

4. 关于响应码

服务端对客户端进行响应时,除了有响应的正文以外,还会有响应代码。常见的响应码:

  • 2xx:正确响应,例如:200、206

  • 3xx:重定向,例如:301、302

  • 4xx:请求错误,例如:400、404、405、406

  • 5xx:服务器内部错误,例如:500

5. 枚举类型

使用enum关键字可以声明枚举类型,例如:

public enum RequestMethod {GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE}

定义枚举的前提是:可以穷举所有可能的值,每个值都不相同,并不关心值本身而只关心它表达的意义。

作业

SPRINGMVC-03-SAMPLE案例中继续完成:

  • 显示登录页面

  • 处理登录数据

  • 显示主页

在每个功能实现之前,使用记事本记录下分析过程。

day03

【续】作业

SPRINGMVC-03-SAMPLE案例中继续完成:

显示登录页面

分析请求:

请求路径:/user/login.do
请求类型:GET
请求参数:无
响应方式:转发

所以,在UserController中添加处理请求的方法:

@RequestMapping("/login.do")
public String showLogin() {return "login"; // /WEB-INF/login.jsp
}

复制并调整现有的reg.jsp,得到login.jsp

处理登录数据

分析请求:

请求路径:/user/handle_login.do
请求类型:POST
请求参数:username, password, ModelMap, HttpSession
响应方式:成功时重定向到/main/index.do,失败时转发到error

所以,在UserController中添加处理请求的方法:

@RequestMapping(value="/handle_login.do", method=RequestMethod.POST)
public String handleLogin(@RequestParam("username") String username,@RequestParam("password") String password,ModelMap modelMap, HttpSession session) {// 如果用户名是`root`且密码是`1234`,视为登录成功,否则,视为登录失败!// 判断用户名是否为root//       用户名正确 > 判断密码是否为1234//            密码正确 > 登录成功 > 记Session//              重定向到/main/index.do//            密码错误    > 封装错误信息//               转发到error//      用户名错误 > 封装错误信息:用户名或密码错误//         转发到error
}

显示主页

分析请求:

请求路径:/main/index.do
请求类型:GET
请求参数:无
响应方式:转发

由于此次的路径是/main而不是之前的/user,所以,必须新建另一个控制器类来完成,则创建cn.tedu.spring.controller.MainController,和之前的控制器类一样,添加2项注解。

然后,在这个新创建的控制器类中添加处理请求的方法:

@RequestMapping("/index.do")
public String showIndex() {return "index"; // /WEB-INF/index.jsp
}

然后,复制error.jsp得到login.jsp

1. SpringMVC中的Interceptor

1.1. 练习目标

在主页显示的“用户名”位置添加超链接,点击后进入“个人中心”。

通过/user/info.do路径,可以显示“个人信息”页面,该页面需要登录后才允许访问。

1.2. 分析问题

一个项目中,可能绝大部分的功能都是需要登录后才允许使用的,但是,在这些功能的处理中,可能都需要执行:

// 判断Session中是否有username
if (session.getAttribute("username") == null) {// 没有,则意味着没有登录,则重定向到登录页return "redirect:login.do";
}

而大量的复制并粘贴以上代码的做法显然是不可取的!

1.3. 解决方案

拦截器(Interceptor)是SpringMVC中的组件,它是运行在DispatcherServlet之后、每个Controller之前的组件,并且,运行时,可以选择拦截放行,则会导致某些请求可以被处理或不被处理!然后,拦截器还可以运行在每个Controller处理完请求之后。

基于拦截器这样的特点,可以在项目中添加“登录拦截器”,使得要求登录的请求都先经过“登录拦截器”进行判断,在“登录拦截器”中将判断Session是否有效,如果有效,则放行,如果没有有效的Session,则直接拦截,并重定向到登录页。

1.4. 使用方式

1.4.1. 创建拦截器

所有的自定义拦截器,都必须实现HandlerInterceptor接口,所以,在项目中创建cn.tedu.spring.interceptor.LoginInterceptor,实现HandlerInterceptor

public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("LoginInterceptor.preHandle()");return false;}public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.out.println("LoginInterceptor.postHandle()");}public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.out.println("LoginInterceptor.afterCompletion()");}}

1.4.2. 配置拦截器

springmvc.xml中添加拦截器的配置:

<!-- 配置拦截器链 -->
<mvc:interceptors><!-- 配置第1个拦截器 --><mvc:interceptor><!-- 1. 拦截的路径 --><mvc:mapping path="/user/info.do" /><mvc:mapping path="/main/index.do" /><!-- 2. 指定拦截器类 --><bean class="cn.tedu.spring.interceptor.LoginInterceptor" /></mvc:interceptor>
</mvc:interceptors>

1.4.3. 执行效果

当尝试访问以上拦截的路径时,可以看到拦截器的preHandle()方法被执行,且界面上显示一片空白,而没有被拦截器路径可以正常访问,拦截器中的任何方法都没有执行。

1.4.4. 完善登录拦截

在控制器中,成功登录时,会在Session中放入用户名,且存入时使用的名称是username,所以,判断是否登录,就是检查Session是否有名为username的值,如果值是存在的,即已经登录,如果值不存在,则没有登录,所以,在拦截器的preHandle()方法中:

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("LoginInterceptor.preHandle()");// 获取SessionHttpSession session= request.getSession();// 判断session中是否有登录信息if (session.getAttribute("username") == null) {// 没有登录信息,则:重定向到登录页response.sendRedirect("../user/login.do");// 执行拦截return false;} else {// 有登录信息,则:允许正常访问,直接放行return true;}
}

1.4.5. 拦截器的更多配置

在配置拦截器时,使用<mvc:mapping path="" />可以配置拦截哪些路径,同一个拦截器的配置中,可以有多个这样的节点。

在配置拦截路径时,可以使用*作为通配符,例如:

<mvc:mapping path="/user/*" />

以上配置将表示拦截/user之下的所有路径,例如:/user/login.do/user/reg.do/user/handler_login.do/user/handler_reg.do/user/inf.do等。

使用1个星号(*)只能通配1层路径,即以上配置对于/user/info/change.do这样的路径是不起作用的!如果需要无视路径的层级,应该使用2个星号(*),即配置为:

<mvc:mapping path="/user/**" />

使用以上配置,可适用于例如:/user/login.do/user/info/change.do/user/info/change/password.do等。

由于使用于通配符后,拦截的范围可能过大,还可以使用<mvc:exclude-mapping />节点来配置例外

<mvc:interceptor><!-- 1. 拦截的路径 --><mvc:mapping path="/user/**" /><mvc:mapping path="/main/index.do" /><!-- 2. 例外的路径,不拦截的路径,即白名单--><mvc:exclude-mapping path="/user/reg.do" /><mvc:exclude-mapping path="/user/handle_reg.do" /><mvc:exclude-mapping path="/user/login.do" /><mvc:exclude-mapping path="/user/handle_reg.do" /><!-- 3. 指定拦截器类 --><bean class="cn.tedu.spring.interceptor.LoginInterceptor" />
</mvc:interceptor>

这些例外可以通俗的理解为白名单,即拦截器对于这些路径的请求完全不受理。

注意:以上配置,必须先配置拦截路径,再配置例外,最后配置拦截器类。

2. 字符编码过滤器

使用SpringMVC框架时,默认并不支持中文,为了能够支持中文,需要通过request.setCharacterEncoding("utf-8")这类的语法来指定使用的字符编码!

这项任务不能够在控制器(Controller)中来完成,因为,指定编码必须在Servlet或其之前就执行,如果运行到Servlet时就已经乱码,然后,在控制器中再来调整,就已经晚了!也就意味着,通过SpringMVC的拦截器是无法实现编码的调整的!

解决方案是使用SpringMVC框架中自带的CharacterEncodingFilter类,这个类是一个过滤器类,是运行在所有的Servlet之前的,所以,通过过滤器指定了编码后,适用于所有的Servlet及后续的流程!

这个类并没有明显的指定使用的编码,所以,在配置时,还需要通过初始化参数来确定所使用的编码!

具体的配置是在web.xml中添加:

<!-- 配置字符编码过滤器 -->
<filter><filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

其它

1. 验证用户提交的数据

在开发规范中,认为凡是用户提交的数据,永远是不可靠的,应该对数据进行有效性验证!

通常,关于数据格式(长度、组成字符)的验证,应该使用正则表达式来验证!

package cn.tedu.spring.util;/*** 字符验证工具类* @author adminitartor*/
public class TextValidator {/*** <p>验证用户名的正则表达式</p>* <p>验证规则:</p>* <ul>* <li>必须由4~16字符组成;</li>* <li>必须由大写字母、小写字母、数字、下划线组成,不允许使用其它字符;</li>* <li>第1个字符必须是大写或小写字母。</li>* </ul>*/public static final String REG_EXP_USERNAME = "[a-zA-Z]{1}[a-zA-Z0-9_]{3,15}";/*** 检查用户名的格式是否正确* @param username 用户名* @return 如果用户名符合验证规则,则返回true,否则,返回false* @see #REG_EXP_USERNAME*/public static boolean checkUsername(String username) {if (username == null) {return false;}return username.matches(REG_EXP_USERNAME);}}

2. 关于内存

一个完整的计算机硬件系统应该包括:运算器、控制器、存储器、输入设备、输出设备。

存储器分为内部存储器(内存/主存)和外部存储器(外存/辅存)。

内存只有:主板上的ROM、CPU内部的Cache(高速缓存)、内存条(RAM)。

RAM:Random Access Memory。

通常在开发领域讨论的内存指的都是内存条。

RAM的特性有:

  • 它是唯一一个CPU可以直接访问的硬件;

  • 一旦断电,内存中的所有数据会全部丢失;

  • 正在执行的程序和数据必须在内存中。

3. 关于static

使用static可以修饰类的成员(属性/成员变量、方法、内部类、内部接口)。

它的特性是:唯一、常驻

唯一:假设Person类中存在被static修饰的static int hands = 2;,无论把Personnew多少次,无论使用哪个对象调用hands属性,对应的都是同一个值,所以,被static修饰的成员不归属于任何一个对象,在调用时,应该使用 类名.成员 的语法来调用,而不推荐使用 对象.成员 来调用。

常驻:被static修饰的成员,会在它所在的类的名称第一次出现在运行的代码中时,就会被加载到内存,并且,会持续的一直在内存中,所以,表现出“常驻内存”的特性!所以,被static修饰的成员不可以直接访问非static成员!

由于static成员是唯一的,所以,慎用static修饰属性(成员变量),因为可能存在多线程下的数据安全问题(多个线程、同时、对同一个数据、进行修改,则可能导致线程安全问题)。

由于static成员是常驻的,所以,无论是用于修饰哪种类型的成员,都应该慎用,因为可能过度的消耗内存!

4. 内存泄露/溢出(Leak)

内存泄露的主要原因是:尝试释放某个资源,却无法释放,而该资源将持续占用内存,却又无法被使用!

当垃圾回收机制尝试回收垃圾时,可能发现这些数据仍处于“使用状态”(可能与另一些资源保持了连接等等),则不会把这些数据视为垃圾,也就不会回收这些数据占用的内存空间!而由于程序中已经无法再使用变量操作这些数据了,对于开发者而言,这些数据已经用不着了,所以,就出现了“用不着的数据没有被当作垃圾来回收,反而一直占用内存”的现象!

所以,少量的内存泄露是没有明显的危害,但是,如果积少成多,会导致可用内存越来越少,当少到极点还继续使用,就会导致数据溢出。

解决内存泄露的做法就是:及时释放掉不需要使用的资源,例如流等等,应该及时调用close()方法进行归还或关闭。

另外,关于垃圾回收,JVM中的垃圾回收机制会定期自动清理垃圾数据,无须开发者手动释放数据所占用的内存空间,即使调用System.gc()方法也不能保证垃圾回收机制马上来回收数据!

5. 乱码

字符集相当于一本字典,记录了每个字符与二进制码的对应关系!

a    110 0001

在ASCII码中,记录了英文输入法下的所有字母、数据、标点符号与二进制码的对应关系。

由于1个字节只有8个二进制位,无法表示中文(汉字的字符种类太多),所以,需要使用2个字节,即16个二制位来表达!

常见的支持中文的编码有:UTF-8、GBK、GB2312……,还有一些其它编码是完全不支持中文的,例如:ISO8859-1、latin1……

由于支持中文的编码格式有多种,但是,每种编码的二进制码与汉字的对应关系不一样,所以,如果存和取的时候,使用了不同的编码,就会导致乱码!

所以,解决乱码的答案:使用统一的、支持中文的编码。

在项目中,需要统一编码的位置:存储位置(例如数据库等)、显示位置(例如HTML/JSP页面、终端/控制台等)、传输过程(接收请求时、响应时、连接数据库时)、程序源代码……

5. 拦截器与过滤器

首先,这2个组件的执行切入点不相同,过滤器是执行在所有的Servlet之前的,而拦截器是执行在DispatcherServlet之后、控制器之前的。

过滤器是Java EE体系中的,使用时,需要在web.xml中进行配置,而拦截器是SpringMVC框架中的,使用时,需要在Spring的配置文件中进行配置。

过滤器的过滤规则相对比较简单,只能通过<url-patter>节点配置它的适用范围,而拦截器的过滤规则相对比较灵活,即可以配置多项黑名单,还可以配置多项白名单。

作业

  1. 创建数据库tedu_ums

  2. 在数据库中创建数据表t_user,并在其中添加usernamepasswordphoneemailbirthday字段,使用自动递增的id作为主键

  3. 添加不少于5条数据,要求支持中文

  4. 查询所有数据

  5. 查询指定用户名的数据

  6. 统计数据的数量

  7. 删除指定id的数据

  8. 修改指定id的数据的password

以上操作只需要在控制台执行即可,使用记事本记录下所需的SQL语句。

JSD1806——8——SpringMVC相关推荐

  1. 【SpringMVC】概述

    概述: SpringMVC:是基于spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的.                       理解是servlet的一个升级 Sp ...

  2. 【SpringMVC】基本概念

    SpringMVC的基本概念 三层架构 我们的开发一般都是基于c/s或者b/s架构.在JavaEE开发中,几乎全都是基于B/S架构开发.在B/S架构中,系统标准的三层架构包括:表现层,业务层,持久层. ...

  3. SSM框架整合(Spring+SpringMVC+MyBatis)

    输出结果 1.Maven Web项目创建 之前有写过Eclipse+Maven创建web项目的帖子,如果需要,请参考这里写链接内容 创建好项目之后因为入下图: 2.SSM整合 2.1 引入需要的JAR ...

  4. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  5. SpringMVC——通俗易懂讲讲Ajax~

    聊聊Ajax 一.什么是Ajax 二.iframe标签简单伪造Ajax 三.Ajax的实现 1. 基本概念的了解 Ajax的核心是什么? XMLHttpRequest是什么? Ajax数据传输的数据格 ...

  6. Jackson、FastJson快速入门(整合SpringMVC)

    目录 1. 什么是 JSON 2. JSON 语法规则 3. JSON 与 JS 对象的关系 4. JSON 和 JS 对象互转 5. Jackson-数据格式转换 1. 环境搭建 2. 对象转jso ...

  7. RESTful风格及其SpringMVC实现

    目录 1.RESTful概念 2.RESTful功能 3.对比:传统方式操作资源 4.SpringMVC实现传统方式操作资源 5.使用RestFul操作资源 6.SpringMVC实现RESTful操 ...

  8. 使用注解开发SpringMVC详细配置教程

    目录 1.使用注解开发SpringMVC 1.新建一个普通的maven项目,添加web支持 2.在pom.xml中导入相关依赖 3.配置web.xml 4.编写SpringMVC配置文件 1. 自动扫 ...

  9. SpringMVC的form:form表单的使用

    为什么要使用SpringMVC的form:form表单,有两个原因:一是可以更加快捷的完成表单的开发,比如会替你做好数据类型装换等本来需要你自己动手的工作.其次就是能够更加方便的实现表单回显. 首先要 ...

最新文章

  1. 列出本地git仓库中的文件?
  2. 给新手程序员的16个工作必备小妙招,省下时间去LOL吧!
  3. 记事本安卓软件代码设计_用轻量级工具 Notepad3 替代 36 岁的微软记事本
  4. 《2018年云上挖矿态势分析报告》发布,非Web类应用安全风险需重点关注
  5. 移动终端开发工程师工作流程的总结
  6. 123456789中间任意加+或-结果等于100
  7. 编辑中的word变成只读_教大家word文档变成只读模式怎么改
  8. ## Android Studio 开发(四)--蓝牙通信
  9. A - Browsing History
  10. 友善串口助手使用教程_友善串口调试助手怎么进行配置-友善串口调试助手使用教程...
  11. 4递归实现阶乘计算器 5递归实现TreeView绑定表MenuTree
  12. 长期宅在家的人都有什么比较好的室内锻炼的方法?
  13. Sundot实习项目——高考志愿填报系统
  14. Visual2022安装步骤社区版,专业版or企业版安装(附注册码)(没有桌面图标的解决方法)
  15. Revit建模:使用技巧【族类应用】希望能帮大家提高效率
  16. C中出现:错误 C1010 在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include stdafx.h”等头文件
  17. 【RBF预测】基于RBF神经网络实现预测matlab源码
  18. 由Maleimide参与的Biotin-C5-Mal试剂具有荧光猝灭能力
  19. 劲乐园合歌(幽灵圣典+飞吧喜鹊+唯一+v3+幽灵圣典2)铃声 劲乐园...
  20. weui icon图标大全

热门文章

  1. 为什么我电脑在线看黄色很慢_为什么我的电脑这么慢?
  2. 如何使用docker容器中的redis
  3. 信息隐藏——扩频水印
  4. 编译、汇编、翻译原理知识概括
  5. 新概念英语第四册41-48课(转)
  6. 两张显卡在win10上如何使用
  7. Linux批量检测网络策略
  8. Windows直接拖拽文件复制到虚拟机Ubuntu
  9. MySQL中出现Duplicate entry 'XXX' for key 'PRIMARY'解决方案
  10. APS54083 DC/DC升降压恒流驱动芯片 2.4G调光 —提供DEMO板