1 1.文件上传下载
1.1 文件上传
1.1.1 文件上传的作用
例如网络硬盘!就是用来上传下载文件的。
在智联招聘上填写一个完整的简历还需要上传照片呢。
1.1.2 文件上传对页面的要求
1.必须使用表单,而不能是超链接;
2.表单的method必须是POST,而不能是GET;
3.表单的enctype必须是multipart/form-data;
4.在表单中添加file表单字段,即<input type=”file”…/>
如下:
<form method="post"action="/fileupload" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" name="上传">
</form>
1.1.3 文件上传对Servlet的要求
当提交的表单是文件上传表单时,那么对Servlet也是有要求的。首先我们要肯定一点,文件上传表单的数据也是被封装到request对象中的。request.getParameter(String)方法获取指定的表单字段字符内容,但文件上传表单已经不再是字符内容,而是字节内容,所以失效。这时可以使用request的getInputStream()方法获取ServletInputStream对象,它是InputStream的子类,这个ServletInputStream对象对应整个表单的正文部分(从第一个分隔线开始,到最后),这说明我们需要的解析流中的数据。当然解析它是很麻烦的一件事情,而Apache已经帮我们提供了解析它的工具:commons-fileupload。
1.2 commons-fileupload
1.2.1 fileupload概述
fileupload是由apache的commons组件提供的上传组件。它最主要的工作就是帮我们解析request.getInputStream()。
fileupload组件需要的JAR包有:
commons-fileupload.jar,核心包;
commons-io.jar,依赖包。
1.2.2 fileupload简单应用
fileupload的核心类有:DiskFileItemFactory、ServletFileUpload、FileItem。
使用fileupload组件的步骤如下:
* 创建工厂类DiskFileItemFactory对象:DiskFileItemFactoryfactory = new DiskFileItemFactory()
* 使用工厂创建解析器对象:ServletFileUpload fileUpload = new ServletFileUpload(factory)
* 使用解析器来解析request对象:List<FileItem> list =fileUpload.parseRequest(request)
FileItem类,它才是我们最终要的结果。一个FileItem对象对应一个表单项(表单字段)。一个表单中存在文件字段和普通字段,可以使用FileItem类的isFormField()方法来判断表单字段是否为普通字段,如果不是普通字段,那么就是文件字段了。
String getName():获取文件字段的文件名称;
String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
String getFieldName():获取字段名称,例如:<input type=”text”name=”username”/>,返回的是username;
String getContentType():获取上传的文件的类型,例如:text/plain。
int getSize():获取上传文件的大小;
boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
InputStream getInputStream():获取上传文件对应的输入流;
voidwrite(File):把上传的文件保存到指定文件中。
1.2.3 简单上传示例
1.引入jar包
2.编写上传表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
${
message}
<form method="post" action="/fileupload" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" name="上传">
</form>
</body>
</html>
3.编写Servlet
public class FileUploadServlet extends HttpServlet {
private static final String UPLOADFILE = "file";
//设置上文文件的最大大小
private static final int MAX_FILE_SIZE = 1024 * 1024 * 40;
//设置内存的临界值,超过后将产生临时文件存储于临时目录中
private static final int MAX_THRESHOLD = 1024 * 1024 * 20;
private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//首先检测是否为多媒体上传
if (!ServletFileUpload.isMultipartContent(req)) {
resp.setCharacterEncoding("UTF-8");
PrintWriterout = resp.getWriter();
out.write("Error: 表单必须包含 enctype=multipart/form-data");
out.flush();
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置内存临界值,超过后将产生临时文件存储于临时目录之中
factory.setSizeThreshold(MAX_THRESHOLD);
//设置临时文件夹
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
ServletFileUpload upload = new ServletFileUpload(factory);
//设置最大文件上传值
upload.setFileSizeMax(MAX_FILE_SIZE);
//设置最大请求值(包含文件和表单数据)
upload.setSizeMax(MAX_REQUEST_SIZE);
//处理中文
upload.setHeaderEncoding("UTF-8");
//文件上传后存储的位置
String uploadPath = req.getServletContext().getRealPath("/") + File.separator + UPLOADFILE;
File file = new File(uploadPath);
if (!file.exists()) {
file.mkdirs();
}
try {
List<FileItem> fileItems = upload.parseRequest(req);
for (FileItem fileItem : fileItems) {
if (!fileItem.isFormField()) {
String fileName = new File(fileItem.getName()).getName();
String filePath = uploadPath + File.separator + fileName;
File storeFile = new File(filePath);
System.out.println(filePath);
fileItem.write(storeFile);
req.setAttribute("message", "文件上传成功");
}
}
} catch (Exception e) {
req.setAttribute("message", "文件上传失败");
}
req.getRequestDispatcher("/").forward(req, resp);
}
}
Servlet注册:
<servlet>
<servlet-name>fileUpload</servlet-name>
<servlet-class>org.sang.FileUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>fileUpload</servlet-name>
<url-pattern>/fileupload</url-pattern>
</servlet-mapping>
1.3 文件上传细节
1.3.1 把上传的文件放到WEB-INF目录下
如果没有把用户上传的文件存放到WEB-INF目录下,那么用户就可以通过浏览器直接访问上传的文件,这是非常危险的。
假如说用户上传了一个a.jsp文件,然后用户在通过浏览器去访问这个a.jsp文件,那么就会执行a.jsp中的内容,如果在a.jsp中有如下语句:Runtime.getRuntime().exec(“shutdown–s –t 1”);
通常我们会在WEB-INF目录下创建一个uploads目录来存放上传的文件,而在Servlet中找到这个目录需要使用ServletContext的getRealPath(String)方法,例如在我的upload1项目中有如下语句:
ServletContext servletContext =this.getServletContext();
String savepath =servletContext.getRealPath(“/WEB-INF/uploads”);
如下:
1.3.2 中文乱码问题
当上传的谁的名称中包含中文时,需要设置编码,commons-fileupload组件为我们提供了两种设置编码的方式:
request.setCharacterEncoding(String):这种方式是我们最为熟悉的方式了;
fileUpload.setHeaderEncdoing(String):这种方式的优先级高与前一种。
上传文件的文件内容包含中文:
通常我们不需关心上传文件的内容,因为我们会把上传文件保存到硬盘上!也就是说,文件原来是什么样子,到服务器这边还是什么样子!
但是如果你有这样的需求,非要在控制台显示上传的文件内容,那么你可以使用fileItem.getString(“utf-8”)来处理编码。
文本文件内容和普通表单项内容使用FileItem类的getString(“utf-8”)来处理编码。
如下:
1.3.3 上传文件同名问题
通常我们会把用户上传的文件保存到uploads目录下,但如果用户上传了同名文件呢?这会出现覆盖的现象。处理这一问题的手段是使用UUID生成唯一名称,然后再使用“_”连接文件上传的原始名称。
例如用户上传的文件是“我的一寸照片.jpg”,在通过处理后,文件名称为:“891b3881395f4175b969256a3f7b6e10_我的一寸照片.jpg”,这种手段不会使文件丢失扩展名,并且因为UUID的唯一性,上传的文件同名,但在服务器端是不会出现同名问题的。
如下:
1.3.4 一个目录不能存放过多文件
1.3.5 上传的单个文件的大小限制
限制上传文件的大小很简单,ServletFileUpload类的setFileSizeMax(long)就可以了。参数就是上传文件的上限字节数,例如servletFileUpload.setFileSizeMax(1024*10)表示上限为10KB。
一旦上传的文件超出了上限,那么就会抛出FileUploadBase.FileSizeLimitExceededException异常。我们可以在Servlet中获取这个异常,然后向页面输出“上传的文件超出限制”。
如下:
抛出异常时做如下判断:
1.3.6 上传文件的总大小限制
有时我们需要限制一个请求的大小。也就是说这个请求的最大字节数(所有表单项之和)!实现这一功能也很简单,只需要调用ServletFileUpload类的setSizeMax(long)方法即可。
例如fileUpload.setSizeMax(1024 * 10);,显示整个请求的上限为10KB。当请求大小超出10KB时,ServletFileUpload类的parseRequest()方法会抛出FileUploadBase.SizeLimitExceededException异常。
1.3.7 缓存大小与临时目录
//设置内存临界值,超过后将产生临时文件存储于临时目录之中
factory.setSizeThreshold(MAX_THRESHOLD);
//设置临时文件夹
factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
默认情况下系统不会一次将所有数据全部保存到内存中然后再写到数据库中去,而是内存中保存10KB,满了之后写入缓存中。所以可以设置内存临界值和缓存目录。
1.4 文件下载
1.4.1 通过Servlet下载1
下载页面:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<a href="/download?path=go1.7.4.windows-amd64.msi">go1.7.4.windows-amd64.msi</a>
<a href="/download?path=QQ8.7.exe">QQ8.7.exe</a>
<a href="/download?path=只有偏执狂才能生存.pdf">只有偏执狂才能生存.pdf</a>
</body>
</html>
下载Servlet:
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String path = req.getParameter("path");
File file = new File(this.getServletContext().getRealPath("/WEB-INF/downloads"), path);
if (!file.exists()) {
resp.setContentType("text/html;charset=utf-8");
}
IOUtils.copy(new FileInputStream(file), resp.getOutputStream());
}
}
上面代码有如下问题:
可以下载msi和exe文件,但在下载框中的文件名称是download;
不能下载pdf,而是在页面中显示它们。
1.4.2 通过Servlet下载2
通过添加content-disposition头来处理上面问题。当设置了content-disposition头后,浏览器就会弹出下载框。
而且还可以通过content-disposition头来指定下载文件的名称!
resp.addHeader("content-disposition", "attachment;filename=" + path);
1.4.3 通过Servlet下载3
上面的方式中文会乱码,解决方案如下:
String s = new String(path.getBytes("utf-8"), "iso-8859-1");
resp.addHeader("content-disposition", "attachment;filename=" + s);
2 2.jQuery
2.1 Day42
2.1.1 jQuery简介
什么是jQuery
其是对javascript封装的一个框架包,简化对javascript的操作。
javascript代码:获得页面节点对象、ajax元素节点对象实现、事件操作、事件对象
jquery代码:无需考虑浏览器兼容问题、代码足够少
特点
1.语法简练、语义易懂、学习快速、丰富文档。
2.jQuery 是一个轻量级的脚本,其代码非常小巧
3.jQuery 支持 CSS1~CSS3 定义的属性和选择器
4.jQuery 是跨浏览器的,它支持的浏览器包括 IE 6.0+、FF1.5+、Safari 2.0+和 Opera 9.0+。
5.能将 JavaScript 脚本与 HTML 源代码完全分离,便于后期编辑和维护。
6.插件丰富,除了 jQuery 自身带有的一些特效外,可以通过插件实现更多功能
2.1.2 jQuery选择器
为在页面上获得各种元素节点对象而使用的条件就是选择器。
document.getElementById()
document.getElementsByTagName();
document.getElementsByName();
基本选择器
1.$('#id属性值') ----------->document.getElementById()
获取计算机基础节点并输出:
$("#id")取出的是jquery对象,这是个集合对象,要想获得dom对象,可以用$("#id").get(i),其中i是jquery对象序列号,从0开始计算。
2.$('tag标签名称')----------->document.getElementsByTagName();
3.$('.class属性值') class属性值选择器
$(".class")取出的是jquery对象,这是个集合对象,要想获得dom对象,可以用$("#id").get(i),其中i是jquery对象序列号,从0开始计算。
4.$('*') 通配符选择器
递归遍历所有元素
5.$('s1,s2,s3')联合选择器
层次选择器
1.$(s1 s2) [父子]
派生选择器:在s1内部获得全部的s2节点(不考虑层次),如下:
红线部分表示该选择器可以获取到的部分
2.$(s1 > s2) [父子]
直接子元素选择器:在s1内部获得s2的子元素节点,如下:
红线部分表示该选择器可以获取到的部分
3.$(s1 + s2) [兄弟]
直接兄弟选择器:在s1后边获得紧紧挨着的第一个兄弟关系的s2节点
4.$(s1 ~ s2) [兄弟]
后续全部兄弟关系节点选择器:在s1后边获得全部兄弟关系的s2节点
过滤选择器
:first $("p:first") 第一个 <p> 元素
:last $("p:last") 最后一个 <p> 元素
:even $("tr:even") 所有偶数 <tr> 元素
:odd $("tr:odd") 所有奇数 <tr> 元素
:eq(index) $("ul li:eq(3)") 列表中的第四个元素(index 从 0 开始)
:gt(no) $("ul li:gt(3)") 列出 index 大于 3的元素
:lt(no) $("ul li:lt(3)") 列出 index 小于 3的元素
:not(selector) $("input:not(:empty)")所有不为空的 input 元素
:header $(":header") 所有标题元素 <h1> - <h6>
内容过滤选择器
1.:contains(内容)
包含内容选择器,获得节点内部必须通过标签包含指定的内容
$("div:contains(beijing)")
<div>linken love beijing</div>
<div>jack love shanghai</div>
2.:empty
获得空元素(内部没有任何元素/文本(空) )节点对象
$("div:empty")
<div>linken love beijing</div>
<div>jack love shanghai</div>
<div></div>
<div><img /></div>
<div> </div>
3.:has(选择器)
内部包含指定元素的选择器
$("div:has(#apple)")
<div>hello</div>
<div><p></p></div>
<div><spanid="apple"></span></div>
<div><spanclass="apple"></span></div>
4.:parent
寻找的节点必须作为父元素节点存在
$("div:parent")
<div>linken love beijing</div>
<div>jack love shanghai</div>
<div></div>
<div><img /></div>
<div> </div>
表单域选择器
2.1.3 jQuery属性操作
$().attr(属性名称); //获得属性信息值
$().attr(属性名称,值); //设置属性的信息
$().removeAttr(属性名称); //删除属性
$().attr(json对象); //同时为多个属性设置信息值,json对象的键值对就是名称和值
$().attr(属性名称,fn); //通过fn函数执行的return返回值对属性进行赋值
2.1.4 jQuery快捷操作
class属性值操作
$().addClass(值); //给class属性追加信息值
$().removeClass(值); //删除某一个class
$().toggleClass(值); //开关效果,有就删除,没有就添加
标签包含内容操作
$().html(); //获得节点包含的信息
$().html(信息); //设置节点包含的内容
$().text(); //获得节点包含的“文本字符串信息”内容
$().text(信息); //设置节点包含的内容(有html标签就把“><”符号变为符号实体)
html() 和 text()方法的区别:
1.获取内容
前者可以获取html标签 和 普通字符串内容
后者只获取普通字符串内容
2.设置内容
前者可以设置html标签 和 普通字符串内容
后者只设置普通字符串内容,如果内容里边有tag标签内容,就把其中的”<”“>”符号转变为符号实体 <转< >转> 空格转
以上两种操作(获取/设置)如果针对的操作内容是纯字符串内容,则使用效果一致。
css样式操作
css样式操作
$().css(name,value); //设置
$().css(name); //获取
$().css(json对象); //同时修改多个css样式
css()样式操作特点
1.样式获取,jquery可以获取 行内、内部、外部的样式。
dom方式只能获得行内样式
2.一些jquery版本不支持复合属性操作,所以需要把复合属性样式需要拆分为"具体样式"才可以操作。
例如: background 需要拆分为 background-color background-image 等进行操作
border: border-left-style border-left-width border-left-color 等
margin: margin-left margin-top 等
value属性快捷操作
$().attr('value');
$().attr('value',信息值);
快捷操作:
$().val(); //获得value属性值
$().val(信息值); //设置value属性的值
复选框操作
下拉列表操作
单选按钮操作
2.1.5 jQuery和DOM对象关系
jQuery和DOM不能互相调用对方的成员
jQuery对象和DOM对象的转化
jQuery->DOM
DOM->jQuery
2.2 Day43
2.2.1 jQuery加载事件
jQuery加载事件实现
jQuery加载事件与传统加载事件的区别
设置个数
在同一个请求里边,jquery的可以设置多个,而传统方式只能设置一个。传统方式加载事件是给onload事件属性赋值,多次赋值,后者会覆盖前者。
jquery方式加载事件是把每个加载事件都存入一个数组里边,成为数组的元素,执行的时候就遍历该数组执行每个元素即可,因此其可以设置多个加载事件。
执行时机
1.传统方式加载事件,是全部内容(文字、图片、样式)在浏览器显示完毕再给执行加载事件。
2.jquery方式加载事件,只要全部内容(文字、图片、样式)在内存里边对应的DOM树结构绘制完毕就给执行,有可能对应的内容在浏览器里边还没有显示。
2.2.2 jQuery普通事件操作
DOM事件处理回忆
jQuery事件处理
2.2.3 jQuery对文档的操作
节点追加
父子关系追加
案例:
兄弟关系追加
案例:
节点替换
节点删除
复制节点
2.2.4 jQuery属性选择器的使用
2.2.5 jQuery事件绑定
事件绑定
取消事件绑定
2.3 Day44
2.3.1 jQuery封装的ajax
AJAX概述
什么是AJAX
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。AJAX还有一个最大的特点就是,当服务器响应时,不用刷新整个浏览器页面,而是可以局部刷新。这一特点给用户的感受是在不知不觉中完成请求和响应过程。
l * 与服务器异步交互;
l * 浏览器页面局部刷新;
同步交互与异步交互
同步交互与异步交互
l 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
l 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
AJAX常见应用情景
当我们在百度中输入一个“传”字后,会马上出现一个下拉列表!列表中显示的是包含“传”字的10个关键字。
其实这里就使用了AJAX技术!当文件框发生了输入变化时,浏览器会使用AJAX技术向服务器发送一个请求,查询包含“传”字的前10个关键字,然后服务器会把查询到的结果响应给浏览器,最后浏览器把这10个关键字显示在下拉列表中。整个过程中页面没有刷新,只是刷新页面中的局部位置而已!当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
当输入用户名后,把光标移动到其他表单项上时,浏览器会使用AJAX技术向服务器发出请求,服务器会查询名为zhangSan的用户是否存在,最终服务器返回true表示名为zhangSan的用户已经存在了,浏览器在得到结果后显示“用户名已被注册!”。
l 整个过程中页面没有刷新,只是局部刷新了;
l 在请求发出后,浏览器不用等待服务器响应结果就可以进行其他操作;
AJAX优缺点
优点:
l AJAX使用Javascript技术向服务器发送异步请求;
l AJAX无须刷新整个页面;
l 因为服务器响应内容不再是整个页面,而是页面中的局部,所以AJAX性能高;
缺点:
l AJAX虽然提高了用户体验,但无形中向服务器发送的请求次数增多了,导致服务器压力增大;
l 因为AJAX是在浏览器中使用Javascript技术完成的,所以还需要处理浏览器兼容性问题;
AJAX技术
AJAX第一例
AJAX发送POST请求
2.3.2 省市县三级联动
1.创建数据库,构造数据源
http://download.csdn.net/detail/u012702547/9827293
2.C3P0提供数据连接池
所需jar包:
配置文件:
<?xml version="1.0"encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="user">root</property>
<property name="password">123</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///pca</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
</default-config>
</c3p0-config>
工具类:
public class DBUtils {
public static QueryRunner getQueryRunner() {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
return new QueryRunner(dataSource);
}
}
3.DAO实现查询操作
public class PCADao {
public QueryRunner queryRunner = DBUtils.getQueryRunner();
public List<AreaBean> getAreaBeenByParentId(Integer parentId) {
List<AreaBean> list = null;
try {
list = queryRunner.query("select * from base_area wherearea_parent_id=?", new BeanListHandler<AreaBean>(AreaBean.class), parentId);
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
}
4.Servlet提供查询接口
public class QueryServlet extends HttpServlet {
private PCADao pcaDao = new PCADao();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String parentId = req.getParameter("parentId");
List<AreaBean> list = pcaDao.getAreaBeenByParentId(Integer.parseInt(parentId));
Gson gson = new Gson();
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(gson.toJson(list));
out.flush();
out.close();
}
}
Servlet注册:
<servlet>
<servlet-name>queryservlet</servlet-name>
<servlet-class>org.sang.QueryServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>queryservlet</servlet-name>
<url-pattern>/query</url-pattern>
</servlet-mapping>
实体类如下:
5.编写html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>省市县三级联动</title>
<script src="js/jquery-3.1.1.min.js"></script>
</head>
<body>
省<select id="province"></select>
市<select id="city"></select>
区/县<select id="area"></select>
<script>
function initProvince(parentId){
$.ajax({
url: '/query',
data: {
parentId:parentId},
success: function (msg) {
var proStr = '';
for (var i = 0; i < msg.length; i++) {
var item = msg[i];
proStr += '<option value="' + item.area_id + '">' + item.area_name+ '</option>';
}
$("#province").html(proStr);
initCity($("#province:first").val());
}
});
}
function initCity(parentId) {
$.ajax({
url: '/query',
data: {
parentId:parentId},
success: function (msg) {
var proStr = '';
for (var i = 0; i < msg.length; i++) {
var item = msg[i];
proStr += '<option value="' + item.area_id + '">' + item.area_name+ '</option>';
}
$("#city").html(proStr);
initArea($("#city:first").val());
}
});
}
function initArea(parentId) {
$.ajax({
url: '/query',
data: {
parentId:parentId},
success: function (msg) {
var proStr = '';
for (var i = 0; i < msg.length; i++) {
var item = msg[i];
proStr += '<option value="' + item.area_id + '">' + item.area_name+ '</option>';
}
$("#area").html(proStr);
}
});
}
initProvince(1);
$("#province").change(function () {
initCity($("#province:first").val());
});
$("#city").change(function () {
initArea($("#city:first").val());
});
</script>
</body>
</html>
2.3.3 树形菜单案例
1.构造数据源
DROP TABLE IF EXISTS `product_category`;
CREATE TABLE `product_category` (
`CATEGORY_ID` int(11) NOT NULLDEFAULT '0',
`PARENT_CATEGORY_ID` int(11) DEFAULT'0',
`CATEGORY_NAME` varchar(255) COLLATEutf8_bin DEFAULT '',
`ACTIVE_STATUS` varchar(4) COLLATEutf8_bin DEFAULT 'Y',
PRIMARY KEY (`CATEGORY_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8COLLATE=utf8_bin;
/*Data for the table `product_category` */
insert into`product_category`(`CATEGORY_ID`,`PARENT_CATEGORY_ID`,`CATEGORY_NAME`,`ACTIVE_STATUS`)values (0,-1,'所有','Y'),(1,0,'图书','Y'),(2,0,'电子设备','Y'),(3,0,'家居建材','Y'),(4,1,'少儿图书','Y'),(5,1,'中学教辅','Y'),(6,1,'文艺书籍','Y'),(7,1,'历史书籍','Y'),(8,2,'手机','Y'),(9,2,'平板','Y'),(10,2,'电脑','Y'),(11,6,'先秦文学','Y'),(12,6,'两汉经学','Y'),(13,6,'魏晋文学','Y');
2.查询数据
public class TreeDao {
private QueryRunner queryRunner = DBUtils.getQueryRunner();
public List<TreeBean> getData(Integer parentId) {
try {
List<TreeBean> list = queryRunner.query("select * from product_category whereparent_category_id=?", new BeanListHandler<TreeBean>(TreeBean.class), parentId);
for (TreeBean treeBean : list) {
List<TreeBean> query = queryRunner.query("select * from product_category whereparent_category_id=?", new BeanListHandler<TreeBean>(TreeBean.class), treeBean.getParent_category_id());
if (query != null && query.size() > 0) {
treeBean.setCanExpand(true);
}
}
return list;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
实体类如下:
3.渲染HTML
public class RenderTreeView {
private StringBuffer result = new StringBuffer();
private TreeDao treeDao = new TreeDao();
public String render(List<TreeBean> treeBeans) {
for (TreeBean treeBean : treeBeans) {
result.append("<li id='" + treeBean.getCategory_id() + "' style=\"list-style-type: none\">");
if (treeBean.isCanExpand()) {
result.append("<img src='img/plus.gif'οnclick='itemClick(" + treeBean.getCategory_id() + ");'>");
} else {
result.append("<img src='img/blank.gif'>");
}
result.append("<img src='img/folder.gif'>");
result.append("<a>" + treeBean.getCategory_name() + "</a>");
if (treeBean.isCanExpand()) {
List<TreeBean> data = treeDao.getData(treeBean.getCategory_id());
result.append("<ulstyle='padding-left:20px;display:none'>");
render(data);
result.append("</ul>");
}
result.append("</li>");
}
return result.toString();
}
}
4.创建Servlet
public class TreeServlet extends HttpServlet {
TreeDao treeDao = new TreeDao();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String parentId = req.getParameter("parentId");
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
List<TreeBean> data = treeDao.getData(Integer.parseInt(parentId));
RenderTreeView renderTreeView = new RenderTreeView();
String render =renderTreeView.render(data);
out.write(render);
out.flush();
out.close();
}
}
Servlet注册:
<servlet>
<servlet-name>treeServlet</servlet-name>
<servlet-class>org.sang.TreeServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>treeServlet</servlet-name>
<url-pattern>/tree</url-pattern>
</servlet-mapping>
5.创建HTML
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>树形菜单</title>
<script src="js/jquery-3.1.1.min.js"></script>
</head>
<body>
<ul style="padding-left: 0px" id="rootFile">
</ul>
<script>
$.ajax({
url: '/tree?parentId=0',
success: function (msg) {
$("#rootFile").html(msg);
}
});
function itemClick(id) {
console.log(id);
var ele = $("#" + id);
if (ele.has("ul").length> 0) {
if (ele.children("ul").css("display") == "none") {
ele.children("ul").css("display", "block");
ele.children(":first").attr("src", "img/minus.gif");
} else {
ele.children("ul").css("display", "none");
ele.children(":first").attr("src", "img/plus.gif");
}
}
return false;
}
</script>
</body>
</html>
3 3.Spring
3.1 Day51
3.1.1 初识Spring
Java应用(从applets的小范围到全套n层服务端企业应用)是一种典型的依赖型应用,它就是由一些互相适当协作的对象构成的。因此,我们说这些对象间存在依赖关系。
Java语言和java平台在架构应用与建立应用方面,提供着丰富的功能。从非常基础的基本数据类型和Class(即定义新类)组成的程序块,到建立具有丰富的特性的应用服务器和web框架都有着很多的方法。一方面,可以通过抽象的显著特性让基础的程序块组成在一起成为一个连贯的整体。这样,构建一个应用(或者多个应用)的工作就可以交给架构师或者开发人员去做。因此,我们就可以清晰的知道哪些业务需要哪些Classes和对象组成,哪些设计模式可以应用在哪些业务上面。 例如:Factory、Abstract Factory、Builder、Decorator 和 Service Locator 这些模式(列举的只是少数)在软件开发行业被普遍认可和肯定(或许这就是为什么这些模式被定型的原因)。这固然是件好事,不过这些模式只是一个有名字的,有说明的,知道最好用在什么地方的,解决应用中什么问题的最佳实践而已。
Spring的IoC控件主要专注于如何利用classes、对象和服务去组成一个企业级应用,通过规范的方式,将各种不同的控件整合成一个完整的应用。Spring中使用了很多被实践证明的最佳实践和正规的设计模式,并且进行了编码实现。如果你是一个,构架师或者开发人员完全可以取出它们集成到你自己的应用之中。这对于那些使用了Spring Framework的组织和机构来说,在spring基础上实现应用不仅可以构建优秀的,可维护的应用并对Spring的设计进行验证,确实是一件好事情。
简化Java开发
Spring是一个开源框架,最早由Rod Johnson创建,并在《Expert One-on-One:J2EE Design and Development》这本著作中进行了介绍。Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但Spring不仅仅局限于服务器端开发,任何Java应用都能在简单性、可测试性和松耦合等方面从Spring中获益。
bean的各种名称……虽然Spring用bean或者JavaBean来表示应用组件,但并不意味着Spring组
件必须要遵循JavaBean规范。一个Spring组件可以是任何形式的POJO(POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称)。
Spring 可以做非常多的事情。但归根结底,支撑Spring的仅仅是少许的基本理念,所有的理念都可以追溯到Spring最根本的使命上:简化Java开发。这是一个郑重的承诺。许多框架都声称在某些方面做了简化,但Spring的目标是致力于全方位的简化Java开发。这势必引出更多的解释,Spring是如何简化Java开发的?
为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
* 基于POJO的轻量级和最小侵入性编程;
* 通过依赖注入和面向接口实现松耦合;
* 基于切面和惯例进行声明式编程;
* 通过切面和模板减少样板式代码。
几乎Spring所做的任何事情都可以追溯到上述的一条或多条策略。
什么是最小侵入式
很多框架通过强迫应用继承它们的类或实现它们的接口从而导致应用与框架绑死。Spring竭力避免因自身的API而弄乱你的应用代码。Spring不会强迫你实现Spring规范的接口或继承Spring规范的类,相反,在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring。最坏的场景是,一个类或许会使用Spring注解,但它依旧是POJO。请参考下面的HelloWorldBean类:
可以看到,这是一个简单普通的Java类——POJO。没有任何地方表明它是一个Spring组件。Spring的非侵入编程模型意味着这个类在Spring应用和非Spring应用中都可以发挥同样的作用。
依赖注入
任何一个有实际意义的应用(肯定比Hello World示例更复杂)都会由两个或者更多的类组成,这些类相互之间进行协作来完成特定的业务逻辑。按照传统的做法,每个对象负责管理与自己相互协作的对象(即它所依赖的对象)的引用,这将会导致高度耦合和难以测试的代码。比如下面的例子,韦小宝有多重身份:小桂子、白龙使。但他每次出场时只是使用其中一种身份,然后做这种身份该做的事,如下:
我在创建WeiXiaoBao这个类的时候,给了他一个属性叫做小桂子,这样这个韦小宝不管我怎么new都只会做小桂子做的事,显然不合理。更重要的是,这个时候我很难对xiaoGuiZi这种身份进行单独的单元测试。我希望韦小宝能够按照我的需求扮演不同的角色,如下:
改造后的韦小宝如下,DoSth是一个接口,所有的身份都实现了这个接口。这时我不在韦小宝这个类的内部来创建身份类,而是通过构造方法传进来,这就是依赖注入的一种方式,叫做构造器注入。这种方式韦小宝没有和任何身份进行绑定。这就是DI所带来的最大收益——松耦合。如果一个对象只通过接口(而不是具体实现或初始化过程)来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行替换。
应用切面
DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(aspect-oriented programming,((AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。面向切面编程往往被定义为促使软件系统实现关注点的分离一项技术。系统由许多不同的组件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。
AOP能够使这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。所造成的结果就是这些组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉及系统服务所带来复杂性。总之,AOP能够确保POJO的简单性。
如图所示,我们可以把切面想象为覆盖在很多组件之上的一个外壳。应用是由那些实现各自业务功能的模块组成的。借助AOP,可以使用各种功能层去包裹核心业务层。这些层以声明的方式灵活地应用到系统中,你的核心应用甚至根本不知道它们的存在。这是一个非常强大的理念,可以将安全、事务和日志关注点与核心业务逻辑相分离。
使用模板消除样板式代码
看一段JDBC代码:
只有中间的查询操作是我们每次要修改的。前面的获取连接,后面的关闭连接都是模板代码,每次重复写这些代码增加了工作量,这里只是举了一种我们常见的模板代码,还有合很多其他的,这些在Spring中都可以通过相应的工具消除。
Spring主要模块
当我们下载Spring发布版本并查看其lib目录时,会发现里面有多个JAR文件。在Spring 4.0中,Spring框架的发布版本包括了20个不同的模块,每个模块会有3个JAR文件(二进制类库、源码的JAR文件以及JavaDoc的JAR文件)。完整的库JAR文件如图所示。
这些模块依据其所属的功能可以划分为6类不同的功能,总体而言,这些模块为开发企业级应用提供了所需的一切。但是你也不必将应用建立在整个Spring框架之上,你可以自由地选择适合自身应用需求的Spring模块;当Spring不能满足需求时,完全可以考虑其他选择。事实上,Spring甚至提供了与其他第三方框架和类库的集成点,这样你就不需要自己编写这样的代码了。如下图:
Spring核心容器
容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能。基于bean工厂,我们还会发现有多种Spring应用上下文的实现,每一种都提供了配置Spring的不同方式。除了bean工厂和应用上下文,该模块也提供了许多企业服务,例如E-mail、JNDI访问、EJB集成和调度。所有的Spring模块都构建于核心容器之上。当你配置应用时,其实你隐式地使用了这些类。
Spring的AOP模块
在AOP模块中,Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础。与DI一样,AOP可以帮助应用对象解耦。借助于AOP,可以将遍布系统的关注点(例如事务和安全)从它们所应用的对象中解耦出来。
数据访问与集成
使用JDBC编写代码通常会导致大量的样板式代码,例如获得数据库连接、创建语句、处理结果集到最后关闭数据库连接。Spring的JDBC和DAO(Data Access Object)模块抽象了这些样板式代码,使我们的数据库代码变得简单明了,还可以避免因为关闭数据库资源失败而引发的问题。该模块在多种数据库服务的错误信息之上构建了一个语义丰富的异常层,以后我们再也不需要解释那些隐晦专有的SQL错误信息了!对于那些更喜欢ORM(Object-RelationalMapping)工具而不愿意直接使用JDBC的开发者,Spring提供了ORM模块。Spring的ORM模块建立在对DAO的支持之上,并为多个ORM框架提供了一种构建DAO的简便方式。Spring没有尝试去创建自己的ORM解决方案,而是对许多流行的ORM框架进行了集成,包括Hibernate、Java Persisternce API、Java Data Object和iBATIS SQL Maps。Spring的事务管理支持所有的ORM框架以及JDBC。本模块同样包含了在JMS(JavaMessage Service)之上构建的Spring抽象层,它会使用消息以异步的方式与其他应用集成。从Spring 3.0开始,本模块还包含对象到XML映射的特性,它最初是Spring Web Service项目的一部分。除此之外,本模块会使用SpringAOP模块为Spring应用中的对象提供事务管理服务。
Web与远程调用
MVC(Model-View-Controller)模式是一种普遍被接受的构建Web应用的方法,它可以帮助用户将界面逻辑与应用逻辑分离。Java从来不缺少MVC框架,Apache的Struts、JSF、WebWork和Tapestry都是可选的最流行的MVC框架。虽然Spring能够与多种流行的MVC框架进行集成,但它的Web和远程调用模块自带了一个强大的MVC框架,有助于在Web层提升应用的松耦合水平。除了面向用户的Web应用,该模块还提供了多种构建与其他应用交互的远程调用方案。Spring远程调用功能集成了RMI(RemoteMethod Invocation)、Hessian、Burlap、JAX-WS,同时Spring还自带了一个远程调用框架:HTTP invoker。Spring还提供了暴露和使用REST API的良好支持。
Instrumentation
Spring的Instrumentation模块提供了为JVM添加代理(agent)的功能。具体来讲,它为Tomcat提供了一个织入代理,能够为Tomcat传递类文件,就像这些文件是被类加载器加载的一样。如果这听起来有点难以理解,不必对此过于担心。这个模块所提供的Instrumentation使用场景非常有限,在实际开发中我们很少会用到。
测试
鉴于开发者自测的重要性,Spring提供了测试模块以致力于Spring应用的测试。通过该模块,你会发现Spring为使用JNDI、Servlet和Portlet编写单元测试提供了一系列的mock对象实现。对于集成测试,该模块为加载Spring应用上下文中的bean集合以及与Spring上下文中的bean进行交互提供了支持。
Spring Portfolio
当谈论Spring时,其实它远远超出我们的想象。事实上,Spring远不是Spring框架所下载的那些。如果仅仅停留在核心的Spring框架层面,我们将错过Spring Portfolio所提供的巨额财富。整个Spring Portfolio包括多个构建于核心Spring框架之上的框架和类库。概括地讲,整个SpringPortfolio几乎为每一个领域的Java开发都提供了Spring编程模型。或许需要几卷书才能覆盖Spring Portfolio所提供的所有内容,下面我们介绍Spring Portfolio中的一些项目,同样,我们将体验一下核心框架之外的另一番风景。
Spring Web Flow
Spring Web Flow建立于Spring MVC框架之上,它为基于流程的会话式Web应用(可以想一下购物车或者向导功能)提供了支持。
Spring Security
安全对于许多应用都是一个非常关键的切面。利用Spring AOP,Spring Security为Spring应用提供了声明式的安全机制。
Spring Integration
许多企业级应用都需要与其他应用进行交互。SpringIntegration提供了多种通用应用集成模式的Spring声明式风格实现。
Spring Batch
当我们需要对数据进行大量操作时,没有任何技术可以比批处理更胜任这种场景。如果需要开发一个批处理应用,你可以通过Spring Batch,使用Spring强大的面向POJO的编程模型。
Spring Data
Spring Data使得在Spring中使用任何数据库都变得非常容易。尽管关系型数据库统治企业级应用多年,但是现代化的应用正在认识到并不是所有的数据都适合放在一张表中的行和列中。一种新的数据库种类,通常被称之为NoSQL数据库,提供了使用数据的新方法,这些方法会比传统的关系型数据库更为合适。不管你使用文档数据库,如MongoDB,图数据库,如Neo4j,还是传统的关系型数据库,Spring Data都为持久化提供了一种简单的编程模型。这包括为多种数据库类型提供了一种自动化的Repository机制,它负责为你创建Repository的实现。
Spring Social
社交网络是互联网领域中新兴的一种潮流,越来越多的应用正在融入社交网络网站,例如Facebook或者Twitter。SpringSocial,这是Spring的一个社交网络扩展模块。
Spring Mobile
移动应用是另一个引人瞩目的软件开发领域。智能手机和平板设备已成为许多用户首选的客户端。Spring Mobile是Spring MVC新的扩展模块,用于支持移动Web应用开发。
Spring for Android
与Spring Mobile相关的是Spring Android项目。这个新项目,旨在通过Spring框架为开发基于Android设备的本地应用提供某些简单的支持。最初,这个项目提供了SpringRestTemplate的一个可以用于Android应用之中的版本。它还能与Spring Social协作,使得原生应用可以通过REST API进行社交网络的连接。
Spring Boot
Spring极大地简化了众多的编程任务,减少甚至消除了很多样板式代码,如果没有Spring的话,在日常工作中你不得不编写这样的样板代码。Spring Boot是一个崭新的令人兴奋的项目,它以Spring的视角,致力于简化Spring本身。Spring Boot大量依赖于自动配置技术,它能够消除大部分(在很多场景中,甚至是全部)Spring配置。它还提供了多个Starter项目,不管你使用Maven还是Gradle,这都能减少Spring工程构建文件的大小。
3.1.2 Bean的注入
Spring提供了三种不同的注入方式:
* 在XML中进行显式配置。
* 在Java中进行显式配置。
* 隐式的bean发现机制和自动装配。
XMl配置
使用xml配置来实例化bean共分为三种方式,分别是:
* 普通构造方法创建
* 静态工厂创建
* 实例工厂创建
在这三种创建方式中,最常用的为第一种。
普通构造方法创建
1.创建Spring配置文件
2.创建实体类
3.配置实体类
4.调用
Spring自带了多种类型的应用上下文。下面罗列的几个是你最有可能遇到的。
* AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文
* AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文
* ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源
* FileSystemXmlapplicationcontext:从文件系统下的一个或多个XML配置文件中加载上下文定义
* XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文定义。
现在我们先简单地使用FileSystemXmlApplicationContext从文件系统中加载应用上下文或者使
用ClassPathXmlApplicationContext从类路径中加载应用上下文。无论是从文件系统中装载应用上下文还是从类路径下装载应用上下文,将bean加载到bean工厂的过程都是相似的。
使用FileSystemXmlApplicationContext和使用ClassPathXmlApplicationContext的区别在于:FileSystemXmlApplicationContext在指定的文件系统路径下查找配置文件;而ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件)下查找配置文件。如果你想从Java配置中加载应用上下文,那么可以使用AnnotationConfigApplicationContext:在这里没有指定加载Spring应用上下文所需的XML文件,AnnotationConfigApplicationContext通过一个配置类加载bean。
应用上下文准备就绪之后,我们就可以调用上下文的getBean()方法从Spring容器中获取
bean。
打印结果如下:
静态工厂创建
1.创建静态工厂方法
2.配置xml文件
3.测试:
实例工厂创建
1.创建普通工厂方法
2.xml中配置
先配置工厂实例,然后在这个实例中根据getUser方法来获取User实例。
3.测试
属性注入方式
属性注入方式有多种,最常用的是set方法注入
构造方法注入
1.新建User类如下:
2.在xml中配置时传入构造方法的参数:
set方法注入
1.新建User类如下:
2.xml文件中配置:
p名称空间注入
1.导入p名称空间
2.配置bean
注意:p名称空间也是通过set方法注入属性值的。
对象的注入
1.创建Music类如下:
2.创建播放类如下:
3.我们需要在Play类中注入Music,注入方式如下:
数组注入
1.创建User类如下:
2.xml中配置如下:
List集合注入
1.创建User类如下
注意,我们在List集合中放的是对象哦。
2.xml文件中进行配置
Map注入
1.创建实体类如下:
2.xml配置如下:
Properties注入
1.创建User实体类如下:
2.xml配置如下
Java配置
在进行显式配置时,JavaConfig是更好的方案,因为它更为强大、类型安全并且对重构友好。因为它就是Java代码,就像应用程序中的其他Java代码一样。同时,JavaConfig与其他的Java代码又有所区别,在概念上,它与应用程序中的业务逻辑是不同的。尽管它与其他的组件一样都使用相同的语言进行表述,但JavaConfig是配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了。
1.创建两个bean,如下:
第二个实体类中引用了第一个。
2.创建JavaConfig配置类,如下:
创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。要在JavaConfig中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解,@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在本例中,bean的名字将会是getSayHelloFunction和getUseSayHelloFunction。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定一个不同的名字。
3.测试
自动配置
Spring从两个角度来实现自动化装配:
* 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
* 自动装配(autowiring):Spring自动满足bean之间的依赖。
组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。
通过Java代码实现
1.创建可被Spring容器发现的Bean
创建可被Spring容器发现的Bean我们有多种注解可用,如下:
@Component注解,没有明确的角色
@Service注解,在业务逻辑层(service层)使用
@Repository注解,在数据访问层(dao层使用)
@Controller注解,在展现层使用
2.将一个Bean注入到另一个Bean中
Bean的注入有三种方式:
@Autowired:Spring提供的注解
@Inject:JSR-330提供的注解
@Resource:JSR-250提供的注解
这三个注解都可以直接用在属性上,也可以用在set方法中。
3.创建Java配置,扫描所有Bean
首先使用@Configuration注解说明这个一个配置类,然后通过@ComponentScan注解说明要扫描的包,这里我们配置了org.sang包,表示凡是这个包以及这个包的子包以及这个包的子包的子包的子包....中,所有声明为Bean的组件都会被注册到。
4.测试
通过XML配置文件实现
基本和Java代码实现一致,唯一不同的是,XML配置文件实现时,不需要Java配置,直接在Spring的配置文件中开启包扫描即可,如下:
混合配置
1.在Java配置中引用XML配置:
Profile
Java配置
1.创建DataSource类
2.创建Java配置文件
3.测试
XML配置
1.创建DataSource
2.创建prod.db.properties和dev.db.properties两个数据库配置文件
3.创建Spring的xml配置
4.测试
条件注解
类似于Profile,但是比Profile更灵活。
1.创建Windows和Linux的判断条件
2.创建命令显示接口及其实现类
3.创建Java配置文件
4.测试
Bean的作用域
单例(Singleton):在整个应用中,只创建bean的一个实例。
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(Session):在Web应用中,为每个会话创建一个bean实例。
请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
其他问题
(1)id属性:bean的名称,id属性值名称任意命名
* id属性值,不能包含特殊符号
* 根据id值得到配置对象
(2)class属性:创建对象所在类的全路径
(3)name属性:功能和id属性一样的
(4)name和id的细微区别
3.2 Day52
3.2.1 AOP简介
面向切面编程(AOP是AspectOriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。 但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。 也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。一般而言,我们把切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。
3.2.2 AOP几个关键概念
Joinpoint(连接点): 类里面可以被增强的方法,这些方法称为连接点
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Aspect(切面): 是切入点和通知(引介)的结合
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
Target(目标对象):代理的目标对象(要增强的类)
Weaving(织入):是把增强应用到目标的过程.把advice 应用到 target的过程
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
3.2.3 AOP原理
AOP通过Java中的动态代理来实现那你。
1.创建计算接口
2.创建计算接口实现类
3.创建接口动态代理类
4.测试
3.2.4 AOP实现
通过Java代码实现
使用注解拦截
前置、后置通知
1.创建注解
@Target一行表示该注解将作用在方法上
@Retention一行表示该注解将一直保存到运行时
@Documented表示该注解将保存在javadoc中
2.创建计算类,并添加注解
凡是使用了@Action注解的方法都是一个切点。
3.编写日志类
@Aspect注解表明该类是一个切面,@Pointcut表示定义一个切点。@After表示该切面会在方法执行之后执行,@Before表示该方法会在方法执行之前执行。
4.创建配置类
@EnableAspectJAutoProxy表示开启Spring对AspectJ代理的支持
5.测试
6.结果
返回通知
环绕通知
异常通知
按照方法规则拦截
前置、后置通知
除了按照注解拦截,也可以按照方法的命名规则拦截。
1.定义计算类如下
2.定义切面,按照方法名称拦截处理
3.定义配置类
4.测试
5.测试结果
返回通知
无论连接点正常返回还是抛出异常,后置通知都会执行。如果只想在连接点返回的时候记录日志,应使用返回通知代替后置通知。在返回通知中,只需要在@AfterReturning注解中添加returning属性,就可以访问连接点的返回值。必须在通知方法的签名中添加一个同名参数,在运行时spring AOP会通过这个参数传递给返回值。
环绕通知
Spring 的环绕通知和前置通知,后置通知有着很大的区别,主要有两个重要的区别:
1.目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知 是不能决定的,他们只是在方法的调用前后执行通知而已,即目标方法肯定是要执行的。
2.环绕通知可以控制返回对象,即你可以返回一个与目标对象完全不同的返回值,虽然这很危险,但是你却可以办到。而后置方法是无法办到的,因为他是在目标方法返回值后调用
异常通知
之前演示的两种案例都属于前置通知和后置通知,除了这两种通知之外,还有异常通知、返回通知以及环绕通知。
只在连接点抛出异常时才执行异常通知,将 throwing 属性添加到 @AfterThrowing 注解中, 也可以访问连接点抛出的异常. Throwable 是所有错误和异常类的超类. 所以在异常通知方法可以捕获到任何错误和异常.如果只对某种特殊的异常类型感兴趣, 可以将参数声明为其他异常的参数类型. 然后通知就只在抛出这个类型及其子类的异常时才被执行.
通过XML配置实现
使用注解拦截
按照方法规则拦截
前置通知
1.创建切面
2.创建XML配置文件
返回通知
1.创建方法
2.XML中配置
环绕通知
1.创建方法
2.XML文件中配置
异常通知
1.创建方法
2.XML中配置
后置通知
3.3 Day53
3.3.1 JdbcTemplate
增删改查
1.增
2.删
3.改
4.查
4.1查询总记录数
4.2按条件查询对象
4.3查询列表
配合数据库连接池
1.XML文件中配置bean
2.创建UserDao
3.创建UserService
4.测试
3.3.2 Spring事务管理
回忆事务
原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。
XML配置实现
1.创建UserDao
2.创建UserService,模拟转账
3.XML配置
3.1配置事务管理器
3.2配置增强/通知
3.3配置AOP
4.测试
Java配置实现
1.创建UserDao
2.创建UserService,模拟转账
3.Java配置
3.1配置事务管理器
3.2配置增强/通知
3.3在需要处理的类上添加注解
4.测试
4 4.SpringMVC基础
4.1 Day54
4.1.1 简介
1.Spring Web MVC是什么
Spring Web MVC是一种基于Java的实现了WebMVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。
Spring Web MVC也是服务到工作者模式的实现,但进行可优化。前端控制器是DispatcherServlet;应用控制器其实拆为处理器映射器(Handler Mapping)进行处理器管理和视图解析器(View Resolver)进行视图管理;页面控制器/动作/处理器为Controller接口(仅包含ModelAndView handleRequest(request, response) 方法)的实现(也可以是任何的POJO类);支持本地化(Locale)解析、主题(Theme)解析及文件上传等;提供了非常灵活的数据验证、格式化和数据绑定机制;提供了强大的约定大于配置(惯例优先原则)的契约式编程支持。
2.Spring Web MVC能帮我们做什么
* 让我们能非常简单的设计出干净的Web层和薄薄的Web层;
* 进行更简洁的Web层的开发;
* 天生与Spring框架集成(如IoC容器、AOP等);
* 提供强大的约定大于配置的契约式编程支持;
* 能简单的进行Web层的单元测试;
* 支持灵活的URL到页面控制器的映射;
* 非常容易与其他视图技术集成,如Velocity、FreeMarker等等,因为模型数据不放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用);
* 非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的API;
* 提供一套强大的JSP标签库,简化JSP开发;
* 支持灵活的本地化、主题等解析;
* 更加简单的异常处理;
* 对静态资源的支持;
* 支持Restful风格。
SpringMVC工作流程
1.工作流程图
2.详述
1.用户发送请求至前端控制器DispatcherServlet
2.DispatcherServlet收到请求调用HandlerMapping处理器映射器
3.处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
4.DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5.执行处理器(Controller,也叫后端控制器)
6.Controller执行完成返回ModelAndView
7.HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9.ViewReslover解析后返回具体View
10.DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
11.DispatcherServlet响应用户
SpringMVC相关组件
1.DispatcherServlet:前端控制器
用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。
2.HandlerMapping:处理器映射器
HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
3.Handler:处理器
Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
4.HandlAdapter:处理器适配器
通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
5.View Resolver:视图解析器
View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
HelloWorld
1.导入相关的jar包
2.web.xml文件中配置SpringMVC
contextConfigLocation:指定springmvc配置的加载位置,如果不指定则默认加载WEB-INF/[DispatcherServlet 的Servlet 名字]-servlet.xml。以上面的配置为例,如果不指定SpringMVC配置文件的位置,将默认加载WEB-INF/springMVC-servlet.xml作为配置文件。
3.在SpringMVC的配置文件中配置HandlerMapping和HandlerAdapter
BeanNameUrlHandlerMapping:表示将请求的URL和Bean名字映射(即Bean的name属性),如URL为"XXX/hello",则Spring配置文件必须有一个名字为"/hello"的Bean,项目名称默认忽略。
SimpleControllerHandlerAdapter:表示所有实现了org.springframework.web.servlet.mvc.Controller接口的Bean可以作为Spring Web MVC中的处理器。如果需要其他类型的处理器可以通过实现HandlerAdapter来解决。
4.配置视图解析器
prefix和suffix:查找视图页面的前缀和后缀(前缀[逻辑视图名]后缀),比如传进来的逻辑视图名为hello,则该该jsp视图页面应该存放在“WEB-INF/jsp/hello.jsp”;
5.编写处理器
*org.springframework.web.servlet.mvc.Controller:页面控制器/处理器必须实现Controller接口。
* public ModelAndViewhandleRequest(HttpServletRequest req, HttpServletResponse resp) :功能处理方法,实现相应的功能处理,比如收集参数、验证参数、绑定参数到命令对象、将命令对象传入业务对象进行业务处理、最后返回ModelAndView对象;
* ModelAndView:包含了视图要实现的模型数据和逻辑视图名;“mv.addObject("users","list");”表示添加模型数据,此处可以是任意POJO对象;“mv.setViewName("userlist");”表示设置逻辑视图名为"userlist",视图解析器会将其解析为具体的视图,用前边的视图解析器InternalResourceViewResolver会将其解析为“WEB-INF/jsp/userlist.jsp”。
然后在SpringMVC的配置文件中注册该bean
注意这里的name="/userList.do"表示当浏览器中的访问地址为/userList.do的时候,就会请求到这个Bean中来。这个主要是由于我们前边配置了BeanNameUrlHandlerMapping这个东西。
6.编写userlist.jsp页面,注意页面位置
7.运行流程
1.首先用户发送请求http://localhost:8080/userList.do到web容器,web容器根据"/userList.do"路径映射到DispatcherServlet(url-pattern为/)进行处理;
2、 DispatcherServlet——>BeanNameUrlHandlerMapping进行请求到处理的映射,BeanNameUrlHandlerMapping将"/userList.do"路径直接映射到名字为"/userList.do"的Bean进行处理,即UserList,BeanNameUrlHandlerMapping将其包装为HandlerExecutionChain(只包括UserList处理器,没有拦截器);
3.DispatcherServlet——>SimpleControllerHandlerAdapter,SimpleControllerHandlerAdapter将HandlerExecutionChain中的处理器(UserList)适配为SimpleControllerHandlerAdapter;
4.SimpleControllerHandlerAdapter——>UserList处理器功能处理方法的调用,SimpleControllerHandlerAdapter将会调用处理器的handleRequest方法进行功能处理,该处理方法返回一个ModelAndView给DispatcherServlet;
5.userlist(ModelAndView的逻辑视图名)——>InternalResourceViewResolver, InternalResourceViewResolver找到具体视图页面在/WEB-INF/jsp/userlist.jsp;
6.JstlView(/WEB-INF/jsp/hello.jsp)——>渲染,将在处理器传入的模型数据(message=HelloWorld!)在视图中展示出来;
7.返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。
到此userList.do请求就完成了,步骤是不是有点多?而且回忆下我们主要进行了如下配置:
1.前端控制器DispatcherServlet;
2.HandlerMapping
3.HandlerAdapter
4.ViewResolver
5.处理器/页面控制器
6.视图
4.1.2 组件介绍
DispatcherServlet
1.DispatcherServlet作用
DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。DispatcherServlet主要用作职责调度工作,本身主要用于控制流程,主要职责如下:
1.文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
2.通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
3.通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
4.通过ViewResolver解析逻辑视图名到具体视图实现;
5.本地化解析;
6.渲染具体的视图等;
7.如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。
2.DispathcherServlet配置详解
load-on-startup:表示启动容器时初始化该Servlet;
url-pattern:表示哪些请求交给Spring Web MVC处理,"/" 是用来定义默认servlet映射的。也可以如“*.html”表示拦截所有以html为扩展名的请求
contextConfigLocation:表示SpringMVC配置文件的路径
其他的参数配置:
参数 |
描述 |
contextClass |
实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定, 默认使用XmlWebApplicationContext。 |
contextConfigLocation |
传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。 |
namespace |
WebApplicationContext命名空间。默认值是[server-name]-servlet。 |
OK,以上是关于SpringMVC的配置,我们再来看看Spring的一个通用配置,如下:
如上配置是Spring集成Web环境的通用配置;一般用于加载除Web层的Bean(如DAO、Service等),以便于与其他任何Web框架集成。
* contextConfigLocation:表示用于加载Bean的配置文件;
* contextClass:表示用于加载Bean的ApplicationContext实现类,默认WebApplicationContext。
创建完毕后会将该上下文放在ServletContext:
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.context);
ContextLoaderListener初始化的上下文和DispatcherServlet初始化的上下文关系,如下图:
从图中可以看出:
ContextLoaderListener初始化的上下文加载的Bean是对于整个应用程序共享的,不管是使用什么表现层技术,一般如DAO层、Service层Bean;
DispatcherServlet初始化的上下文加载的Bean是只对Spring Web MVC有效的Bean,如Controller、HandlerMapping、HandlerAdapter等等,该初始化上下文应该只加载Web相关组件。
Controller
1.@RequestMapping
通过@RequestMapping注解可以定义不同的处理器映射规则。
1.1 URL路径映射
@RequestMapping(value="/item")或@RequestMapping("/item)
value的值是数组,可以将多个url映射到同一个方法
1.2 窄化请求映射
在class上添加@RequestMapping(url)指定通用请求前缀,限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。
如下:
@RequestMapping放在类名上边,设置请求前缀
@Controller
@RequestMapping("/item")
方法名上边设置请求映射url:
@RequestMapping放在方法名上边,如下:
@RequestMapping("/queryItem ")
访问地址为:/item/queryItem
1.3 请求方法限定
* 限定GET方法
@RequestMapping(method = RequestMethod.GET)
如果通过Post访问则报错:
HTTP Status 405 - Request method 'POST' notsupported
例如:
@RequestMapping(value="/editItem",method=RequestMethod.GET)
* 限定POST方法
@RequestMapping(method =RequestMethod.POST)
如果通过GET访问则报错:
HTTP Status 405 - Request method 'GET' notsupported
* GET和POST都可以
@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
2.controller方法返回值
2.1 返回ModelAndView
controller方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。
2.2 返回void
在controller方法形参上可以定义request和response,使用request或response指定响应结果:
2.2.1 使用request转向页面,如下:
request.getRequestDispatcher("页面路径").forward(request,response);
2.2.2 也可以通过response页面重定向:
response.sendRedirect("url")
2.2.3 也可以通过response指定响应结果,例如响应json数据如下:
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
2.3 返回字符串
2.3.1 逻辑视图名
controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
//指定逻辑视图名,经过视图解析器解析为jsp物理路径:/WEB-INF/jsp/item/editItem.jsp
return "item/editItem";
2.3.2 Redirect重定向
Contrller方法返回结果重定向到一个url地址,如下商品修改提交后重定向到商品查询方法,参数无法带到商品查询方法中。
//重定向到queryItem.action地址,request无法带过去
return"redirect:queryItem.action";
redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。
由于新发起一个request原来的参数在转发时就不能传递到下一个url,如果要传参数可以/item/queryItem.action后边加参数,如下:
/item/queryItem?...&…..
2.3.3 forward转发
controller方法执行后继续执行另一个controller方法,如下商品修改提交后转向到商品修改页面,修改商品的id参数可以带到商品修改方法中。
//结果转发到editItem.action,request可以带过去
return "forward:editItem.action";
forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。
3.参数绑定
处理器适配器在执行Handler之前需要把http请求的key/value数据绑定到Handler方法形参数上。
3.1 默认支持的参数类型
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值。
3.1.1 HttpServletRequest
通过request对象获取请求信息
3.1.2 HttpServletResponse
通过response处理响应信息
3.1.3 HttpSession
通过session对象得到session中存放的对象
3.1.4 Model/ModelMap
ModelMap是Model接口的实现类,通过Model或ModelMap向页面传递数据,如下:
//调用service查询商品信息
Items item = itemService.findItemById(id);
model.addAttribute("item", item);
页面通过${item.XXXX}获取item对象的属性值。
使用Model和ModelMap的效果一样,如果直接使用Model,springmvc会实例化ModelMap。
3.2 参数绑定介绍
注解适配器对RequestMapping标记的方法进行适配,对方法中的形参会进行参数绑定,早期springmvc采用PropertyEditor(属性编辑器)进行参数绑定将request请求的参数绑定到方法形参上,3.X之后springmvc就开始使用Converter进行参数绑定。
3.2.1 简单类型
当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。
3.2.2 整型
public String editItem(Model model,Integerid) throws Exception{
}
3.2.3 字符串、单精度/双精度
3.2.4 布尔型
处理器方法:
public String editItem(Model model,Integerid,Boolean status) throws Exception
3.2.5 @RequestParam
使用@RequestParam常用于处理简单类型的绑定。
value:参数名字,即入参的请求参数名字,如value=“item_id”表示请求的参数区中的名字为item_id的参数的值将传入;
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报;
TTP Status 400 - Required Integer parameter'XXXX' is not present
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
定义如下:
public StringeditItem(@RequestParam(value="item_id",required=true) String id) {
}
形参名称为id,但是这里使用value=" item_id"限定请求的参数名为item_id,所以页面传递参数的名必须为item_id。
注意:如果请求参数中没有item_id将跑出异常:
HTTP Status 500 - Required Integer parameter'item_id' is not present
这里通过required=true限定item_id参数为必需传递,如果不传递则报400错误,可以使用defaultvalue设置默认值,即使required=true也可以不传item_id参数值
3.3 pojo
3.3.1 简单pojo
将pojo对象中的属性名与传递进来的属性名对应,如果传进来的参数名称和对象中的属性名称一致则将参数值设置在pojo对象中
页面定义如下;
<input type="text"name="name"/>
<input type