Blog

springmvc运算-基础练手项目(1)-查询操作

1、编辑web.xml ,常规配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<filter>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<servlet>
		<servlet-name>dispatcherServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
			<!-- 配置 DispatcherServlet 的一个初始化参数: 配置 SpringMVC 配置文件的位置和名称 -->
		<!-- 
			实际上也可以不通过 contextConfigLocation 来配置 SpringMVC 的配置文件, 而使用默认的.
			默认的配置文件为: /WEB-INF/<servlet-name>-servlet.xml
		-->

		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
	
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcherServlet</servlet-name>
		<!-- 
               第一种:*.action 或者 *.do,访问以.action或*.do结尾 由DispatcherServlet进行解析
               第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析
               使用此种方式可以实现 RESTful风格的url
               第三种:/*,这样配置不对,使用这种配置,最终要转发到一个jsp页面时,
               仍然会由DispatcherServlet解析jsp地址,不能根据jsp页面找到handler,会报错。
        -->
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>

2、编辑转发配置文件(springmvc.xml)

<?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 http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">

	<!-- 配置自动扫描的包 -->
	<context:component-scan base-package="com.test.springmvc"></context:component-scan>

	<!-- 配置视图解析器 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/views/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>

</beans>

3、测试页面

第一步:请求控制器返回查询页面:
浏览器访问链接: http://localhost:8080/springmvc-2/doGetEmps
或者在页面中配置超链接

<body>
<a href="doGetEmps">请求所有雇员信息页面</a>
</body>

第二步:在查询页面中配置了超链接,请求控制器返回查询后的结果
查询页:getEmps.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<a href="emps">请求所有雇员信息(超链接属于get请求)</a>

</body>
</html>

本质上,只要一步直接请求好了,但是为了后续增删改的统一,所以拆成2步。

4、结果响应页面

showGetEmps.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Get All</title>
</head>
<body>

	<c:if test="${empty requestScope.employees}">
没有任何员工信息
</c:if>
	<c:if test="${ !empty requestScope.employees }">
		<table border="1" cellpadding="10" cellspacing="0">
			<tr>
				<th>ID</th>
				<th>LastName</th>
				<th>Email</th>
				<th>Gender</th>
				<th>Department</th>
				<th>Edit</th>
				<th>Delete</th>
			</tr>

			<c:forEach items="${requestScope.employees }" var="emp">
			<tr>
			<td>${emp.id}</td>
            <td>${emp.lastName}</td>
            <td>${emp.email}</td>	            
			<td>${emp.gender==0? "Female":"Male"}</td>					
			<td>${emp.department.departmentName}</td>			
			<td><a href="">Edit</a></td>
			<td><a href="">Delete</a></td>			
			</tr>
			</c:forEach>
		</table>
	</c:if>

</body>
</html>

5、编辑控制器

package com.test.springmvc.crud.handlers;

import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.test.springmvc.crud.dao.DepartmentDao;
import com.test.springmvc.crud.dao.EmployeeDao;
import com.test.springmvc.crud.entities.Employee;



@Controller
public class EmployeeHandler {

	@Autowired
	private EmployeeDao employeeDao;
	
	@Autowired
	private DepartmentDao departmentDao;

	
	//跳转到查看所有雇员信息页
        @RequestMapping("/doGetEmps")
	public String doGetEmps(){
    	return "getEmps";
    }
    
	//查看所有雇员信息,采用默认的get方法,属于crud操作
	@RequestMapping("/emps")
	public String getEmps(Map<String, Object> map){
		map.put("employees", employeeDao.getAll());
		return "showGetEmps";
	}

	
}

6、数据源配置

(1)entity

Employee.java

package com.test.springmvc.crud.entities;

import java.util.Date;

import javax.validation.constraints.Past;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;

public class Employee {

	private Integer id;
	
	private String lastName;

	private String email;
	//1 male, 0 female
	private Integer gender;
	
	private Department department;
	

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Integer getGender() {
		return gender;
	}

	public void setGender(Integer gender) {
		this.gender = gender;
	}

	public Department getDepartment() {
		return department;
	}

	public void setDepartment(Department department) {
		this.department = department;
	}

	
	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email="
				+ email + ", gender=" + gender + ", department=" + department
				+ "]";
	}

	public Employee(Integer id, String lastName, String email, Integer gender,
			Department department) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
		this.department = department;
	}

	public Employee() {
		// TODO Auto-generated constructor stub
	}
}

Department.java

package com.test.springmvc.crud.entities;

public class Department {

	private Integer id;
	private String departmentName;

	public Department() {
		// TODO Auto-generated constructor stub
	}
	
	public Department(int i, String string) {
		this.id = i;
		this.departmentName = string;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getDepartmentName() {
		return departmentName;
	}

	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}

	@Override
	public String toString() {
		return "Department [id=" + id + ", departmentName=" + departmentName
				+ "]";
	}
	
}

(2)Dao

package com.test.springmvc.crud.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.test.springmvc.crud.entities.Department;
import com.test.springmvc.crud.entities.Employee;


@Repository
public class EmployeeDao {

	private static Map<Integer, Employee> employees = null;
	
	@Autowired
	private DepartmentDao departmentDao;
	
	static{
		employees = new HashMap<Integer, Employee>();

		employees.put(1001, new Employee(1001, "E-AA", "[email protected]", 1, new Department(101, "D-AA")));
		employees.put(1002, new Employee(1002, "E-BB", "[email protected]", 1, new Department(102, "D-BB")));
		employees.put(1003, new Employee(1003, "E-CC", "[email protected]", 0, new Department(103, "D-CC")));
		employees.put(1004, new Employee(1004, "E-DD", "[email protected]", 0, new Department(104, "D-DD")));
		employees.put(1005, new Employee(1005, "E-EE", "[email protected]", 1, new Department(105, "D-EE")));
	}
	
	private static Integer initId = 1006;
	
	public void save(Employee employee){
		if(employee.getId() == null){
			employee.setId(initId++);
		}
		
		employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
		employees.put(employee.getId(), employee);
	}
	
	public Collection<Employee> getAll(){
		return employees.values();
	}
	
	public Employee get(Integer id){
		return employees.get(id);
	}
	
	public void delete(Integer id){
		employees.remove(id);
	}
}
package com.test.springmvc.crud.dao;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Repository;

import com.test.springmvc.crud.entities.Department;


@Repository
public class DepartmentDao {

	private static Map<Integer, Department> departments = null;
	
	static{
		departments = new HashMap<Integer, Department>();
		
		departments.put(101, new Department(101, "D-AA"));
		departments.put(102, new Department(102, "D-BB"));
		departments.put(103, new Department(103, "D-CC"));
		departments.put(104, new Department(104, "D-DD"));
		departments.put(105, new Department(105, "D-EE"));
	}
	
	public Collection<Department> getDepartments(){
		return departments.values();
	}
	
	public Department getDepartment(Integer id){
		return departments.get(id);
	}
	
}

 

springmvc运算-基础练手项目-概述

为了掌握springmvc的增删改查操作,进行的练手项目。

1、查询操作(Get)

2、添加操作(Post)

3、删除操作(Delete)

4、更新操作(Put)

总结:做各个功能模块时,应该都统一,方便以后的理解和维护。虽然可能会造成一定的冗余,但是总体上还是不错的。

在这个项目:分成两步请求了。第一步,请求控制器跳转到相应的增删改查页面。第二步,在增删改查页面中,又一次请求控制器,然后返回结果。

springmvc基础-@ModelAttribute注解详解

@ModelAttribute  三种用法

1、对handler中的方法进行注解:

@ModelAttribute 注解的方法,本质来说:会在其他方法执行前,会首先被调用。用于初始化一些参数。然后其他方法执行时,会先查找这些参数是否已经存在,若不存则创建一个。

参看:springmvc基础-控制器返回值总结

2、对handler内的方法的形参进行注解:

html表单提交的,可以在控制器内生产一个pojo对象。默认情况下:会将该pojo对象的类型的首字母小写作为关键字,将pojo对象值 作为参数。放置到request属性中。如果pojo对象前面有@ModelAttribute(value =”XXX”),则request存储的pojo对象关键字为XXX,不再是该对象的类型的首字母小写。

参看:springmvc基础-请求映射总结

3、在jsp中的使用springmvc 的表单标签 问题:

参数页面:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<body>

       <!--  
		1. WHY 使用 form 标签呢 ?
		可以更快速的开发出表单页面, 而且可以更方便的进行表单值的回显
		2. 注意:
		可以通过 modelAttribute 属性指定绑定的模型属性,
		若没有指定该属性,则默认从 request 域对象中读取 command 的表单 bean
		如果该属性值也不存在,则会发生错误。
	-->

        <form:form action="emp" method="post" modelAttribute="employee">
		<!-- path标签对应 html表单标签的 name属性值 -->
                LastName:<form:input path="lastName"  />

		<input type="submit" value="submit" />
	</form:form>

       <!-- form:form 标签在springmvc会有回显值,一开始加载页面时,就需要填充表单,
       所以 modelAttribute="employee" 用来标记从 request中的 取出 employee 关键字的 属性值 -->

</body>

编辑处理器:

@RequestMapping(value="/emp", method=RequestMethod.GET)
	public String input(Map<String, Object> map){

	    //主要是每次 访问表单时,springmvc 都会 回显 之前的表单值,因为第一次 没有创建,所以需要默认新建一个
		map.put("employee", new Employee());
		return "input";
	}

 

eclipse-常用快捷键总结

1、参看某个类

MAC: command + shift+T
Windows:ctrl+shift+T

 

eclipse 查找类方法 用 ctrl+ Shift+t

eclipse 查找接口实现类:ctrl+t

eclipse 类全名:可以 command或crtl  +鼠标 点击 跳转 查看类

command + 2 + l  自动生成 局部变量


  1. ctrl+shift+r:打开资源

这可能是所有快捷键组合中最省时间的了。这组快捷键可以让你打开你的工作区中任何一个文件,而你只需要按下文件名或mask名中的前几个字母,比如applic*.xml。美中不足的是这组快捷键并非在所有视图下都能用。

2. ctrl+o:快速outline

如果想要查看当前类的方法或某个特定方法,但又不想把代码拉上拉下,也不想使用查找功能的话,就用ctrl+o吧。它可以列出当前类中的所有方法及属性,你只需输入你想要查询的方法名,点击enter就能够直接跳转至你想去的位置。

3. ctrl+e:快速转换编辑器

这组快捷键将帮助你在打开的编辑器之间浏览。使用ctrl+page down或ctrl+page up可以浏览前后的选项卡,但是在很多文件打开的状态下,ctrl+e会更加有效率。

4. ctrl+2,L:为本地变量赋值

开发过程中,我常常先编写方法,如Calendar.getInstance(),然后通过ctrl+2快捷键将方法的计算结果赋值于一个本地变量之上。 这样我节省了输入类名,变量名以及导入声明的时间。Ctrl+F的效果类似,不过效果是把方法的计算结果赋值于类中的域。

    5. alt+shift+r:重命名

重命名属性及方法在几年前还是个很麻烦的事,需要大量使用搜索及替换,以至于代码变得零零散散的。今天的Java IDE提供源码处理功能,Eclipse也是一样。现在,变量和方法的重命名变得十分简单,你会习惯于在每次出现更好替代名称的时候都做一次重命名。要使 用这个功能,将鼠标移动至属性名或方法名上,按下alt+shift+r,输入新名称并点击回车。就此完成。如果你重命名的是类中的一个属性,你可以点击alt+shift+r两次,这会呼叫出源码处理对话框,可以实现get及set方法的自动重命名。

    6. alt+shift+l以及alt+shift+m:提取本地变量及方法

源码处理还包括从大块的代码中提取变量和方法的功能。比如,要从一个string创建一个常量,那么就选定文本并按下alt+shift+l即可。如果同 一个string在同一类中的别处出现,它会被自动替换。方法提取也是个非常方便的功能。将大方法分解成较小的、充分定义的方法会极大的减少复杂度,并提 升代码的可测试性。

    7. shift+enter及ctrl+shift+enter

Shift+enter在当前行之下创建一个空白行,与光标是否在行末无关。Ctrl+shift+enter则在当前行之前插入空白行。

    8. Alt+方向键

这也是个节省时间的法宝。这个组合将当前行的内容往上或下移动。在try/catch部分,这个快捷方式尤其好使。

    9. ctrl+m

大显示屏幕能够提高工作效率是大家都知道的。Ctrl+m是编辑器窗口最大化的快捷键。

    10. ctrl+.及ctrl+1:下一个错误及快速修改

ctrl+.将光标移动至当前文件中的下一个报错处或警告处。这组快捷键我一般与ctrl+1一并使用,即修改建议的快捷键。新版Eclipse的修改建 议做的很不错,可以帮你解决很多问题,如方法中的缺失参数,throw/catch exception,未执行的方法等等。


更多快捷键组合可在Eclipse按下ctrl+shift+L查看。

让我们按照使用频率来看看我最爱用的一些热键组合。(注:以下内容在Eclipse3.02及一上版本通过测试)

1. Control-Shift-T: 打开类型(Open type)。如果你不是有意磨洋工,还是忘记通过源码树(source tree)打开的方式吧。用eclipse很容易打开接口的实现类的,按ctrl+t会列出接口的实现类列表

2. Control-Shift-R: 打开资源(不只是用来寻找Java文件)。小提示:利用Navigator视图的黄色双向箭头按钮让你的编辑窗口和导航器相关联。这会让你打开的文件对应显示在导航器的层级结构中,这样便于组织信息。如果这影响了速度,就关掉它。

3. F3: 打开申明(Open declaration)。或者,利用Declaration Tab(在Java视图模式下,选择Windows –> Show View — > Declaration)。当你选中代码中的一个方法,然后按这个按键,它会把整个方法在申明方框里显示出来。

4. Alt-left arrow: 在导航历史记录(Navigation History)中后退。就像Web浏览器的后退按钮一样,在利用F3跳转之后,特别有用。(用来返回原先编译的地方)

5. Alt-right arrow: 导航历史记录中向前。

6. Control-Q: 回到最后一次编辑的地方。这个快捷键也是当你在代码中跳转后用的。特别是当你钻的过深,忘记你最初在做什么的时候。

7. Control-Shift-G: 在workspace中搜索引用(reference)。这 是重构的前提。对于方法,这个热键的作用和F3恰好相反。它使你在方法的栈中,向上找出一个方法的所有调用者。一个与此相关的功能是开启“标记”功能 (occurrence marking) 。选择Windows->Preferences->Java-> Editor-> Mark Occurrences,勾选选项。这时,当你单击一个元素的时候,代码中所有该元素存在的地方都会被高亮显示。我个人只使用“标记本地变量”(Mark Local Variables)。注意:太多的高亮显示会拖慢Eclipse。

8. Control-Shift-F: CodeàJavaàPreferencesà根据代码风格设定重新格式化代码。我 们的团队有统一的代码格式,我们把它放在我们的wiki上。要这么做,我们打开Eclipse,选择Window Style,然后设置Code Formatter,Code Style和Organize Imports。利用导出(Export)功能来生成配置文件。我们把这些配置文件放在wiki上,然后团队里的每个人都导入到自己的Eclipse中。

9. Control-O: 快速概要(quick outline)。通过这个快捷键,你可以迅速的跳到一个方法或者属性,只需要输入名字的头几个字母。

10. Control-/: 对一行注释或取消注释。对于多行也同样适用。

11. Control-Alt-down arrow: 复制高亮显示的一行或多行。

12. Alt-down arrow: 将一行或多行向下移动。Alt-up arrow会向上移动。

其他的热键在菜单里有。你可以通过按下Control-Shift-L(从3.1版本开始), 看到所有快捷键的列表。按下Control-Shift-L两次,会显示热键对话框(Keys Preferences dialog),你可以在这里自己设置热键。我欢迎你在Talkback部分发表你的Eclipse提示。

其他的Eclipse窍门

我总结了几个相关的小窍门:

锁定命令行窗口:在命令行视图中(Window ->Show View ->Other ->Basic ->Console),试试看用滚动锁定按钮来锁定控制台输出不要滚屏。

使用Ant视图: 在我的Java或Debug模式下,我喜欢显示出Ant视图,这样我就可以迅速的运行Ant任务。通过Window Ant可以找到该视图。把Ant视图放在屏幕的一角, 通过“添加编译文件(Addà Other à Show View à Buildfiles)”按钮来添加build.xml文件。在3.1版本中,甚至支持Ant调试脚本语言。

自动遍历一个集合:for + Control-Space: 如果你还不知道,那么你应该记住Control-Space是自动完成功能。在Eclipse中,你还可以自动完成结构。在一个数组或集合范围内,试试看 输入“for”然后按下Control-Space键。Eclipse会问你你想要遍历哪一个集合然后自动完成循环代码。

使用分级布局: 在包浏览视图(Package Explorer view)中默认的布局(扁平式)方式让我困惑,它把包的全名显示在导航树(navigation tree)中。我更喜欢我源码的包和文件系统视图,在Eclipse中叫做分级布局(Hierarchical Layout)。要切换到这种模式,点击包浏览视图中向下的按钮,选择布局(Layout),然后选择分级(Hierarchial)。

一次显示多个文件:你可以一次浏览多个文件。把不在激活状态的编辑窗口拖到激活窗口的底部或侧边的滚动条上,就可以打开该编辑窗口。这是我能描述该窍门的最好方式了。

同时打开两个Eclipse: 要将改动从一个CVS分支上合并到另外一个上,我喜欢通过同时打开两个工作目录(Workspace)不同Eclipse来实现。这样我可以通过比较 CVS上的最新版本看到所有的变化(右键单击工程,然后选择Compare Lastest from HEAD)然后把每一个变化都合并到另外一个CVS分支上。启动多个Eclipse的最简单的方法是利用Eclipseàwith Launcher。

Implementors插件:安装一个能够跳到一个接口的实现的插件。如果你是个dependency injection 粉丝,或者正在基于编写优良的接口工作,那么你需要一个这样的插件来加速代码导航。 你可以在SourceForge找到这个插件。

Ctrl+Alt+H

如果你想知道一个类的方法到底被那些其他的类调用,那么请选中这个方法名,然后按“Ctrl+Alt+H”,

Eclipse就会显示出这个方法被哪些方法调用,最终产生一个调用关系树。
1. Ctrl+左键

这个是大多数人经常用到的,用来查看变量、方法、类的定义

2. Ctrl+O

查看一个类的纲要,列出其方法和成员变量。提示:再多按一次Ctrl+O,可以列出该类继承的方法和变量。

助记:”O”—>”Outline”—>”纲要”

3. Ctrl+T

查看一个类的继承关系树,是自顶向下的,再多按一次Ctrl+T, 会换成自底向上的显示结构。

提示:选中一个方法名,按Ctrl+T,可以查看到有这个同名方法的父类、子类、接口。

助记:”T”——->”Tree”—–>”层次树”

4.Alt+左右方向键

我们经常会遇到看代码时Ctrl+左键,层层跟踪,然后迷失在代码中的情况,这时只需要按“Alt+左方向键

”就可以退回到上次阅读的位置,同理,按“Alt+右方向键”会前进到刚才退回的阅读位置,就像浏览器的

前进和后退按钮一样。

导入包:Ctrl+Shift+O
编辑
作用域 功能 快捷键
全局 查找并替换 Ctrl+F
文本编辑器 查找上一个 Ctrl+Shift+K
文本编辑器 查找下一个 Ctrl+K
全局 撤销 Ctrl+Z
全局 复制 Ctrl+C
全局 恢复上一个选择 Alt+Shift+↓
全局 剪切 Ctrl+X
全局 快速修正 Ctrl1+1
全局 内容辅助 Alt+/
全局 全部选中 Ctrl+A
全局 删除 Delete
全局 上下文信息 Alt+?
Alt+Shift+?
Ctrl+Shift+Space
Java编辑器 显示工具提示描述 F2
Java编辑器 选择封装元素 Alt+Shift+↑
Java编辑器 选择上一个元素 Alt+Shift+←
Java编辑器 选择下一个元素 Alt+Shift+→
文本编辑器 增量查找 Ctrl+J
文本编辑器 增量逆向查找 Ctrl+Shift+J
全局 粘贴 Ctrl+V
全局 重做 Ctrl+Y
查看
作用域 功能 快捷键
全局 放大 Ctrl+=
全局 缩小 Ctrl+-
窗口
作用域 功能 快捷键
全局 激活编辑器 F12
全局 切换编辑器 Ctrl+Shift+W
全局 上一个编辑器 Ctrl+Shift+F6
全局 上一个视图 Ctrl+Shift+F7
全局 上一个透视图 Ctrl+Shift+F8
全局 下一个编辑器 Ctrl+F6
全局 下一个视图 Ctrl+F7
全局 下一个透视图 Ctrl+F8
文本编辑器 显示标尺上下文菜单 Ctrl+W
全局 显示视图菜单 Ctrl+F10
全局 显示系统菜单 Alt+-
导航
作用域 功能 快捷键
Java编辑器 打开结构 Ctrl+F3
全局 打开类型 Ctrl+Shift+T
全局 打开类型层次结构 F4
全局 打开声明 F3
全局 打开外部javadoc Shift+F2
全局 打开资源 Ctrl+Shift+R
全局 后退历史记录 Alt+←
全局 前进历史记录 Alt+→
全局 上一个 Ctrl+,
全局 下一个 Ctrl+.
Java编辑器 显示大纲 Ctrl+O
全局 在层次结构中打开类型 Ctrl+Shift+H
全局 转至匹配的括号 Ctrl+Shift+P
全局 转至上一个编辑位置 Ctrl+Q
Java编辑器 转至上一个成员 Ctrl+Shift+↑
Java编辑器 转至下一个成员 Ctrl+Shift+↓
文本编辑器 转至行 Ctrl+L
搜索
作用域 功能 快捷键
全局 出现在文件中 Ctrl+Shift+U
全局 打开搜索对话框 Ctrl+H
全局 工作区中的声明 Ctrl+G
全局 工作区中的引用 Ctrl+Shift+G
文本编辑
作用域 功能 快捷键
文本编辑器 改写切换 Insert
文本编辑器 上滚行 Ctrl+↑
文本编辑器 下滚行 Ctrl+↓
文件
作用域 功能 快捷键
全局 保存 Ctrl+X
Ctrl+S
全局 打印 Ctrl+P
全局 关闭 Ctrl+F4
全局 全部保存 Ctrl+Shift+S
全局 全部关闭 Ctrl+Shift+F4
全局 属性 Alt+Enter
全局 新建 Ctrl+N
项目
作用域 功能 快捷键
全局 全部构建 Ctrl+B
源代码
作用域 功能 快捷键
Java编辑器 格式化 Ctrl+Shift+F
Java编辑器 取消注释 Ctrl+/
Java编辑器 注释 Ctrl+/
Java编辑器 添加单个import Ctrl+Shift+M
Java编辑器 组织多个import Ctrl+Shift+O
Java编辑器 使用try/catch块来包围 未设置,太常用了,所以在这里列出,建议自己设置。
也可以使用Ctrl+1自动修正。
调试/运行
作用域 功能 快捷键
全局 单步返回 F7
全局 单步跳过 F6
全局 单步跳入 F5
全局 单步跳入选择 Ctrl+F5
全局 调试上次启动 F11
全局 继续 F8
全局 使用过滤器单步执行 Shift+F5
全局 添加/去除断点 Ctrl+Shift+B
全局 显示 Ctrl+D
全局 运行上次启动 Ctrl+F11
全局 运行至行 Ctrl+R
全局 执行 Ctrl+U
重构
作用域 功能 快捷键
全局 撤销重构 Alt+Shift+Z
全局 抽取方法 Alt+Shift+M
全局 抽取局部变量 Alt+Shift+L
全局 内联 Alt+Shift+I
全局 移动 Alt+Shift+V
全局 重命名 Alt+Shift+R
全局 重做 Alt+Shift+Y

(1)Ctrl+M切换窗口的大小
(2)Ctrl+Q跳到最后一次的编辑处
(3)F2当鼠标放在一个标记处出现Tooltip时候按F2则把鼠标移开时Tooltip还会显示即Show Tooltip

Description。
F3跳到声明或定义的地方。
F5单步调试进入函数内部。
F6单步调试不进入函数内部,如果装了金山词霸2006则要把“取词开关”的快捷键改成其他的。
F7由函数内部返回到调用处。
F8一直执行到下一个断点。
(4)Ctrl+Pg~对于XML文件是切换代码和图示窗口
(5)Ctrl+Alt+I看Java文件中变量的相关信息
(6)Ctrl+PgUp对于代码窗口是打开“Show List”下拉框,在此下拉框里显示有最近曾打开的文件
(7)Ctrl+/ 在代码窗口中是这种//~注释。
Ctrl+Shift+/ 在代码窗口中是这种/*~*/注释,在JSP文件窗口中是〈!–~–〉。
(8)Alt+Shift+O(或点击工具栏中的Toggle Mark Occurrences按钮) 当点击某个标记时可使本页面中其他

地方的此标记黄色凸显,并且窗口的右边框会出现白色的方块,点击此方块会跳到此标记处。
(9)右击窗口的左边框即加断点的地方选Show Line Numbers可以加行号。
(10)Ctrl+I格式化激活的元素Format Active Elements。
Ctrl+Shift+F格式化文件Format Document。
(11)Ctrl+S保存当前文件。
Ctrl+Shift+S保存所有未保存的文件。
(12)Ctrl+Shift+M(先把光标放在需导入包的类名上) 作用是加Import语句。
Ctrl+Shift+O作用是缺少的Import语句被加入,多余的Import语句被删除。
(13)Ctrl+Space提示键入内容即Content Assist,此时要将输入法中Chinese(Simplified)IME-

Ime/Nonlme Toggle的快捷键(用于切换英文和其他文字)改成其他的。
Ctrl+Shift+Space提示信息即Context Information。
(14)双击窗口的左边框可以加断点。
(15)Ctrl+D删除当前行。

Eclipse快捷键大全
Ctrl+1 快速修复(最经典的快捷键,就不用多说了)
Ctrl+D: 删除当前行
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)

Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑ 当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)

Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性

Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)

Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/ 注释当前行,再按则取消注释
Ctrl+O 快速显示 OutLine
Ctrl+T 快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一个
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)

Ctrl+/(小键盘) 折叠当前类中的所有代码

Ctrl+×(小键盘) 展开当前类中的所有代码

Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用

Alt+/来代替)

Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)

Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有

,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)

Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)

Ctrl+Shift+F4 关闭所有打开的Editer

Ctrl+Shift+X 把当前选中的文本全部变味小写

Ctrl+Shift+Y 把当前选中的文本全部变为小写

Ctrl+Shift+F 格式化当前代码

Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之

)

下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开

头的了)

Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)

Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)

Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)

Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)

Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)

Alt+Shift+I 合并变量(可能这样说有点不妥Inline)
Alt+Shift+V 移动函数和变量(不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)

来自:https://blog.csdn.net/pochuanpiao/article/details/12497169

springmvc基础-视图与视图解析器详解

一、概述

  • 请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的 处理方法,Spring MVC 也会在内部将它们装配成一个ModelAndView 对象,它包含了逻辑名和模型对象的视图。
  • Spring MVC 借助视图解析器(ViewResolver)得到最终 的视图对象(View),最终的视图可以是 JSP ,也可能是Excel、JFreeChart  等各种表现形式的视图。
  • 对于最终究竟采取何种视图对象对模型数据进行渲染,处 理器并不关心,处理器工作重点聚焦在生产模型数据的工 作上,从而实现 MVC 的充分解耦。

二、视图含义

  • 视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客 户。
  • 为了实现视图模型和具体实现技术的解耦,Spring 在org.springframework.web.servlet 包中定义了一个高度抽象的 View接口:

  • 视图对象由视图解析器负责实例化。由于视图是无状态的,所以他们不会有线程安全的问题

三、常用的视图实现类

四、视图解析器

  • SpringMVC 为逻辑视图名的解析提供了不同的策略,可 以在 Spring WEB 上下文中配置一种或多种解析策略,并 指定他们之间的先后顺序。每一种映射策略对应一个具体 的视图解析器实现类。
  • 视图解析器的作用比较单一:将逻辑视图解析为一个具体 的视图对象。
  • 所有的视图解析器都必须实现 ViewResolver 接口:

五、常用的视图解析器实现类

  • 程序员可以选择一种视图解析器或混用多种视图解析器
  • 每个视图解析器都实现了 Ordered 接口并开放出一个 order 属性,可 以通过 order 属性指定解析器的优先顺序,order 越小优先级越高。
  • SpringMVC 会按视图解析器顺序的优先顺序对逻辑视图名进行解析,直到解析成功并返回视图对象,否则将抛出 ServletException 异常

六、自定义视图

大白话总结:1、编辑正常的请求连接提交给控制器。2、控制器处理请求 返回视图名。3、视图解析器  利用视图名   从IOC容器中 获取 自定义视图。

比如 控制器返回”success”,

如果配置用InternalResourceViewResolver解析的视图,肯定有解析器的前缀和后缀,可能最终解析完路径是 /WEB-INF/views/success.jsp
[即实际地址是:网站根目录/WEB-INF/views/success.jsp]
因为如果不想用 解析器的前缀和后缀 ,那么控制器的返回值应该是 WEB-INF/views/success.jsp
补充:控制器返回字符串值 “/WEB-INF/views/success.jsp” 和这个效果一样 “WEB-INF/views/success.jsp”

如果配置用BeanNameViewResolver视图解析器,则会根据视图名去IOC容器里面,获取一个视图。

1、编辑测试页

在测试页面添加超链接

<body>
测试自定义view
<a href="testredirect">testdiyview</a>
</body>

2、编辑控制器

@RequestMapping("/diyview")
public String testdiyview(){
		
	System.out.println("test-diyview");
	//返回值是自定义视图的类名且第一个字母需小写
        return "helloView";
}

3、编辑转发配置页

<!-- 配置视图  BeanNameViewResolver 解析器: 使用视图的名字来解析视图 -->
<!-- 通过 order 属性来定义视图解析器的优先级, order 值越小优先级越高 , 至于InternalResourceViewResolver其优先级是最低的 -->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <property name="order" value="100"></property>
</bean>

核心注意:当时在转发配置页中:扫描注解包时,扫描路径是
com.test.springmvc.handlers 所以com.test.springmvc.views 无法扫描到【views包下面有view类,里面有注解】所以正确做法是:

<!-- 对于注解的Handler可以单个配置 实际开发中建议使用组件扫描 -->
<!-- <bean class="com.test.springmvc.handlers.HelloWorld" />  -->
<!-- 可以扫描controller、service、...这里让扫描controller,指定controller的包,但是view的包下面的注解类就扫描不到了,
所以 com.test.springmvc.handlers 改成 com.test.springmvc就行啦,子目录都能扫描得到  -->
<!--下面是 配置扫描  指定目录包 中的 java文件 -->
<context:component-scan base-package="com.test.springmvc"></context:component-scan>

4、创建自定义view

package com.test.springmvc.views;

import java.util.Date;
import java.util.Map;

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

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.View;

//这个@Component注解的意思是  将该view 放到 IOC 容器里面 
@Component
public class HelloView implements View {

	@Override
	public String getContentType() {
		// TODO Auto-generated method stub
		return "text/html";
	}

	@Override
	public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		// TODO Auto-generated method stub
		response.getWriter().print("hello view ,time" + new Date());
	}

}

 

springmvc基础-控制器返回值总结

关于请求问题:
请求映射到了控制器,如果控制器处理后的返回值是 “success” ,经过解析器转换成 WEB-INF/views/success.jsp 【如果解析器没有前后缀,则控制器的返回值就必须是完整的,也就是带上了前后缀的】
最后的完整路径是 :网站根目录/WEB-INF/views/success.jsp
【当然,因为/WEB-INF 文件夹仅对服务器可见,对浏览器隐藏的,所以直接访问网站根目录/WEB-INF/views/success.jsp  是无法访问的】

springmvc的控制器【controller】或者叫Handler,它的返回值是:ModelAndView 类型。之前学习过说返回值可以是map或string。其实到最后,都会内部转化成ModelAndView 类型。

一、基础流程

测试: <a href=”springmvc/testModelAndView”>Test ModelAndView</a>

控制器:

/**
	 * 目标方法的返回值可以是 ModelAndView 类型。 
	 * 其中可以包含视图和模型信息
	 * SpringMVC 会把 ModelAndView 的 model 中数据放入到 request 域对象中. 
	 * @return
	 */
	@RequestMapping("/testModelAndView")
	public ModelAndView testModelAndView(){
		String viewName = SUCCESS;
		ModelAndView modelAndView = new ModelAndView(viewName);
		
		//添加模型数据到 ModelAndView 中.
		modelAndView.addObject("time", new Date());
		
		return modelAndView;
	}

控制器中 ModelAndView 的构造函数名,就是ModelAndView中的view里面的转发页面的名字。

返回页jsp编辑:
time: ${requestScope.time }

二、控制器中的map参数

控制器中的map参数,最后变成了返回值 ModelAndView 中的model。至于ModelAndView 中的view里面包含了返回页的名字。

测试: <a href=”springmvc/testMap”>Test Map</a>

控制器:

      /**
	 * 目标方法可以添加 Map 类型(实际上也可以是 Model 类型或 ModelMap 类型)的参数. 【因为是继承关系,最后都是一样的】
	 * @param map
	 * @return
	 */
	@RequestMapping("/testMap")
	public String testMap(Map<String, Object> map){
		System.out.println(map.getClass().getName()); 
		map.put("names", Arrays.asList("Tom", "Jerry", "Mike"));
		return "success";
	}

返回页jsp编辑:

names: ${requestScope.names }

三、控制器中的@SessionAttributes

@SessionAttributes : 该注解只能放在类的上面. 而不能修饰放方法. 

测试:<a href=”springmvc/testSessionAttributes”>Test SessionAttributes</a>

控制器:

@SessionAttributes(value={"user"}, types={String.class})
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest {

      /**
	 * @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外(实际上使用的是 value 属性值),
	 * 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(实际上使用的是 types 属性值)
	 * 
	 * 注意: 该注解只能放在类的上面. 而不能修饰放方法. 
	 */
	@RequestMapping("/testSessionAttributes")
	public String testSessionAttributes(Map<String, Object> map){
		User user = new User("Tom", "123456", "[email protected]", 15);
		map.put("user", user);
		map.put("school", "atguigu");
		return "success";
	}

}

这个实验表示:加了@SessionAttributes 注解后,map会根据相应的关键字或类型名,额外放置一份数据到session中。原本map放置到request中的信息,仍会保留在request中。

返回值jsp页:

	request user: ${requestScope.user }
	<br><br>
	
	session user: ${sessionScope.user }
	<br><br>
	
	request school: ${requestScope.school }
	<br><br>
	
	session school: ${sessionScope.school }

四、控制器中的@ModelAttribute

@ModelAttribute 注解的方法,本质来说:会在其他方法执行前,会首先被调用。用于初始化一些参数。然后其他方法执行时,会先查找这些参数是否已经存在,若不存则创建一个。

这个例子:介绍的是从数据库中读取一段信息,然后修改了一部分,又重新保存到数据库中。
测试页:

<!--  
		模拟修改操作
		1. 原始数据为: 1, Tom, 123456,[email protected],12
		2. 密码不能被修改.
		3. 表单回显, 模拟操作直接在表单填写对应的属性值
	-->
	<form action="springmvc/testModelAttribute" method="Post">
		<input type="hidden" name="id" value="1"/>
		username: <input type="text" name="username" value="Tom"/>
		<br>
		email: <input type="text" name="email" value="[email protected]"/>
		<br>
		age: <input type="text" name="age" value="12"/>
		<br>
		<input type="submit" value="Submit"/>
	</form>

控制器:

       /**
	 * 1. 有 @ModelAttribute 标记的方法, 会在每个目标方法执行之前被 SpringMVC 调用! 
	 * 2. @ModelAttribute 注解也可以来修饰目标方法 POJO 类型的入参, 其 value 属性值有如下的作用:
	 * 1). SpringMVC 会使用 value 属性值在 implicitModel 中查找对应的对象, 若存在则会直接传入到目标方法的入参中.
	 * 2). SpringMVC 会一 value 为 key, POJO 类型的对象为 value, 存入到 request 中. 
	 */
	@ModelAttribute
	public void getUser(@RequestParam(value="id",required=false) Integer id, 
			Map<String, Object> map){
		System.out.println("modelAttribute method");
		if(id != null){
			//模拟从数据库中获取对象
			User user = new User(1, "Tom", "123456", "[email protected]", 12);
			System.out.println("从数据库中获取一个对象: " + user);
			
			map.put("user", user);
		}
	}
	
	/**
	 * 运行流程:
	 * 1. 执行 @ModelAttribute 注解修饰的方法: 从数据库中取出对象, 把对象放入到了 Map 中. 键为: user
	 * 2. SpringMVC 从 Map 中取出 User 对象, 并把表单的请求参数赋给该 User 对象的对应属性.
	 * 3. SpringMVC 把上述对象传入目标方法的参数. 
	 * 
	 * 注意: 在 @ModelAttribute 修饰的方法中, 放入到 Map 时的键需要和目标方法入参类型的第一个字母小写的字符串一致!
	 * 
	 * SpringMVC 确定目标方法 POJO 类型入参的过程
	 * 1. 确定一个 key:
	 * 1). 若目标方法的 POJO 类型的参数木有使用 @ModelAttribute 作为修饰, 则 key 为 POJO 类名第一个字母的小写
	 * 2). 若使用了  @ModelAttribute 来修饰, 则 key 为 @ModelAttribute 注解的 value 属性值. 
	 * 2. 在 implicitModel 中查找 key 对应的对象, 若存在, 则作为入参传入
	 * 1). 若在 @ModelAttribute 标记的方法中在 Map 中保存过, 且 key 和 1 确定的 key 一致, 则会获取到. 
	 * 3. 若 implicitModel 中不存在 key 对应的对象, 则检查当前的 Handler 是否使用 @SessionAttributes 注解修饰, 
	 * 若使用了该注解, 且 @SessionAttributes 注解的 value 属性值中包含了 key, 则会从 HttpSession 中来获取 key 所
	 * 对应的 value 值, 若存在则直接传入到目标方法的入参中. 若不存在则将抛出异常. 
	 * 4. 若 Handler 没有标识 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 则
	 * 会通过反射来创建 POJO 类型的参数, 传入为目标方法的参数
	 * 5. SpringMVC 会把 key 和 POJO 类型的对象保存到 implicitModel 中, 进而会保存到 request 中. 
	 * 
	 * 源代码分析的流程
	 * 1. 调用 @ModelAttribute 注解修饰的方法. 实际上把 @ModelAttribute 方法中 Map 中的数据放在了 implicitModel 中.
	 * 2. 解析请求处理器的目标参数, 实际上该目标参数来自于 WebDataBinder 对象的 target 属性
	 * 1). 创建 WebDataBinder 对象:
	 * ①. 确定 objectName 属性: 若传入的 attrName 属性值为 "", 则 objectName 为类名第一个字母小写. 
	 * *注意: attrName. 若目标方法的 POJO 属性使用了 @ModelAttribute 来修饰, 则 attrName 值即为 @ModelAttribute 
	 * 的 value 属性值 
	 * 
	 * ②. 确定 target 属性:
	 * 	> 在 implicitModel 中查找 attrName 对应的属性值. 若存在, ok
	 * 	> *若不存在: 则验证当前 Handler 是否使用了 @SessionAttributes 进行修饰, 若使用了, 则尝试从 Session 中
	 * 获取 attrName 所对应的属性值. 若 session 中没有对应的属性值, 则抛出了异常. 
	 * 	> 若 Handler 没有使用 @SessionAttributes 进行修饰, 或 @SessionAttributes 中没有使用 value 值指定的 key
	 * 和 attrName 相匹配, 则通过反射创建了 POJO 对象
	 * 
	 * 2). SpringMVC 把表单的请求参数赋给了 WebDataBinder 的 target 对应的属性. 
	 * 3). *SpringMVC 会把 WebDataBinder 的 attrName 和 target 给到 implicitModel. 
	 * 近而传到 request 域对象中. 
	 * 4). 把 WebDataBinder 的 target 作为参数传递给目标方法的入参. 
	 */
	@RequestMapping("/testModelAttribute")
	public String testModelAttribute(User user){
		System.out.println("修改: " + user);
		return "success";
	}

转发显示jsp页:

request user: ${requestScope.user }

五、视图和视图解析器

请求处理方法执行完成后,最终返回一个 ModelAndView 对象。对于那些返回 String,View 或 ModeMap 等类型的处理方法,Spring MVC 也会在内部将它们装配成一个ModelAndView 对象,它包含了逻辑名和模型对象的视图。

Spring MVC 借助视图解析器(ViewResolver)得到最终的视图对象(View),最终的视图可以是 JSP ,也可能是Excel、JFreeChart 等各种表现形式的视图。

对于最终究竟采取何种视图对象对模型数据进行渲染,处理器并不关心,处理器工作重点聚焦在生产模型数据的工作上,从而实现 MVC 的充分解耦。

六、重定向和转发

  • 一般情况下,控制器方法返回字符串类型的值会被当成逻 辑视图名处理
  • 如果返回的字符串中带 forward:redirect: 前缀时,SpringMVC 会对他们进行特殊处理:将 forward: 和redirect: 当成指示符,其后的字符串作为 URL 来处理

–redirect:success:请求重定向到  ->  站点入口所在目录/success ,继续交由handler处理

–forward:success:请求转发到    ->  站点入口所在目录/success ,继续交由handler处理【不过转发时,请求路径是不会跳转的】

测试页:

@RequestMapping("/testredirect")
public String testRedirect(){

System.out.println("test-redirect");

//return "redirect:/index";
return "forward:/success";

}

七、静态资源请求

核心说明:
1、如果url请求对象是 xxx.jsp ,就按照网站路径去查找对应的文件。
2、如果url请求对象不是 xxx.jsp 比如 xx.html 或者 xx ,就交给视图解析器【如果设置了前缀和后缀,就转换成最终路径】去查询路径中是否存在文件。

这样,就造成了无法直接请求静态资源的问题:(在配置开发环境中已提到)

为了能够在网页中请求静态资源,如加载js和css等,所以需要  编辑   转发配置器。

 <!--  
		default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
		它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 
		Servlet 处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理

		一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
		若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定
		
	-->
	<mvc:default-servlet-handler/>
	<!-- 前面的 mvc:default-servlet-handler 可以解决静态资源了,但是让其他经过handler的请求,又失效了,所以配置了下面的 mvc:annotation-driven 来解决问题 -->
	<mvc:annotation-driven></mvc:annotation-driven>

 

 

 

springmvc基础-请求映射总结

@RequestMapping  请求映射总结

注意:

@RequestMapping(“/helloworld”)
public String hello( HttpServletRequest request ,HttpServletResponse response){
System.out.println(“hello request” + request.toString());
System.out.println(“hello response” + response.toString());
return “success”;
}
handler 控制器:函数参数 可以有request和 response,可以有其中一个,也可以都没有,request和response 都是spring容器注入的。他们是接口。还有将request 和 response 内部对象  作为形参的 。另外也可以将  map【返回值ModelAndView中的model】作为形参。

经过了测试:如果  控制器的映射是这个–>@RequestMapping("/") 那么访问网站根目录时,还是以 网站根目录下的 index.jsp、 index.html  等做为结果 返回给浏览器。


一、请求映射-路径区分

1、请求映射-路径区分-基础

<a href="springmvc/testRequestMapping">Test RequestMapping</a>
package com.test.springmvc.handlers;

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

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

@Controller // spring里面 又名handler 处理器
public class HelloWorld {

	/**
	 * 1、使用RequestMapping 注解来映射 请求的url
	 * 2、返回值会通过视图解析器解析为实际的物理视图,
	 * 对于org.springframework.web.servlet.view.InternalResourceViewResolver
	 * 会做如下解析,prefix + returnVal + suffix 得到实际的物理视图。然后做转发操作。
	 * 即 WEB-INF/views/success.jsp
 	 */
	@RequestMapping("/helloworld")
	public String hello( HttpServletRequest request,HttpServletResponse response){
		System.out.println("hello request" + request.toString());
		System.out.println("hello response" + response.toString());
		return "success";
	}
	
}

2、请求映射-路径区分-利用post方式

<form action="springmvc/testMethod" method="POST">
	<input type="submit" value="submit"/>
</form>
/**
	 * 常用: 使用 method 属性来指定请求方式
	 */
	@RequestMapping(value = "/testMethod", method = RequestMethod.POST)
	public String testMethod() {
		System.out.println("testMethod");
		return "success";
	}

3、请求映射-路径区分-利用请求参数和请求头

	<a href="springmvc/testParamsAndHeaders?username=atguigu&age=10">Test ParamsAndHeaders</a>
/**
	 * 了解: 可以使用 params 和 headers 来更加精确的映射请求. params 和 headers 支持简单的表达式.
	 * 
	 * @return
	 */
	@RequestMapping(value = "testParamsAndHeaders", params = { "username","age!=10" }, headers = { "Accept-Language=en-US,zh;q=0.8" })
	public String testParamsAndHeaders() {
		System.out.println("testParamsAndHeaders");
		return "success";
	}

4、请求映射-路径区分-利用ant风格

Ant 风格资源地址支持3种匹配符:
?:匹配文件名中的一个字符
*:匹配一层路径
**:** 匹配多层路径

测试: <a href=”springmvc/testAntPath/mnxyz/abc”>Test AntPath</a>

@RequestMapping("/testAntPath/*/abc")
	public String testAntPath() {
		System.out.println("testAntPath");
		return "success";
	}

二、请求映射-信息传递

1、请求映射-信息传递-利用路径变量

测试: <a href=”springmvc/testPathVariable/1″>Test PathVariable</a>

/**
	 * @PathVariable 可以来映射 URL 中的占位符到目标方法的参数中.
	 * @param id
	 * @return
	 */
	@RequestMapping("/testPathVariable/{id}")
	public String testPathVariable(@PathVariable("id") Integer id) {
		System.out.println("testPathVariable: " + id);
		return "success";
	}

2、请求映射-信息传递-REST风格

示例:
/order/1 HTTP  GET :得到id = 1的 order –
/order/1 HTTP DELETE:删除 id = 1 的 order –
/order/1 HTTP PUT:更新 id = 1的 order –
/order HTTP POST:新增 order –

HiddenHttpMethodFilter:浏览器 form 表单只支持 GET与POST请求,而DELETE、PUT 等method并不支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与DELETE 请求。

第一步:首先在web.xml的添加过滤器

<!-- 
	配置 org.springframework.web.filter.HiddenHttpMethodFilter: 可以把 POST 请求转为 DELETE 或 POST 请求 
	-->
	<filter>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>
	
	<filter-mapping>
		<filter-name>HiddenHttpMethodFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

第二步:编写测试网页

<form action="springmvc/testRest/1" method="post">
		<input type="hidden" name="_method" value="PUT"/>
		<input type="submit" value="TestRest PUT"/>
	</form>
	<br><br>
	
	<form action="springmvc/testRest/1" method="post">
		<input type="hidden" name="_method" value="DELETE"/>
		<input type="submit" value="TestRest DELETE"/>
	</form>
	<br><br>
	
	<form action="springmvc/testRest" method="post">
		<input type="submit" value="TestRest POST"/>
	</form>
	<br><br>
	
	<a href="springmvc/testRest/1">Test Rest Get</a>
	<br><br>

3、请求映射-信息传递-利用请求参数

	/**
	 * @RequestParam 来映射请求参数. value 值即请求参数的参数名 required 该参数是否必须. 默认为 true
	 *               defaultValue 请求参数的默认值
	 */
	@RequestMapping(value = "/testRequestParam")
	public String testRequestParam(
			@RequestParam(value = "username") String un,
			@RequestParam(value = "age", required = false, defaultValue = "0") int age) {
		System.out.println("testRequestParam, username: " + un + ", age: "
				+ age);
		return "success";
	}

测试: <a href=”springmvc/testRequestParam?username=atguigu&age=11″>Test RequestParam</a>

其实也可以用表单的post方法,来传递请求参数,因为控制器也能解析的。

第三步:编写控制器

/**
	 * Rest 风格的 URL. 以 CRUD 为例: 新增: /order POST 修改: /order/1 PUT update?id=1 获取:
	 * /order/1 GET get?id=1 删除: /order/1 DELETE delete?id=1
	 * 
	 * 如何发送 PUT 请求和 DELETE 请求呢 ? 1. 需要配置 HiddenHttpMethodFilter 2. 需要发送 POST 请求
	 * 3. 需要在发送 POST 请求时携带一个 name="_method" 的隐藏域, 值为 DELETE 或 PUT
	 * 
	 * 在 SpringMVC 的目标方法中如何得到 id 呢? 使用 @PathVariable 注解
	 * 
	 */
	@RequestMapping(value = "/testRest/{id}", method = RequestMethod.PUT)
	public String testRestPut(@PathVariable Integer id) {
		System.out.println("testRest Put: " + id);
		return "success";
	}

	@RequestMapping(value = "/testRest/{id}", method = RequestMethod.DELETE)
	public String testRestDelete(@PathVariable Integer id) {
		System.out.println("testRest Delete: " + id);
		return "success";
	}

	@RequestMapping(value = "/testRest", method = RequestMethod.POST)
	public String testRest() {
		System.out.println("testRest POST");
		return "success";
	}

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

4、请求映射-信息传递-利用请求头

测试: <a href=”springmvc/testRequestHeader”>Test RequestHeader</a>

/**
	 * 了解: 映射请求头信息 用法同 @RequestParam
	 */
	@RequestMapping("/testRequestHeader")
	public String testRequestHeader(
			@RequestHeader(value = "Accept-Language") String al) {
		System.out.println("testRequestHeader, Accept-Language: " + al);
		return "success";
	}

5、请求映射-信息传递-利用cookie值

测试:<a href=”springmvc/testCookieValue”>Test CookieValue</a>

/**
	 * 了解:
	 * 
	 * @CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
	 */
	@RequestMapping("/testCookieValue")
	public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
		System.out.println("testCookieValue: sessionId: " + sessionId);
		return "success";
	}

6、请求映射-信息传递-form表单的pojo

将  表单信息  转换成  bean  ,支持级联属性。如:dept.deptId、dept.address.tel 等

1、测试页:

<form action="springmvc/testPojo" method="post">
		username: <input type="text" name="username"/>
		<br>
		password: <input type="password" name="password"/>
		<br>
		email: <input type="text" name="email"/>
		<br>
		age: <input type="text" name="age"/>
		<br>
		city: <input type="text" name="address.city"/>
		<br>
		province: <input type="text" name="address.province"/>
		<br>
		<input type="submit" value="Submit"/>
	</form>

2、控制页

	/**
	 * Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配, 自动为该对象填充属性值。支持级联属性。
	 * 如:dept.deptId、dept.address.tel 等
	 */
	@RequestMapping("/testPojo")
	public String testPojo(User user) {
		System.out.println("testPojo: " + user);
		return "success";
	}

3、Bean [估计是调用默认构造函数,然后set属性,完成bean创建]

bean – user

public class User {
	
	private Integer id;
	
	private String username;
	private String password;

	private String email;
	private int age;
	
	private Address address;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

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

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Address getAddress() {
		return address;
	}

	public void setAddress(Address address) {
		this.address = address;
	}

	public User() {}
}

bean-address

public class Address {

	private String province;
	private String city;

	public String getProvince() {
		return province;
	}

	public void setProvince(String province) {
		this.province = province;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}
}

 

显示jsp页:

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

request user: ${requestScope.user }

核心摘要:在控制器中,自动生成的pojo的 User类型对象。会自动产生一个首字母小写的类型对象的关键字,并将关键字和其类型对象值,一同传入request中。

如这里:会将 “user” 作为关键字,user 对象作为value值,存放到request中。 

操作和 request.setAttribute("user", user);一样
如果控制器的pojo参数用了 @ModelAttribute 注解标记,那么标记值若是XXX,则效果就像 request.setAttribute("XXX", user);一样

//举例如下:
//控制器中 用 @ModelAttribute&nbsp;注解标记 的情况
public String testPojo(@ModelAttribute(value ="test") User user) {
	System.out.println("testPojo: " + user);
	return "success";
}
//在转发显示页 用 ${requestScope.test} 调用。当然别忘记添加 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

总之一旦有pojo对象生成,那显示jsp页就可以直接调用${requestScope.标记名 }


7、请求映射-信息传递-原始servletapi作为参数

测试:<a href=”springmvc/testServletAPI”>Test ServletAPI</a>

/**
	 * 可以使用 Serlvet 原生的 API 作为目标方法的参数 具体支持以下类型
	 * 
	 * HttpServletRequest 
	 * HttpServletResponse 
	 * HttpSession
	 * java.security.Principal 
	 * Locale InputStream 
	 * OutputStream 
	 * Reader 
	 * Writer
	 * @throws IOException 
	 */
	@RequestMapping("/testServletAPI")
	public void testServletAPI(HttpServletRequest request,
			HttpServletResponse response, Writer out) throws IOException {
		System.out.println("testServletAPI, " + request + ", " + response);
		out.write("hello springmvc");
//		return "success";
	}

Handler的函数参数可以添加上面的这些,其实本质上来说:函数参数只有 request和response。其他的参数都是这些方法里面包括的。比如writer就是response.getWriter()


springmvc踩坑-配置视图解析前后缀内容重复

配置文件里面的 配置内容重复了。

当时在springmvc中,配置转发器时,发现视图解析器 居然有两个。一个添加了前缀和后缀,一个没有添加。

然后我在handler【也叫controller】中,    —》{这个是非注解配置的handler}

@Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception{ 
    
    //新建ModelAndView
    ModelAndView modelAndView =  new ModelAndView();
    //相当 于request的setAttribut,在jsp页面中通过itemsList取数据
    modelAndView.addObject("itemsList", ""); 
   
 
    //指定视图 (本来这里就是 最终返回地址了,但是视图解析器有前缀和后缀 干扰了;正确做是删除视图解析器的前缀和后缀)
    modelAndView.setViewName("/WEB-INF/views/success.jsp");
    
    //或者 正确的做法 这里返回success;在视图解析器添加 前缀和后缀
    //modelAndView.setViewName("success");

    return modelAndView;

}

发现,返回的路径一直是:/WEB-INF/views/WEB-INF/views/success.jsp.jsp
所以一直报错了。。。

服务器开发-概述

想做一个简单的服务器,用来更好的了解web交互模式。


http1.0 简单服务器 :三个文件

package com.httpserver;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

import javax.swing.JTextArea;
import javax.swing.JTextField;


public class HttpResponse extends Thread {
	Socket socket;
	JTextArea ta;
	PrintStream pout;
	boolean isHttp1 = false;
	String path = null;
	HttpResponse(Socket socket, JTextArea ta,JTextField path)
	{
		this.socket = socket;
		this.ta = ta;
		this.path = path.getText();
	}
	
	public void run()
	{
		try {
		
			BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pout = new PrintStream(socket.getOutputStream());
			String requestLine = br.readLine();
			//String requestCmds = ""+requestLine;
			if(requestLine==null){
				error(400,"Empty Request");
			}
			else{
				ta.append("Http请求,来自:["+socket.getInetAddress()+":"+socket.getPort()+"]   "+requestLine+"\n");
			}
			if(requestLine.toLowerCase().indexOf("http/1.")!=-1){
				isHttp1 = true;
			}
			String[] request = requestLine.split(" ");
			if(request.length<2)
			{
				error(400,"Bad Request");
			}
			String str1 = request[0];
			if(str1.equals("GET")){
				serveFile(request[1]);
			}
			else{
				error(400, "Bad Request");
				ta.append("Bad Request");
			}
			
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			return;
		}
		
	}
	
	
	private void error(int erorcd, String erortx) {
		erortx = "<html><h1>" + erortx + "</h1></html>";
		if (isHttp1) {
			pout.println("HTTP/1.0 " + erorcd + " " + erortx);
			pout.println("Content-type: text/html");
			pout.println("Content-length: " + erortx.length() + "\n");
		}
		pout.println(erortx);
	}
	
	private void serveFile(String requestPath)  {
		if (requestPath.equals("/")){
			/**
			 * 取首页文件,首页文件可以为index.html或index.htm
			 */
			requestPath = "/index.html";
			if(path==null){
				path=new File("").getAbsolutePath();
			}
			if(!new File(path+requestPath).exists()){
				requestPath="/index.htm";
			}			
		}				
		try {
			sendFileData(requestPath);
			ta.append("文件传输成功 !       "+requestPath+"\n");
		} catch (Exception e) {
			error(404, "");
			ta.append("请求文件不存在\n");
		}		
	}
	
	private void sendFileData(String requestPath) throws IOException,FileNotFoundException {
		InputStream inputstream = new FileInputStream(path+requestPath);
		if (inputstream == null)
			throw new FileNotFoundException(requestPath);		
		if (isHttp1) {
			pout.println("HTTP/1.0 200 Document follows");
			pout.println("Content-length: " + inputstream.available());
			
			if (requestPath.endsWith(".gif"))
				pout.println("Content-type: image/gif");
			else if (requestPath.endsWith(".jpg"))
				pout.println("Content-type: image/jpeg");
			else if (requestPath.endsWith(".png") || requestPath.endsWith(".htm"))
				pout.println("Content-Type: text/png");
			
			else if (requestPath.endsWith(".css") || requestPath.endsWith(".htm"))
				pout.println("Content-Type: text/css");
			else if (requestPath.endsWith(".js") || requestPath.endsWith(".htm"))
				pout.println("Content-Type: text/javascript");

			else if (requestPath.endsWith(".html") || requestPath.endsWith(".htm"))
				pout.println("Content-Type: text/html");

			else
				pout.println("Content-Type: application/octet-stream");
			
			pout.println("Connection: Keep-Alive");//println 表示写完自带换行
			/*****下面这个换行回车,必须要有 这个是报头 格式******/
			pout.println();
			/***** 但是这个 只需要一个换行回车就行了 **/
			//pout.println();

           
			
		}
		/*缓冲区设为8K*/
		byte[] is = new byte[8*1024];
		/*实际测试 缓冲区 太小了,请求一张 大于 8K 的图片就 无法传输了,好像不是这个问题*/
		//byte[] is = new byte[300*1024];

		
		int length=0;
		while((length=inputstream.read(is))!=-1){
			pout.write(is, 0, length);
		}
		inputstream.close();
		pout.flush();
	}

}
package com.httpserver;

import java.net.*;

import javax.swing.*;

public final class ServerThread extends Thread {
	int port;
	JTextArea display;
	JTextField status;
	JTextField direc;
	//JTextField ipAdd;
	ServerSocket listener = null;
	ServerThread (int port, JTextArea display, JTextField status,JTextField direc,JTextField ipAdd) throws Exception
	{
		this.port = port;
		this.display = display;
		this.status = status;
		this.direc = direc;
		byte[] ip = new byte[4];
		String ipp = ipAdd.getText();
		//System.out.println(ipp);
		String[] ip1 = ipp.split("\\.");
		for(int i = 0;i<ip1.length;i++){
			try{
				ip[i] = (byte) Integer.parseInt(ip1[i]);
			}
			catch(Exception e){
				ip[i] = -1;
			}
			//System.out.println(ip1[i]);
		}
		//System.out.println(ip[0]+" "+ip[1]+" "+ip[2]+" "+ip[3]);
		//this.ipAdd = ipAdd;
		try{
			listener = new ServerSocket(port,20, InetAddress.getByAddress(ip));
		}
		catch(Exception e){
			
			listener = new ServerSocket(port,20);
			display.append("无效的IP地址,已使用默认地址:"+InetAddress.getLocalHost().getHostAddress()+"\n");
			ipAdd.setText(InetAddress.getLocalHost().getHostAddress());
		}
		display.append("服务器已开启,端口:"+port+"\n");
		status.setText("服务器已开启。 "+ipAdd.getText()+":"+port+"  "+direc.getText());
	}
	
	public void run()
	{
		
		
		while(true)
		{
			try{
				Socket socket = listener.accept();
				/*for(String s = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine();s!="\n";
						s = new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine()){
					display.append(new BufferedReader(new InputStreamReader(socket.getInputStream())).readLine());
				}
				*/
				/*BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				for(String s = br.readLine();s!=null;s = br.readLine()){
					display.append(s+"\n");
				}*/
				new HttpResponse(socket, display, direc).start();
			}
			catch(Exception e){
				display.append(e+"\n");
			}
		}
	}
	
}
package com.httpserver;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.synth.SynthStyle;

public class Surface implements ActionListener {

	JFrame jf = new JFrame("Web Server");
	JLabel iP = new JLabel("IP: ");
	JLabel port = new JLabel("Port: ");
	JTextField ipAdd = new JTextField(15);
	JTextField portNum = new JTextField(4);
	JButton start = new JButton("Start");
	JButton over = new JButton("Stop");
	JLabel direct = new JLabel("Main Directory: ");
	JTextField directory = new JTextField(24);
	JButton br = new JButton("...");
	// JLabel diary = new JLabel("Diary:");
	JTextArea ta = new JTextArea(10, 40);
	// JTextField outDirct = new JTextField(15);
	// JTextField outIp = new JTextField(8);
	JTextField outStatus = new JTextField(40);
	// JLabel status = new JLabel("Status:");
	ServerThread listener = null;
	boolean hasStarted = false;
	String ipp;

	public void init() {
		JPanel jp1 = new JPanel();
		jp1.add(iP);
		jp1.add(ipAdd);
		jp1.add(port);
		jp1.add(portNum);
		jp1.add(start);
		jp1.add(over);
		JPanel jp2 = new JPanel();
		jp2.add(direct);
		jp2.add(directory);
		jp2.add(br);
		// JPanel jp3 = new JPanel();
		// jp3.add(diary);
		// JScrollPane jsp = new JScrollPane();
		// jsp.add(ta);
		Border bb = BorderFactory.createEtchedBorder();
		Border tb1 = BorderFactory.createTitledBorder(bb, "Console");
		Border tb2 = BorderFactory.createTitledBorder(bb, "Diary");
		JScrollPane jsp = new JScrollPane(ta);
		JPanel jp4 = new JPanel();
		jp4.add(jsp);
		jp4.setBorder(tb2);
		ta.setLineWrap(true);
		ta.setEditable(false);
		start.addActionListener(this);
		over.addActionListener(this);
		br.addActionListener(this);
		Box jb1 = Box.createVerticalBox();
		jb1.add(jp1);
		jb1.add(jp2);
		jb1.setBorder(tb1);
		directory.setText("/Users/hello/Sites/testwebsites");
		// Box jb2 = Box.createVerticalBox();
		// jb2.add(jp3);
		// jb2.add(jp4);
		Box jb = Box.createVerticalBox();
		// jb.setBorder(bb);
		outStatus.setBackground(jf.getBackground());
		outStatus.setBorder(null);
		outStatus.setEditable(false);
		JPanel jp5 = new JPanel();
		jp5.setBorder(bb);
		// jp5.add(status);
		jp5.add(outStatus);
		outStatus.setText("服务器已停止");
		try {
			ipAdd.setText(InetAddress.getLocalHost().getHostAddress());
		} catch (Exception e) {
			ipAdd.setText("0.0.0.0");
		}
		jb.add(jb1);
		jb.add(jp4);
		jb.add(jp5);
		// jb.add(jp3);
		// jb.add(jp4);
		jf.add(jb);
		// jf.add(jp1);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setResizable(false);
		jf.pack();
		jf.setVisible(true);

	}

	public void startServer() {
		start.setEnabled(false);
		over.setEnabled(true);
		int port = 8080;
		try {
			port = Integer.parseInt(portNum.getText());
		} catch (Exception e) {
			ta.append("端口号异常,已使用8080端口。\n");
			portNum.setText("8080");
		}
		if (hasStarted && port == listener.port) {
			listener.resume();
			ta.append("服务器已开启,端口:" + listener.port + "\n");
			outStatus.setText("服务器已开启。" + ipp + ":" + port + "  " + directory.getText());
			// ipAdd.setText(InetAddress.getLocalHost().getHostAddress());
			return;
		}

		try {
			listener = new ServerThread(port, ta, outStatus, directory, ipAdd);
			ipp = ipAdd.getText();
		} catch (Exception e) {
			ta.append("端口已被其他程序占用,请重试。\n");
			listener = null;
			hasStarted = false;
		}
		if (listener == null) {
			start.setEnabled(true);
		} else {
			hasStarted = true;
			listener.start();
		}
	}

	public void exitServer() {
		ta.append("服务器关闭。\n");
		if (listener != null) {
			listener.stop();
		}
		System.exit(0);
	}

	public void stopServer() {
		start.setEnabled(true);
		over.setEnabled(false);
		listener.suspend();
		ta.append("服务器已停止。\n");
		outStatus.setText("服务器已停止。");
	}

	public void selectPath() {
		String str = "";
		String loc = "";
		JFileChooser chooser = new JFileChooser();
		chooser.setCurrentDirectory(new java.io.File("."));
		chooser.setDialogTitle("主目录");
		chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		chooser.setAcceptAllFileFilterUsed(false);
		if (chooser.showOpenDialog(jf) == JFileChooser.APPROVE_OPTION) {
			// str += chooser.getCurrentDirectory();
			str += chooser.getSelectedFile();
			loc = str.replaceAll("\\\\", "/"); //// Windows路径到JAVA路径的转换
		}
		directory.setText(loc);
	}

	public void actionPerformed(ActionEvent event) {
		String command = event.getActionCommand();
		if (command.equals("Start")) {
			startServer();
		}
		if (command.equals("Stop")) {
			stopServer();
		}
		if (command.equals("Exit")) {
			exitServer();
		}
		if (command.equals("...")) {
			selectPath();
		}
	}

	public static void main(String[] args) {
		Surface sf = new Surface();
		sf.init();
		// sf.outStatus.setText("服务器已开启:220.111.332.444:8000 sdafsagassasa");

		 String a = new String("源码下载站");
		 System.out.println("16制"+strTo16(a));

		char c = '8';
		System.out.printf("The value of char %c  %X,is %d.%n", c, (int)c,(int)c);
        //16进制 转成10进制。  java 内部编码是 utf-16 
		
		String str = String.valueOf(c);
		byte[] bys;
		try {
			bys = a.getBytes("UTF-8");
			for (int i = 0; i < bys.length; i++) {
				System.out.printf("%X ", bys[i]);
			}
            
			//因为是Unicode编码,所以编码前有字节序。
			//(byte & 0xFF) 这是一个字节相与,然后 << 8  代表数据 左移 8位 ,相当于 两字节大小 与后面的 或运算
			// 相当于 地位的字节 复制了 新添加的 字节
			//int unicode = (bys[2] & 0xFF) << 8 | (bys[3]& 0xFF);
			//System.out.printf("The unicode value of %c is %d.%n", c, unicode);

		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public static String strTo16(String s) {
		String str = "";

		System.out.println("字符串长度"+s.length());
		for (int i = 0; i < s.length(); i++) {
			int ch = (int) s.charAt(i);
			String s4 = Integer.toHexString(ch);
			str = str + s4;
		}
		return str;
	}

}

参考自:GitHub

另,Tomcat源码剖析电子书:

代码和UML图:https://github.com/Aresyi/HowTomcatWorks 
排版更好的百度电子书:https://yuedu.baidu.com/ebook/ac92f0d35122aaea998fcc22bcd126fff7055d60

网络编程资料:

自定义 服务器 css 设置成了 二进制类型 导致加载 不出来 样式,报文头和体 有且只有空一行 才能 被 浏览器 正确 解析。空两行 浏览器 加载不了 图片

http://localhost/~cool/beike/index.html

http://www.ietf.org/rfc/rfc1945.txt

 

http编码

https://blog.csdn.net/hongxingxiaonan/article/details/49963643

tcp连接详解:

https://blog.csdn.net/liuxinmingcode/article/details/50376035

tcp包长度

https://blog.csdn.net/lishanmin11/article/details/77045745

http 长度

https://blog.csdn.net/zerooffdate/article/details/78962818

http详解

https://segmentfault.com/a/1190000006689767#articleHeader8

https://blog.csdn.net/u012813201/article/details/70211255

websocket

https://www.jianshu.com/p/f666da1b1835

http://www.cnblogs.com/hustskyking/p/websocket-with-node.html

https://www.cnblogs.com/oshyn/p/3574497.html

浏览器开发-概述

想做个很简单浏览器,这样做可以,更加方便的web交互基础。不过很麻烦。这里留个档案吧。


浏览器内涵

 所谓的“浏览器内核”无非指的是一个浏览器最核心的部分——“Rendering Engine”,直译这个词汇叫做“渲染引擎”,不过我们也常称其为“排版引擎”、“解释引擎”。这个引擎的作用是帮助浏览器来渲染网页的内容,将页面内容和排版代码转换为用户所见的视图。

注:有时候我们所说的“浏览器内核”甚至“渲染引擎”,其实除了渲染引擎,也悄悄包含了javascript引擎,如WebKit,它由渲染引擎WebCore和javascript引擎JSCore组成。

常见的浏览器内核(或者说渲染引擎)有很多个,如Trident、Gecko、WebKit等等,不同的内核对网页编写语法的解释也有不同,进而导致同一个页面在不同内核的浏览器下显示出来的效果也会有所出入,这也是前端工程师需要让作品兼容各种浏览器的原因。

我们常常喜欢把浏览器内核与某浏览器名称直接挂钩起来,如IE内核、Chrome内核,其实是不全面的说法。比如Opera在7.0版本到12.16版本中采用的是独立研发的Presto引擎,但在后续跟随了Chrome的脚步加入了WebKit大本营,放弃了Presto;另外即使名称相同,但版本不同的引擎也可能存在较大差别。比如IE6使用的是Trident早期版本,存在许多bug,性能也较低。而最新的IE11所使用的Trident7.0版本已经可以支持WebGL(3D绘图标准)以及HTML5大部分标准。

下面按照各个主流浏览器,介绍下它们所使用的浏览器内核的历程。

Internet Explorer:

IE开发计划开始于1994年夏天,微软为抵抗当时主流的网景Netscape Navigator,要在Windows中开发适合自己的浏览器,但微软并没有时间从零开始。因此和Spyglass合作,于是IE从早期一款商业性的专利网页浏览器Spyglass Mosaic派生出来,虽然Spyglass Mosaic与NCSA Mosaic(首款应用得最广泛的网页浏览器)甚为相似,但Spyglass Mosaic则相对地较不出名并使用了NCSA Mosaic少量的源代码。

1996年,微软通过给予季度费用和部分收入从Spyglass中取得了Spyglass Mosaic的源代码和授权。从而使IE逐渐成为微软专属软件。它采用的排版引擎(俗称内核)为Trident。每一次新的IE版本发布,也标志着Trident内核版本号的提升。

下面是各Trident版本信息:

冷知识:除Trident之外,微软还有另一个网页浏览器排版引擎,称为Tasman,它是使用在「Internet Explorer for Mac」的排版引擎。相较于Trident,Tasman引擎对网页标准有较佳的支持,但微软自04年开始已经停止了Mac计算机版本的 Internet Explorer的开发。

Safari

        Safari是苹果公司开发的浏览器,使用了KDE(Linux桌面系统)的KHTML作为浏览器的运算核心,Safari所用浏览器内核的名称是大名鼎鼎的WebKit。 Safari在2003年1月7日首度发行测试版,并成为Mac OS X v10.3与之后版本的默认浏览器,也成为苹果其它系列产品的指定浏览器(也已支持Windows平台)。

        如上述可知,WebKit前身是KDE小组的KHTML引擎,可以说WebKit是KHTML的一个开源的分支。当年苹果在比较了Gecko和KHTML后,选择了后者来做引擎开发,是因为KHTML拥有清晰的源码结构和极快的渲染速度。

        需要了解的是,虽然我们称WebKit为浏览器内核(或浏览器引擎),但不太适合直接称之为我们开头提到的Rendering Engine(渲染引擎),因为WebKit本身主要是由两个引擎构成的,一个正是渲染引擎“WebCore”,另一个则是javascript解释引擎“JSCore”,它们均是从KDE的渲染引擎KHTML及javascript解释引擎KJS衍生而来。

        在2010年4月,苹果公司宣布了其浏览器引擎Webkit的最新项目 Webkit2。Webkit2的目标是实现独立进程与非阻断式API。

        WebKit可以说是苹果公司给开源世界的一大贡献,基于此开源引擎,衍生了多个WebKit分支,如下面要介绍的Chrome的浏览器引擎。

Chrome / Chromium

        谷歌Chrome/Chromium浏览器从08年创始至今一直使用苹果公司的WebKit作为浏览器内核原型,是WebKit的一个分支,我们可以称之为Chromium引擎(注意我们这里说的是Chromium引擎,而不是Chromium浏览器)。

        这里顺便介绍下Chrome和Chromium两个浏览器的区别——Chromium浏览器是谷歌为发展自家的浏览器Chrome而开启的计划,所以Chromium相当于Chrome的工程版或称实验版(尽管Chrome自身也有β版阶段),新功能会率先在Chromium上实现,待验证后才会应用在Chrome上。Chromium一天最多可以更新十几二十个版本,实验性的新特性都会现在这里放出,但是Chromium本身其实并不稳定;而Chrome总共有四个更新分支:Canary、Dev、Beta、Stable,稳定性依次增强。

        我们说回引擎。Chromium引擎虽然是属于WebKit的分支,却把WebKit的代码梳理得可读性提高很多,所以以前可能需要一天进行编译的代码,现在只要两个小时就能搞定。因此Chromium引擎和其它基于WebKit的引擎所渲染页面的效果也是有出入的。基于以上原因,有的地方会把Chromium引擎跟WebKit区分开来,有的地方则直接把Chromium引擎归为WebKit(比如维基百科),其实都有其道理。

        然而在13年发布的Chrome 28.0.1469.0版本开始,Chrome放弃Chromium引擎转而使用最新的Blink引擎(基于WebKit2——苹果公司于2010年推出的新的WebKit引擎),Blink对比上一代的引擎精简了代码、改善了DOM框架,也提升了安全性。

Opera

        Opera浏览器,是一款挪威Opera Software ASA公司制作的支持多页面标签式浏览的网络浏览器。是跨平台浏览器可以在Windows、Mac和Linux三个操作系统平台上运行。Opera浏览器创始于1995年4月,到2014年3月4日,官方发布的个人电脑用的最新版本为Opera20。

        Opera的一个里程碑作品是Opera7.0,因为它使用了Opera Software自主开发的Presto渲染引擎,取代了旧版Opera 4至6版本使用的Elektra排版引擎。

        Presto加入了动态功能,例如网页或其部分可随着DOM及Script语法的事件而重新排版。Presto在推出后不断有更新版本推出,使不少错误得以修正,以及阅读Javascript效能得以最佳化,并成为当时速度最快的引擎。

        然而为了减少研发成本,Opera在2013年2月宣布放弃Presto,转而跟随Chrome使用WebKit分支的Chromium引擎作为自家浏览器核心引擎。

        在Chrome与2013年推出Blink引擎(也是基于WebKit的分支)之后,Opera也紧跟其脚步表示将转而使用Blink作为浏览器核心引擎。

Firefox

        Mozilla Firefox是一个开源网页浏览器,原名是Firebird,2004年2月9日,Mozilla Firebird决定改称Mozilla Firefox。Firefox浏览器使用的是Gecko内核,其发展历程如下:

        1997年,网景收购了DigitalStyle。当时,网景浏览器在各方面的表现已经比不上她的主要竞争对手Internet Explorer。网景开始研发下一代的排版引擎,并期望把新的排版引擎应用于下一版本的网景浏览器上。

        1998年初,Mozilla计划开始执行。这个新的排版引擎名为Raptor,以开发源码的方式发放于互联网上。后来,因为商标问题,Raptor改名为NGLayout(即next generation layout之意)。而最后NGLayout就被网景重新命名为Gecko。

        2003年7月15日时代华纳解散了网景公司,大部分开发者被解雇。Mozilla基金会亦在当天成立,继续推动着Gecko的发展。时至今天,Gecko仍继续由Mozilla的雇员和义工所维护和发展。

        最后还是再谈谈javascript引擎(后面统称JS引擎)这东西。我们上述的渲染引擎主要是负责HTML、CSS以及其他一些东西的渲染,而JS引擎则主要负责对javascript的渲染,一个JS引擎的好坏决定了一个浏览器对脚本的加载和执行速度,也影响了其跑分。

        下方列出各种主流浏览器各自的JS引擎,了解下即可:

Firefox:

SpiderMonkey:第一款JavaScript引擎,由Brendan Eich在Netscape Communications时编写,用于Mozilla Firefox 1.0~3.0版本。

Rhino:由Mozilla基金会管理,开放源代码,完全以Java编写。

TraceMonkey:基于实时编译的引擎,其中部份代码取自Tamarin引擎,用于Mozilla Firefox 3.5~3.6版本。

JaegerMonkey:德文Jäger原意为猎人,结合追踪和组合码技术大幅提高性能,部分技术借凿了V8、JavaScriptCore、WebKit:用于Mozilla Firefox 4.0以上版本。

IonMonkey:可以对JavaScript编译后的结果进行优化,用于Mozilla Firefox 18.0以上版本。

OdinMonkey:可以对asm.js进行优化,用于Mozilla Firefox 22.0以上版本。

Chrome:

V8:开源,由Google丹麦开发,是Google Chrome的一部分。

注:我们上面提到Chrome是基于WebKit的分支,而WebKit又由渲染引擎“WebCore”和JS解释引擎“JSCore”组成,可能会让你搞不清V8和JSCore的关系。你可以这样理解——WebKit是一块主板,JSCore是一块可拆卸的内存条,谷歌实际上认为Webkit中的JSCore不够好,才自己搞了一个V8 JS引擎,这就是Chrome比Safari在某些JS测试中效率更高的原因。

IE:

Chakra:中文译名为查克拉,用于Internet Explorer 9的32位版本及IE10+。

Opera:

Linear A:用于Opera 4.0~6.1版本。

Linear B:用于Opera 7.0~9.2版本。

Futhark:用于Opera 9.5~10.2版本。

Carakan:由Opera软件公司编写,自Opera10.50版本开始使用。

其它:

KJS:KDE的ECMAScript/JavaScript引擎,最初由Harri Porten开发,用于KDE项目的Konqueror网页浏览器中。

Narcissus:开放源代码,由Brendan Eich编写(他也参与编写了第一个SpiderMonkey)。

Tamarin:由Adobe Labs编写,Flash Player 9所使用的引擎。

Nitro(原名SquirrelFish):为Safari 4编写。