-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
296 lines (296 loc) · 268 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Git常用命令记录]]></title>
<url>%2F2018%2F12%2F03%2FGit%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E8%AE%B0%E5%BD%95%2F</url>
<content type="text"><![CDATA[  前言:这段时间稍微比较忙,刚刚进入一家公司实习不久,需要学习的东西有很多,业务流程分析、微服务框架等等。在学习过程中踩了很多坑,到现在回忆起来都有感觉。这段时间主要学习的SpringCloud全家桶微服务解决思路及架构,后面抽个时间详细记录下学习过程与步骤的。这篇文章主要记录下在使用版本控制中经常使用的一些Git命令(自己老是忘记,这里记录一下,忘了就来翻翻)。 Git常用命令  什么是Git就不再记录,而Idea也非常友好的集成了Git相关操作的,但是按钮不如黑框敲命令来的直接,所以能动手敲下就绝不使用快捷按钮。 git常用命令1234567891011121314151617181920212223242526272829303132333435363738git init # 在本地项目生成一个.git的隐藏文件(本地仓库)git clone # 如 $ git clone https://github.com/zqiheng/MicroServiceDemo2.git 就会克隆这个远程仓库的项目到本地git add . # 注意:点(.)前面有个空格,表示将所有与.git文件同级或子级的文件添加到本地仓库(.git)中git add 文件名 # 指定一个文件添加到本地仓库中git status # 查看本地仓库的文件状态git log # 查看日志git diff # 此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容git commit # 提交刚刚被 add 进来的操作 git commit -m “the commit message" git commit --amend # 增补提交. 会使用与当前提交节点相同的父节点进行一次新的提交,旧的提交将会被取消# 这里省略远程仓库的ssh配置git reset # undo changes and commits.git revert # 反转撤销提交.只要把出错的提交(commit)的名字(reference)作为参数传给命令就可以了 git revert HEAD # 撤销最近的一个提交 git revert会创建一个反向的新提交,可以通过参数-n来告诉Git先不要提交git remote add origin [远程仓库地址] # 此操作是将远程仓库关联到本地仓库git push -u origin master # 提交本地仓库到远程仓库(**注意:如果新建远程仓库是空的,如没有Readme文件。则需要加上 -u 参数**)# 如果远程参数不是空的(在建立的时候自己添加了文件),那么上述命令(git push -u origin master)在本地执行时会报错,通过下面命令解决git pull --rebase origin master # 即先从远程仓库拉取更新文件git push origin master # 然后再添加文件到远程仓库   简单的一些操作命令就先记录到这里,后面有添加再更新。]]></content>
<categories>
<category>开发工具</category>
</categories>
<tags>
<tag>开发工具</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Java数据结构——实现自定义泛型可扩容数组]]></title>
<url>%2F2018%2F10%2F30%2FJava%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E2%80%94%E2%80%94%E5%AE%9E%E7%8E%B0%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%9B%E5%9E%8B%E5%8F%AF%E6%89%A9%E5%AE%B9%E6%95%B0%E7%BB%84%2F</url>
<content type="text"><![CDATA[  前言:数组是一种数据结构,用来存储同一类型的值。运用很广泛,在很多Java数据结构中,底层大多采用数组结构来实现。下面记录下自己写的一个自定义的泛型可扩容数组。 1. 废话不多说,直接上代码MyArray.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158package cn.zqiheng.utils;import java.util.Arrays;/** * @author Heng * Date:2018.10.30 * 线性结构:【泛型数组】 * 要求: * 1.使用数组实现一个可动态扩容的数组,每添加一个元素数组长度加一。 * 2.实现数组的增、删、改、查、插入。 * 3.使用泛型 */public class MyArray<T> { private T[] arrays; /** * 构造函数初始化一个为0的数组 */ public MyArray() { this.arrays = (T[]) new Object[0]; } /** * @return 返回数组的长度 */ public int size() { return arrays.length; } /** * 向数组的末尾添加一个元素 * * @param value 添加的值 */ public void add(T value) { //创建一个新的数组 T[] newArrays = (T[]) new Object[arrays.length + 1]; //把原数组的内容赋值给新的数组 for (int i = 0; i < arrays.length; i++) { newArrays[i] = arrays[i]; } //把添加的元素放入到新的数组末尾 newArrays[newArrays.length - 1] = value; //新数组替换旧数组 arrays = newArrays; } /** * 根据传入的下标值找到对应的值 * * @param location 下标值 * @return 对应的值 */ public T get(int location) { if (location < 0 || location > arrays.length - 1) throw new RuntimeException("数组下标越界"); return arrays[location]; } /** * 根据传入的下标值,删除指定元素 * * @param location 下标 */ public void remove(int location) { if (location < 0 || location > arrays.length - 1) throw new RuntimeException("数组下标越界"); //定义一个新数组 T[] newArrays = (T[]) new Object[arrays.length - 1]; //将旧数组的值赋值给新数组 for (int i = 0; i < newArrays.length; i++) { if (i < location) { newArrays[i] = arrays[i]; } else { newArrays[i] = arrays[i + 1]; } } //新数组替换旧数组 arrays = newArrays; } /** * 修改指定元素的值 * * @param location 要修改的元素位置 * @param value 修改的值 */ public void set(int location, T value) { if (location < 0 || location > arrays.length - 1) throw new RuntimeException("数组下标越界"); arrays[location] = value; } /** * 在指定位置插入元素 * * @param location 要插入的位置 * @param value 插入的值 */ public void insert(int location, T value) { if (location < 0 || location > arrays.length - 1) throw new RuntimeException("数组下标越界"); //定义一个新数组 T[] newArrays = (T[]) new Object[arrays.length + 1]; //将原数组的值赋值给新数组 for (int i = 0; i < newArrays.length; i++) { if (i < location) { newArrays[i] = arrays[i]; } else if (i == location) { newArrays[i] = value; } else { newArrays[i] = arrays[i - 1]; } } //新数组替换旧数组 arrays = newArrays; } /** * 判断数组中是否包含指定元素 * * @param value 元素 * @return true|false */ public boolean contains(T value) { /** * 获取参数的类型 */ String type = value.getClass().getName(); for (T arr : arrays) { if (type.equals("java.lang.Integer") || type.equals("java.lang.Byte") || type.equals("java.lang.Short") || type.equals("java.lang.Long")) { if (arr == value) return true; } else if (type.equals("java.lang.Float") || type.equals("java.lang.Double")) { if ((Double) arr - (Double) value == 0) { return true; } } else { if (arr == value && arr.equals(value)) return true; } } return false; } /** * 格式化输出 * * @return */ public String toString() { return Arrays.toString(arrays); }} 2. 测试代码2.1 Integer类型测试12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package cn.zqiheng.basic;import cn.zqiheng.utils.*;public class ArrayDemo { public static void main(String[] args) { /** * 测试自定义的数组 */ MyArray<Integer> myArray = new MyArray(); for(int i=0;i<10;i++){ myArray.add((int)(Math.random()*1000)); } System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //获取下标为2的数组元素 System.out.println("获取下标为2的数组元素:"+myArray.get(2)); System.out.println("================================================"); //删除下标为2的数组元素 myArray.remove(2); System.out.println("删除下标为2的数组元素"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //修改下标为5的元素为88 myArray.set(5,88); System.out.println("修改下标为5的元素为88"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //在3号位置插入8 myArray.insert(3,8); System.out.println("在3号位置插入8"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //数组中是否包含指定元素 System.out.println(myArray.contains(88)); }}   测试结果: 2.2 String类型测试12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package cn.zqiheng.basic;import cn.zqiheng.utils.*;public class ArrayDemo { public static void main(String[] args) { /** * 测试自定义的数组 */ MyArray<String> myArray = new MyArray(); for(int i=0;i<10;i++){ myArray.add("Heng-"+(int)(Math.random()*1000)); } System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //获取下标为2的数组元素 System.out.println("获取下标为2的数组元素:"+myArray.get(2)); System.out.println("================================================"); //删除下标为2的数组元素 myArray.remove(2); System.out.println("删除下标为2的数组元素"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //修改下标为5的元素为恒 myArray.set(5,"恒"); System.out.println("修改下标为5的元素为恒"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //在3号位置插入Tom myArray.insert(3,"Tom"); System.out.println("在3号位置插入Tom"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //数组中是否包含指定元素 System.out.println(myArray.contains("恒")); }}   测试结果: 2.3 Double类型测试12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849package cn.zqiheng.basic;import cn.zqiheng.utils.*;public class ArrayDemo { public static void main(String[] args) { /** * 测试自定义的数组 */ MyArray<Double> myArray = new MyArray(); for(int i=0;i<10;i++){ myArray.add((Math.random()*1000)); } System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //获取下标为2的数组元素 System.out.println("获取下标为2的数组元素:"+myArray.get(2)); System.out.println("================================================"); //删除下标为2的数组元素 myArray.remove(2); System.out.println("删除下标为2的数组元素"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //修改下标为5的元素为88D myArray.set(5,88D); System.out.println("修改下标为5的元素为88D"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //在3号位置插入3D myArray.insert(3,3D); System.out.println("在3号位置插入3D"); System.out.println("数组的长度为:" + myArray.size()); System.out.println("数组的元素为:" + myArray.toString()); System.out.println("================================================"); //数组中是否包含指定元素 System.out.println(myArray.contains(88D)); }}   测试结果:]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>Java</tag>
</tags>
</entry>
<entry>
<title><![CDATA[SSM框架之Spring+SpringMVC+MyBaits框架技术整合]]></title>
<url>%2F2018%2F10%2F22%2FSSM%E6%A1%86%E6%9E%B6%E4%B9%8BSpring%2BSpringMVC%2BMyBaits%E6%A1%86%E6%9E%B6%E6%8A%80%E6%9C%AF%E6%95%B4%E5%90%88%2F</url>
<content type="text"><![CDATA[前言:这段时间学习了MyBatis知识,这篇文章记录下SSM框架的搭建过程,SSM框架是现在企业开发中常见的一种开发模式,下面使用一个登陆、注册的小实例来整合这三个框架。   开发工具: Intellij IDEA 2018.1.5 JDK 8 Maven 3.5.3 Tomcat 9 MySql 5   功能要求:用户通过url进入登陆页面,如有账号则登陆,没有则需要注册。 整合思路:SpringMVC是Spring大家族中的一个组件,因此我们不需要整合这两个,只需要整合Spring和MyBatis即可。 1. 数据库建表  在数据库中新建一个user用户表,包含字段如下。 id (主键自增) username (用户名) account (登陆账号) password (登陆密码) gender (性别)   如下图所示。 2. 新建Maven项目  使用Idea新建一个Maven webapp项目,并配置好 Modules 和 Tomcat 服务器。   先给一张项目结构图。 3. 导入项目依赖包  在pom.xml中导入项目的依赖包。 pom.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <!-- java ee jar包 --> <javaee.version>8.0</javaee.version> <!-- jstl jar包 --> <jstl.version>1.2</jstl.version> <!-- mysql-connector包 --> <mysql.version>5.1.45</mysql.version> <!-- commons-dbcp --> <dbcp.version>1.4</dbcp.version> <!-- mybatis-spring整合包 --> <mybatis-spring.version>1.3.1</mybatis-spring.version> <!-- spring版本号 --> <spring.version>4.2.5.RELEASE</spring.version> <!-- mybatis版本号 --> <mybatis.version>3.4.6</mybatis.version> <!-- jackson版本号 --> <jackson.verson>2.8.9</jackson.verson> <!-- junit --> <junit-version>4.12</junit-version> <!-- cglib --> <cglib-version>3.2.2</cglib-version> <!-- log4j日志文件管理包版本 --> <slf4j.version>1.7.21</slf4j.version> <log4j.version>1.2.17</log4j.version></properties><dependencies> <!-- 导入java ee jar 包 --> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>${javaee.version}</version> </dependency> <!-- jstl包 --> <!-- JSTL标签类 --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <!-- 导入Mysql数据库链接jar包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!-- dbcp数据库连接池包 --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>${dbcp.version}</version> </dependency> <!--mybatis-spring整合包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>${mybatis-spring.version}</version> </dependency> <!-- mybatis-核心包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!--spring-核心包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!-- jackson包 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.verson}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>${jackson.verson}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>${jackson.verson}</version> </dependency> <!-- 测试包 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit-version}</version> <scope>test</scope> </dependency> <!-- cglib包 --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>${cglib-version}</version> </dependency> <!--日志记录的jar包 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <!-- aspectjweaver包 --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency> <!-- aspectjrt 包--> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.5.3</version> </dependency> <!--MD5加密工具依赖包--> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.10</version> </dependency></dependencies>   注意:这里需要在 build 中加入下面配置代码 pom.xml12345678910111213141516171819 <build> <finalName>SSMDemo</finalName> <!-- 让idea识别 Mapper.mxl 的映射文件和其他配置文件 --> <!-- 也可以不用配置这里,而是把 cn.zqiheng.mapper 这个子包标记为resources文件要不然在编译的时候mybatis中会找不到maapper的xml和其它配置文件 --> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> </build> 4. 配置web.xml文件、日志文件、数据库连接属性配置文件  JavaWeb项目和核心配置文件。其中使用了DispatcherServlet作为核心控制器。web.xml1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <!-- 加载spring整合mybatis的容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mybatis.xml</param-value> </context-param> <!--过滤器:解决post提交中文乱码处理--> <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <!--这里是 /* 不是 / --> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--SpringMVC的核心控制器配置--> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--下面的contextConfigLocation配置springMVC加载的配置文件,映射器、处理器、视图解析器的文件--> <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映射,拦截所有url请求--> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <!--这里是 / 不是 /* --> <url-pattern>/</url-pattern> </servlet-mapping></web-app>   下面这个日志文件可以不用改(网上也有很多)。 log4j.xml12345678910111213141516171819<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"><log4j:configuration debug="true"> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="debug" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root></log4j:configuration>   数据库属性配置文件需要改成自己的用户名和密码(这里用的是MySql)。db.properties1234driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/ssmdemo?useUnicode=true&characterEncoding=UTF-8&useSSL=trueuser=rootpassword=root 5. SpringMVC的视图解析器配置springmvc.xml1234567891011121314151617181920212223<?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--启用注解驱动--> <mvc:annotation-driven/> <!--扫描指定包,注册到spring工厂中--> <context:component-scan base-package="cn.zqiheng.controller"/> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- jsp所在的位置(前缀)--> <!--统一放在//WEB-INF/jsp/ 目录下--> <property name="prefix" value="/WEB-INF/jsp/" /> <!-- jsp文件的后缀--> <property name="suffix" value=".jsp" /> </bean></beans> 6. Spring整合MyBatis的配置文件  建立一个spring-mybatis.xml文件,将开始在mybatis中的数据库连接和注册Mapper映射文件以及SqlSessionFactoryBean交给Spring管理。spring-mybatis.xml1234567891011121314151617181920212223242526272829303132333435363738<?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" 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.xsd"> <!--自动扫描指定包--> <context:component-scan base-package="cn.zqiheng.*"/> <!--开启注解--> <context:annotation-config/> <!--导入数据库配置文件--> <context:property-placeholder location="classpath:db.properties"/> <!--配置数据源--> <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </bean> <!--Mybatis核心配置,将SqlSessionFactoryBean交给Spring管理--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="datasource"/> <!--这句话是配置Mapper配置文件的位置,如果采用注解的方式这句话可以省去--> <!--路径统一放在cn/zqiheng/mapper中--> <property name="mapperLocations" value="classpath:cn/zqiheng/mapper/*Mapper.xml"/> <!--这里可以加入mybatis的总配置文件--> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> <!--自动搜索Mapper的接口,统一放在dao层中--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="cn.zqiheng.dao"/> </bean></beans>   Mybaits的总配置文件mybatis-config.xml(虽然说spring整合了mybatis,但是为了方便维护和可读性,mybatis的配置文件中可以配置一些其它属性)。mybatis-config.xml12345678910111213141516<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "_//mybatis.ort//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!-- settings 标签,全局mybatis属性配置 --> <settings> <setting name="logImpl" value="LOG4J"/> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true"/> </settings> <!--类取别名等等--></configuration> 7. 新建用户实体类、MD5加密工具类  用户实体类需和数据库对应一致。 user.java123456789101112131415161718192021222324252627282930313233package cn.zqiheng.pojo;/** * @author Heng * 用户类(id、用户名、账号、密码、性别) */public class User { /** * 主键id */ private int id; /** * 用户名 */ private String username; /** * 账号名 */ private String account; /** * 密码 */ private String password; /** * 性别 */ private boolean gender; /** * 此处省略了 get和set方法、构造方法、toString方法 *?} 此处MD5加密算法随便在网上找了一个:参考起个名字好难 MD5.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566package cn.zqiheng.tools;import java.security.MessageDigest;public class MD5 { //公盐 private static final String PUBLIC_SALT = "SSMDemo" ; //十六进制下数字到字符的映射数组 private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; /** * 用户密码加密,盐值为 :私盐+公盐 * @param password 密码 * @param salt 私盐 * @return MD5加密字符串 */ public static String encryptPassword(String password,String salt){ return encodeByMD5(PUBLIC_SALT+password+salt); } /** * md5加密算法 * @param originString * @return */ private static String encodeByMD5(String originString){ if (originString != null){ try{ //创建具有指定算法名称的信息摘要 MessageDigest md = MessageDigest.getInstance("MD5"); //使用指定的字节数组对摘要进行最后更新,然后完成摘要计算 byte[] results = md.digest(originString.getBytes()); //将得到的字节数组变成字符串返回 String resultString = byteArrayToHexString(results); return resultString.toUpperCase(); } catch(Exception ex){ ex.printStackTrace(); } } return null; } /** * 转换字节数组为十六进制字符串 * @param * @return 十六进制字符串 */ private static String byteArrayToHexString(byte[] b){ StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++){ resultSb.append(byteToHexString(b[i])); } return resultSb.toString(); } /** 将一个字节转化成十六进制形式的字符串 */ private static String byteToHexString(byte b){ int n = b; if (n < 0) n = 256 + n; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; }} 8. Dao层开发、Service层开发  数据持久层开发,因为这里我们需要使用MyBaits操作数据库,因此不需要接口实现类。UserDao.java12345678910111213141516171819202122232425262728293031package cn.zqiheng.dao;import cn.zqiheng.pojo.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repositorypublic interface UserDao { /** * 检查登陆信息是否正确 * @param account 登陆账户 * @param password 登陆密码 * @return User */ User checkLoginInfo(@Param("account") String account,@Param("password") String password); /** * 检查是否有该用户存在 * @param account 账户名 * @return User */ User isExistAccount(@Param("account") String account); /** * 添加用户信息(注册) * @param user 用户实体类 * @return 返回结果 */ Integer addUserInfo(User user);}   业务层Service开发,并有一个接口实现类。UserService.java1234567891011121314151617181920212223242526272829package cn.zqiheng.service;import cn.zqiheng.pojo.User;public interface UserService { /** * Service层,根据用户登陆名和密码匹配数据库中的信息 * @param account * @param password * @return */ User checkLoginInfo(String account,String password); /** * Service层,根据用户账号检测数据库中是否存在该账号 * @param account * @return */ User isExistAccount(String account); /** * Service层,添加用户信息(注册功能) * @param user * @return */ Integer addUserInfo(User user);}   Service接口实现类。ServiceImpl.java1234567891011121314151617181920212223242526272829package cn.zqiheng.service.impl;import cn.zqiheng.dao.UserDao;import cn.zqiheng.pojo.User;import cn.zqiheng.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public User checkLoginInfo(String account, String password) { return userDao.checkLoginInfo(account,password); } @Override public User isExistAccount(String account) { return userDao.isExistAccount(account); } @Override public Integer addUserInfo(User user) { return userDao.addUserInfo(user); }} 9. Mapper映射文件、Controller层开发  根据对应的Dao开发相应的Mapper映射文件。UserMapper.xml123456789101112131415161718192021222324252627282930<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "_//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="cn.zqiheng.dao.UserDao"> <!-- 检测数据库中是否存在该用户信息 --> <select id="checkLoginInfo" resultType="cn.zqiheng.pojo.User"> select * from user where account = #{account} and password = #{password} </select> <!-- 检查数据库中是否存在该账号信息 --> <select id="isExistAccount" resultType="cn.zqiheng.pojo.User"> select * from user where account = #{account} </select> <!--添加用户信息--> <insert id="addUserInfo"> insert into user(account,username,password,gender) values (#{account},#{username},#{password},#{gender}) </insert></mapper>   URL请求操作的Controller层开发。UserController.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129package cn.zqiheng.controller;import cn.zqiheng.pojo.User;import cn.zqiheng.service.UserService;import cn.zqiheng.tools.MD5;import org.apache.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.SessionAttributes;import org.springframework.web.bind.support.SessionStatus;import org.springframework.web.servlet.ModelAndView;import java.util.Map;@Controller@SessionAttributes("user")public class UserController { private final static Logger LOG = Logger.getLogger(UserController.class); @Autowired private UserService userService; @RequestMapping( "/") public ModelAndView toHome(Map<String,Object>map){ ModelAndView mav = new ModelAndView(); LOG.info("-----------------调用toHome方法-----------------"); if(map.get("user") == null){ mav.setViewName("redirect:/login"); }else{ mav.setViewName("welcome"); } return mav; } /** * 加载登陆页面 * @return */ @RequestMapping(value = "/login",method = RequestMethod.GET) public String getLogin(String msg){ LOG.info("-----------------------加载登陆页面---------------------"); return "login"; } /** * 登陆表单请求处理 * @param u 用户实体类 * @param map map共享域 * @return */ @RequestMapping(value = "/login",method = RequestMethod.POST) public String login(User u, Map<String,Object> map){ LOG.info(u); if(!u.getPassword().equals("")){ u.setPassword(MD5.encryptPassword(u.getPassword(),u.getAccount())); } LOG.info(u.getPassword()); if(!u.getAccount().equals("") && !u.getPassword().equals("")){ User user = userService.checkLoginInfo(u.getAccount(),u.getPassword()); LOG.info(user); if(user != null){ map.put("user",user); //存在用户,重定向到访问根目录 return "redirect:/"; }else{ map.put("msg","验证失败,请输入正确的用户名和密码!"); map.put("loginUser",u); return "login"; } } //将验证失败的消息放到共享域对象中,类似于:request.setAttribute ("msg", "验证失败!"); map.put("msg","请输入用户名或密码!"); map.put("loginUser",u); return "login"; } /** * 登出操作 * @param map * @param status * @return */ @RequestMapping("/logout") public String loginOut(Map<String,Object> map, SessionStatus status){ LOG.info("---------------退出登陆操作--------------------"); map.remove("user"); status.setComplete(); return "redirect:login"; } /** * 加载注册页面 * @return */ @RequestMapping(value = "/register",method = RequestMethod.GET) public String getRegister(){ return "register"; } /** * 注册用户信息 * @param u 表单提交的用户信息 * @param map map共享域 * @return */ @RequestMapping(value = "/register",method = RequestMethod.POST) public String register(User u,Map<String,Object> map){ LOG.info(u); //密码加密保存 if(!u.getPassword().equals("")){ u.setPassword(MD5.encryptPassword(u.getPassword(),u.getAccount())); } if(!u.getAccount().equals("") && !u.getUsername().equals("") && !u.getPassword().equals("")){ int result = userService.addUserInfo(u); if(result > 0){ LOG.info("注册结果信息:"+result); return "redirect:login"; } } map.put("msg","请输入完整的信息!"); map.put("registerUser",u); return "register"; }} 10. jsp页面编辑开发login.jsp12345678910111213141516171819202122232425<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="cn.zqiheng.pojo.User" %><% String msg = (String) request.getAttribute("msg"); User user = (User) request.getAttribute("loginUser");%><html><head> <title>登陆页面</title></head><body> <form action="/login" method="post"> 账&nbsp;&nbsp;&nbsp;号:<input type="text" name="account" placeholder="手机号或邮箱" value="<%= user == null ? "":user.getAccount()%>"/> <br/> 密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/> <br/><br/> <input type="submit" value="登陆"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="/register">没有账号?点击注册</a> </form> <h3 style="color: crimson"><%= msg == null? "" : msg %></h3></body></html> register.jsp123456789101112131415161718192021222324252627282930<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="cn.zqiheng.pojo.User" %><% String msg = (String) request.getAttribute("msg"); User user = (User) request.getAttribute("registerUser");%><html><head> <title>用户注册页面</title></head><body> <form action="/register" method="post"> 用户名:<input type="text" name="username" value="<%= user==null?"":user.getUsername()%>"/> <br/> 账&nbsp;&nbsp;&nbsp;号:<input type="text" name="account" placeholder="手机号或邮箱" value="<%= user==null?"":user.getAccount() %>"/> <br/> 密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"/> <br/> 性&nbsp;&nbsp;&nbsp;别:<input type="radio" value="true" name="gender" checked>男&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <input type="radio" value="false" name="gender">女<br/> <input type="submit" value="注册">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <input type="reset" value="重置">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="/login">登陆</a> </form> <h4 style="color: red"><%= msg == null ? "":msg%></h4></body></html> welcom.jsp1234567891011121314151617181920<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="cn.zqiheng.pojo.User" %><% User user = (User) request.getAttribute("user");%><html><head> <title>登陆页面</title></head><body> <%= user.getUsername() == null ? "":user.getUsername()%> 您好,欢迎登陆! <br/><br/><br/> <form action="/logout" method="post"> <input type="submit" value="退出登陆信息"/> </form></body></html> 11. 运行结果  启动Tomcat服务器,注册运行结果如下图。 注册页面 控制台输出 数据库结果   登陆结果如下图。 登陆页面 登陆成功页面   总结:这次在SSM框架整合过程中还是出现了一些错误。比较严重的一个错误是在开始搭建完成后,测试是否正确的时候,项目运行始终报错,也没有什么异常提醒,然后调试了一个小时左右,发现代码没有错,是我自己把 db.properties 数据库配置url中的地址写错了。真的是给自己挖了一个大坑😭。所以,在开发过程中,一定要仔细、认真。OK,SSM简单的整合过程就记录到这里了,后面有问题在补充。]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MyBatis学习总结(二)]]></title>
<url>%2F2018%2F10%2F18%2FMyBatis%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%EF%BC%88%E4%BA%8C%EF%BC%89%2F</url>
<content type="text"><![CDATA[  前面记录了MyBatis的一些背景、相关知识以及如何使用MyBatis进行数据库的CRUD操作,接下来就讲解Mapper XML文件和MyBatis自带的缓存机制。 1. Mapper XML  MyBaits 真正强大和有魅力的地方就在于它的映射语句。它将传统的JDBC操作数据库方式进一步封装,优雅而又有内涵的编程方式,省略了大量持久层Java逻辑代码,开发人员也只需要写好SQL映射文件即可。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 就是针对 SQL 构建的,并且比普通的方法做的更好。   Mapper XML文件有几个比较重要的标签(一个Mapper XML文件对应一个namespace【命名空间】)。 cache ==》给定命名空间的二级缓存配置。cache-ref ==》其它命名空间的缓存引用。resultMap ==》用来描述如和从数据库结果集中来加载对象。sql ==》可以被其它语句引用的可重用代码insert ==》映射插入语句select ==》映射查询语句update ==》映射修改语句delete ==》映射删除语句   以上的标签具体用法,请参考文档Mapper XML详解。这里我就不再重复,下面会用一些实例来讲解如何使用。 注:这里简单记录一下MyBatis的分步查询和延迟加载,后面代码中会详细写到。 (1)什么是分步查询和延迟加载?  在常用的的应用开发中,多数实体之间是相互关联的,我们需要查询一个实体的信息,可以获取对象的全部关联信息,但是有时候我们又想要在用的时候就可以查询出,不用的时候就不用查询。这个解决办法,MyBatis就使用 resultMap 标签和其子标签 association 来帮我们实现。 (2)使用步骤 在总配置文件中开启延迟加载 123<!-- 开启延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/> 需要在查询 select 映射语句中自定义返回类型 resultMap 并在其中使用 association 标签关联另一个查询方法。 2. MyBatis 缓存机制  对于需要频繁查询的数据,如果没有采取相应措施,我们则需要一直重复 连接数据库->发起SQL语句->获取结果集->关闭连接,这在数据量比较的大场景下这简直就是灾难,不仅耗时、而且还容易挂掉甚至发生不可预测的问题。因此,人们为了解决这个问题,就在中间加了数据缓存技术。缓存是计算机中的一块存储区域,把数据放入到存储区域中,读取速度会很快。 2.1 一级缓存  MyBatis 的一级缓存是 SqlSession 级别的缓存,默认开启,缓存数据在第一次查询时就放入本地缓存(Loacl Cache)中。   缓存有效期: 当不是同一个SqlSession对象时,不能跨session共享缓存数据。 当SqlSession对象调用了clearCache()方法后,会自动清除当前session的缓存。 当在两次相同的查询中,穿插了(新增、修改、删除)操作时,也会自动清理当前缓存。 当SqlSession对象关闭时,清除缓存。   一级缓存实现原理及性能分析: (1)基于PerpetualCache的HashMap本地存储。 (2)一级缓存通过简单的Map集合来实现,并没有对Map集合的大小、容量进行限制。 (3)一级缓存是一个粗粒度的缓存,没有办法去精确控制缓存中的数据是否存在时长、是否过期、以及更新缓存数据。 (4)多个SqlSession对象缓存中的数据无法共享。 2.2 二级缓存  MyBatis 的二级缓存是Mapper(namespace)级别的缓存,即一个XML映射文件对应一个二级缓存。二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域范围,并且可自定义存储源,如 Ehcache。   二级缓存需要我们自己手动配置和开启。 第一步:在MyBatis总配置文件中设置开启二级缓存 1<setting name="cacheEnabled" value="true"/> 第二步:在Mapper XML 文件中加入标签 第三步:实体类需要实现 Serializable 接口 第四步:查询语句 select 标签中加入属性 useCache=”true”   二级缓存原理。(这里引用一张图) 注: (1)和一级缓存一样,当执行了insert、update、delete等操作并commit提交后就会清空二级缓存区域。 (2)查询数据过程:二级缓存 ==》一级缓存 ==》数据库 2.3 第三方缓存  同样的,MyBatis开放了Cache接口,可以整合第三方缓存(如Redis、MemCached),这个后面在补充。 3. 代码实例  注意:其中写代码注释的比较多,是比较重要的知识点,可以仔细看下。 3.1 实体类  前面已经讲过了如何使用MyBaits一级项目搭建过程,这里不再赘述,直接在上一个Demo中新增两个实体类,学生 Student 和班级 Clazz(为了避免和class冲突),多个学生对应一个班级。 Clazz.java12345678910111213141516171819package cn.zqiheng.po;import java.io.Serializable;/** * @author Heng * 班级类 */public class Clazz implements Serializable { //主键id private int c_id; //名称 private String c_name; /** * 篇幅问题,这里省略了构造方法、set/get以及toString()方法。 */} Student.java12345678910111213141516171819202122package cn.zqiheng.po;import java.io.Serializable;/** * @author Heng * 学生类 */public class Student implements Serializable { //主键id private int id; //姓名 private String name; //性别 private boolean gender; //所属班级 private Clazz clazz; /** * 篇幅问题,这里省略了构造方法、set/get以及toString()方法。 */} 3.2 Dao层接口ClazzDao.java123456789101112131415161718192021222324package cn.zqiheng.dao;import cn.zqiheng.po.Clazz;import java.util.List;/** * @author Heng */public interface ClazzDao { /** * 获取所有班级列表 * @return */ List<Clazz> getAllClazz(); /** * 根据指定id获取班级信息 * @param id * @return */ Clazz getOneClazzById(int id);} StudentDao.java123456789101112131415161718192021222324252627282930package cn.zqiheng.dao;import cn.zqiheng.po.Student;import java.util.List;/** * @author Heng */public interface StudentDao{ /** * 获取所有学生列表信息,查询数据放入二级缓存 * @return */ List<Student> getAllStudent(); /** * 通过实现 association 标签来封装关联属性值 * @return */ List<Student> getAllStudent1(); /** * 此方法用来演示分步查询、延迟加载 * @param id * @return */ Student getOneStudentById(int id);} 3.3 Dao层映射Mapper XML文件  注:我所有的Mapper XML配置文件单独放在Maven项目的resources资源目录下的。 ClazzMapper.xml123456789101112131415161718<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "_//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="cn.zqiheng.dao.ClazzDao"> <select id="getAllClazz" resultType="cn.zqiheng.po.Clazz"> select * from clazz </select> <!-- 实现分步查询和延迟加载,在StudentMapper.xml中关联查询 --> <select id="getOneClazzById" resultType="cn.zqiheng.po.Clazz"> select * from clazz where c_id = #{c_id} </select> </mapper> 注:这里写了一些注释,可仔细阅读。 StudentMapper.xml1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "_//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="cn.zqiheng.dao.StudentDao"> <!-- 开启二级缓存 eviction:缓存回收策略 FIFO:先进先出 flushInterval:刷新间隔,可以被设置为任意的正整数,而且它们代表一个合理的毫秒 形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。 size:引用数目,可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目。 readerOnly:只读,只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 。这会慢一些,但是安全,因此默认是 false。 --> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!-- 使用<resultMap>标签:封装自定义返回数据 使用<association>标签,可以实现对关联类的属性封装、分步查询、延迟加载。。。 --> <resultMap id="studentMap" type="cn.zqiheng.po.Student"> <!-- <id>为主键标签的封装格式,其余都用<result>标签 --> <id property="id" column="id"/> <result property="name" column="name"/> <result property="gender" column="gender"/> <!--封装关联类的属性--> <result property="clazz.c_id" column="c_id"/> <result property="clazz.c_name" column="c_name"/> </resultMap> <!-- 想要让当前查询支持二级缓存,需要在标签中加入 useCache="true" , 如果不需要当前查询支持缓存 改为false即可,默认为true , flushCache="true" 该属性用于刷新缓存,将其设置为 true时,任何时候只要语句被调用,都会导致一级缓存和二级缓存都会被清空,默认值:false。 --> <select id="getAllStudent" resultMap="studentMap" useCache="true" flushCache="false"> select * from student s,clazz c where s.c_id = c.c_id </select> <!-- 使用association代替上面封装方法 --> <resultMap id="studentMap1" type="cn.zqiheng.po.Student"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="gender" column="gender"/> <!--使用association标签封装关联属性--> <association property="clazz" javaType="cn.zqiheng.po.Clazz"> <id property="c_id" column="c_id"/> <result property="c_name" column="c_name"/> </association> </resultMap> <select id="getAllStudent1" resultMap="studentMap1"> select * from student s,clazz c where s.c_id = c.c_id </select> <!--- 分布查询+延迟加载 1.首先查询当前学生,得到班级id 2.通过班级id查询出班级的信息 3.将查询的班级信息封装到员工对象中 --> <resultMap id="selectByStep" type="cn.zqiheng.po.Student"> <id property="id" column="id"/> <result property="name" column="name"/> <result property="gender" column="gender"/> <!-- 使用association来实现分步查询 select:查询方法(全类名+方法) cloumn:传入查询参数(即where 条件后的刷选条件) 注: 1.需要关联另一个实体查询方法并配置了相对应的Mapper XML映射语句。 2.运行此段代码,会发现向数据库发出了两条查询语句,如果想要实现后面的SQL语句需要的时候才加载, 则需要开启延迟加载,需在 mybatis 总的配置文件中进行配置,需要手动开启。(节省内存的开销和查询时间) --> <association property="clazz" javaType="cn.zqiheng.po.Clazz" select="cn.zqiheng.dao.ClazzDao.getOneClazzById" column="c_id"/> </resultMap> <select id="getOneStudentById" resultMap="selectByStep"> select * from student where id = #{id} </select></mapper> 3.4 Junit测试类StudentTest.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129import cn.zqiheng.dao.StudentDao;import cn.zqiheng.po.Student;import cn.zqiheng.tools.SqlSessionTool;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.List;public class StudentTest { /** * 获取所有学生信息 * resultMap 标签封装测似 */ @Test public void getAllStudentTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); StudentDao studentDao = session.getMapper(StudentDao.class);// List<Student> list = studentDao.getAllStudent(); List<Student> list = studentDao.getAllStudent1(); System.out.println(list); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 在mapper映射文件中,使用<resultMap>和<association>标签实现分步查询 * 分步查询,延迟加载测试 */ @Test public void getOneStudentByIdTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); StudentDao studentDao = session.getMapper(StudentDao.class); Student student = studentDao.getOneStudentById(2); /** * 第一步测试:只需获得学生姓名 */ System.out.println(student.getName()); System.out.println("---------------------------------------"); /** * 第二步测试:需要获得学生姓名和关联的班级名称 */ System.out.println(student.getClazz().getC_name()); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * Mybatis 一级缓存测试 */ @Test public void chcheTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); StudentDao studentDao = session.getMapper(StudentDao.class); List<Student> list = studentDao.getAllStudent(); System.out.println("第一次查询数据:"+list); /** * 手动清理缓存 */// session.clearCache();// session.close(); List<Student> list2 = studentDao.getAllStudent(); System.out.println("第二次查询数据:"+list2); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * Mybatis 二级缓存测试 */ @Test public void chcheTest2(){ SqlSession session1 = null; SqlSession session2 = null; try{ session1 = SqlSessionTool.getSession(); StudentDao studentDao = session1.getMapper(StudentDao.class); List<Student> list = studentDao.getAllStudent(); System.out.println("第一次查询数据:"+list); /** * 在新的SqlSession对象,使用二级缓存中的数据的时候, * 需要手动关闭前面的SqlSession对象,数据才会加载到二级缓存 * * 在测试的时候:第一次我们不关闭上一个会话、第二次关闭上一个会话 */ session1.close(); //新建一个会话,查询数据库 session2 = SqlSessionTool.getSession(); StudentDao studentDao2 = session2.getMapper(StudentDao.class); List<Student> list2 = studentDao2.getAllStudent(); System.out.println("第二次查询数据:"+list2); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session1); SqlSessionTool.close(session2); } }} 3.5 运行结果分析  (1)分步查询、延迟加载测试结果分析 运行结果图1(此时我们只需要获取学生的姓名) 运行结果图2(此时我们需要获取学生的姓名和学生所对应的班级名称)   总结:从上面两张结果运行图中可以看出,当我们只需要获取对象本身的属性时,不会去加载关联实体信息,只有在需要的时候才去查询,这样节约内存开销和减少了耗时操作。   (2)一级缓存测试结果分析 运行结果图1(此时我们在两次相同查询中间手动清理了缓存) 运行结果图2(此时我们在两次相同查询中间没有做任何操作(包括增删改)) 运行结果图3(此时我们在两次相同查询中间手动关闭session)   总结:从上面几次运行结果图可以看出,在两次相同的查询操作时(同一个session),MyBatis会优先在缓存中查找数据,如果没有,才会去数据库查找。如果手动关闭当前会话,则SqlSession会话结束,自动清理缓存。   (3)二级缓存测试结果分析 运行结果图1(此时我们在两次查询中间没有关闭上一个SqlSession对象会话) 运行结果图2(此时我们在两次查询中间关闭了上一个SqlSession对象会话)   总结:从上面运行的两次结果来看,MyBatis的二级缓存是可以被不同session共享的,并且在查询相同操作的时候,后面不需要在建立数据库连接。这为开发中数据查询速度带来了极大的方便、和高效。   MyBatis只是我们在操作数据库时的一种便利工具,如何去运用它,这就要看你想不想学好它。好了,基础学习知识就到这里了,后面在补充MyBatis的进阶版。]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Mybatis</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Mybatis</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MyBatis学习总结(一)]]></title>
<url>%2F2018%2F10%2F15%2FMyBatis%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%EF%BC%88%E4%B8%80%EF%BC%89%2F</url>
<content type="text"><![CDATA[  前言:MyBaits是现在企业开发使用较多的一种数据库操作工具,开始学的是JDBC和Hibernate,最近看各种面试题,发现MyBatis技术是用的比较多的,因此花了两天时间综合学习下。 1. 初识MyBatis  在介绍MyBatis技术前,我们先了解下MyBatis的背景,下面一段来自百度百科和官方文档的说明。   MyBatis 是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java对象)映射成数据库中的记录。 MyBatis 参考文档:http://www.mybatis.org/mybatis-3/zh/index.html 2. MyBatis 的优缺点  优点: 简单易学:本身就很小且简单。没有任何第三方依赖,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路黑客实现。 灵活:MyBatis不会对应用程序或者数据库的现有设计加强任何影响。SQL写在xml文件里,便于统一管理和优化。通过SQL基本上可以实现我们不使用数据访问框架可以实现的所有功能。 松耦合:解除了SQL与程序业务逻辑代码的耦合,通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。SQL和代码的分离,提高了可维护性。 提供映射标签,支持对象与数据库的ORM字段关系映射。 提供对象关系映射标签,支持对象关系的组建维护。 提供xml标签,支持编写动态SQL。   缺点: 需要大量编写SQL语句,尤其是在字段多、关联多表时,更是如此。 SQL语句依赖于数据库,导致数据库移植性差,针对不同数据库,需要编写不同SQL语言。 缓存机制不佳。 等等… 3. MyBatis 的功能架构  MyBatis 的功能架构大致可分为三层。 (1)API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层接收到调用请求就会调用数据处理层来完成具体的数据处理。 (2)数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。 (3)基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。 4. MyBatis 使用方法  说了那么多概念问题,接下来我们就来实际操作一下使用MyBatis进行数据库的CRUD操作。 4.1 第一步:导入jar包  在使用第三方支持工具开发的时候,我们都需要先导入相对应的开发支持.jar包。如果是普通项目,自己去maven的库里下载一个 mybatis-x.x.x.jar文件放置于classpath下即可(一般我们都下载最新版本)。如果使用的是maven来构建项目,则只需要导入pom.xml依赖即可。这里我建立的是一个普通的maven Java项目,下面是依赖文件代码。 pom.xml12345678910111213141516171819202122232425262728293031323334353637383940<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.zqiheng</groupId> <artifactId>MybatisDemo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <!-- junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies></project> 4.2 第二步:配置mybatis-config.xml文件、数据库连接信息dp.properties文件、和日志log4j.xml文件  MyBatis 的配置文件包含了影响MyBatis行为甚深的设置和属性信息。具体配置标签和属性,请参考:MyBatis的总配置文件xml配置 mybatis-config.xml1234567891011121314151617181920212223242526272829303132333435363738<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "_//mybatis.ort//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <!-- 导入配置文件,可以在当前页面使用 ${key} 来获取键所对应的value值 --> <properties resource="db.properties"/> <!-- settings 标签,全局mybatis属性配置 --> <settings> <setting name="logImpl" value="LOG4J"/> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> <!-- 开启二级缓存 --> <setting name="cacheEnabled" value="true"/> </settings> <!-- 数据库连接相关配置(封装了JDBC)--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${user}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- 注册SQL代码和映射定义信息 --> <mappers> <mapper resource="EmployeeMapper.xml"/> <mapper resource="ClazzMapper.xml"/> <mapper resource="StudentMapper.xml"/> </mappers></configuration> dp.properties1234driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/jdbctestuser=rootpassword=root log4j.xml12345678910111213141516171819<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"><log4j:configuration debug="true"> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="debug" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root></log4j:configuration> 4.3 第三步:自己配置一个工具类  虽然MyBatis给我们封装了底层JDBC连接数据库的步骤,但是需要我们从总配置文件中读取数据库连接配置信息,在连接数据库每次都要操作一边比较麻烦,因此我们自己简单的封装下。这里的SqlSession是一个比较重要的方法,大家可以参考SqlSession文档。 SqlSessoinTool.java123456789101112131415161718192021222324252627282930313233343536373839package cn.zqiheng.tools;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import java.io.Reader;public class SqlSessionTool { static SqlSession sqlSession; static SqlSessionFactory sqlSessionFactory; static{ try{ //获取当前Mybatis总配置文件的路径 String resource = "mybatis-config.xml"; //获取当前配置文件的输入流 Reader reader = Resources.getResourceAsReader(resource); //通过流对象,来创建一个SqlSessionFactory对象,数据库会话工厂 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); }catch (Exception e){ e.printStackTrace(); } } public static SqlSession getSession(){ //通过数据库会话工厂,开启跟数据库的一次会话 sqlSession = sqlSessionFactory.openSession(); return sqlSession; } public static void close(SqlSession session){ if(session != null){ session.close(); } }} 4.4 第四步:新建POJO类  项目结构图: Employee.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869package cn.zqiheng.po;/** * @author 恒 * 员工类 */public class Employee { //主键id private int id; //姓名 private String name; //性别(true:男、false:女) private boolean gender; //地址 private String address; public Employee() { } public Employee(int id, String name, boolean gender, String address) { this.id = id; this.name = name; this.gender = gender; this.address = address; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isGender() { return gender; } public void setGender(boolean gender) { this.gender = gender; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", address='" + address + '\'' + '}'; }} 4.5 第五步:定义Dao层接口  注意:这里是定义的一个接口,并没有实现类(直接交给MyBatis帮我们实现)。 EmployeeDao.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124package cn.zqiheng.dao;import cn.zqiheng.po.Employee;import org.apache.ibatis.annotations.Param;import java.util.List;/** * @author Heng * 员工数据操作层,定义Dao接口 */public interface EmployeeDao { /** * 1.单个参数Mybatis不会做特殊处理 * 使用 #{key} 取值 * 2.传入POJO对象 * #{对象的属性名} * 3.多个参数,Mybatis会做特殊处理,会把传入的参数 自动封装成Map类型 * Map 的key值就是从param1....param10....paramN * map.put("param1",values); * map.put("param2",values); * 在SQL语句中取参数 需使用 #{param1}...#{paramN} * * 注:手动指定Map的key值 * 在传参的方法前,使用 @Param("key") 注解 * 如: List<Employee> queryListByNameAndGender(@Param("name") String name,@Param("gender") boolean gender); * 4.直接传入Map * 5.Collection(集合)类型(List、Set)、数组 * Mybatis也会做特殊处理 * 如果是List或Set ==》 封装到Map中 * map.put("list",你传入的集合); * 如果时数组 * map.put("array",你传入的数组); */ /** * 查询所有员工集合 * @return */ List<Employee> getAllEmployee(); /** * 根据id 查询指定员工 * @param id * @return */ Employee getOneById(int id); /** * 插入员工 * @param employee * @return */ Integer insertEmployee(Employee employee); /** * 修改员工 * @param employee * @return */ Integer updateEmployee(Employee employee); /** * 更具id 删除指定员工 * @param id * @return */ Integer deleteEmployee(int id); /** * 多条件查询(跟姓名和性别查询) * @param name * @param gender * @return */ List<Employee> queryListByNameAndGender(@Param("name") String name,@Param("gender") boolean gender); /** * 动态SQL * 单条件.......if 标签使用 * @param employee * @return */ List<Employee> queryListByIf(Employee employee); /** * 动态SQL * 多条件......choose 标签使用 * @param employee * @return */ List<Employee> queryListByChoose(Employee employee); /** * 动态SQL * trim 标签使用 * @param employee * @return */ List<Employee> queryListByTrim(Employee employee); /** * 动态SQL * where 标签使用 * @param employee * @return */ List<Employee> queryListByWhere(Employee employee); /** * 批量删除 * @param list 传入List集合为参数 * @return */ Integer deleteByList(List<Employee> list); /** * 批量删除 * @param strs 传入数组为参数 * @return */ Integer deleteByArray(int[] strs);} 4.6 第六步:实现Mapper.xml映射文件  注:这里我将xxxMapper.xml文件统一放在resources资源目录下的,在MyBatis总配置文件中可直接识别,不需要加全路径。Mapper.xml配置文件里,一个接口对应一个namespace空间(要和POJO类映射也行,只是在调用CRUD方法时稍微麻烦点,这里统一映射Dao接口层)。 EmployeeMapper.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "_//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="cn.zqiheng.dao.EmployeeDao"> <!--添加员工信息--> <insert id="insertEmployee"> insert into employee values(#{id},#{name},#{address},#{gender}) </insert> <!--获取所有员工信息--> <select id="getAllEmployee" resultType="cn.zqiheng.po.Employee"> select * from employee </select> <!--根据Id查找员工信息--> <select id="getOneById" resultType="cn.zqiheng.po.Employee"> select * from employee where id = #{id} </select> <!--修改员工信息--> <update id="updateEmployee"> update employee set name = #{name},gender = #{gender},address = #{address} where id = #{id} </update> <!--删除指定员工信息--> <delete id="deleteEmployee"> delete from employee where id = #{id} </delete> <!--多条件查询,传入多个参数,String name,boolean gender 姓名采用模糊匹配--> <select id="queryListByNameAndGender" resultType="cn.zqiheng.po.Employee"> <!-- 多个参数,Mybatis会做特殊处理,会把传入的参数 自动封装成Map类型 Map 的key值就是从param1....param10....paramN map.put("param1",values); map.put("param2",values); 在SQL语句中取参数 需使用 #{param1}...#{paramN} 注:手动指定Map的key值 在传参的方法前,使用 @Param("key") 注解 如: List<Employee> queryListByNameAndGender(@Param("name") String name,@Param("gender") boolean gender); #{}默认采用预处理的方式去处理SQL语句 ${}是采用非预处理的方式去处理SQL语句 --> select * from employee where name like '${name}%' and gender = #{gender} </select> <!-- 动态SQL--> <!-- if --> <select id="queryListByIf" resultType="cn.zqiheng.po.Employee"> select * from employee where 1 = 1 <if test="name != null"> and name like '${name}%' </if> </select> <!-- choose 多条件 (多个条件中选择一个)--> <select id="queryListByChoose" resultType="cn.zqiheng.po.Employee"> select * from employee where 1 = 1 <choose> <when test="name != null"> and name like '${name}%' </when> <when test="address != null"> and address = #{address} </when> <otherwise> order by name </otherwise> </choose> </select> <!-- trim 处理动态SQL语句拼接时 1=1 的问题--> <select id="queryListByTrim" resultType="cn.zqiheng.po.Employee"> select * from employee <trim prefix="where" prefixOverrides="AND | OR"> <if test="name != null"> name like '${name}%' </if> <if test="address != null"> and address = #{address} </if> </trim> </select> <!--where 标签使用--> <select id="queryListByWhere" resultType="cn.zqiheng.po.Employee"> select * from employee <where> <if test="name != null"> name like '${name}%' </if> <if test="address != null"> and address = #{address} </if> </where> </select> <!-- 使用List集合作为入参, 使用<foreach/>标签遍历传入的List --> <delete id="deleteByList" > delete from employee where id in <foreach collection="list" open="(" separator="," close=")" item="i"> #{i} </foreach> </delete> <!-- 使用数组作为入参, 使用<foreach/>标签遍历传入的array --> <delete id="deleteByArray" > delete from employee where id in <foreach collection="array" open="(" separator="," close=")" item="i"> #{i} </foreach> </delete> </mapper> 4.7 第七步:编写测试类  这里我采用的是junit方法测试、日志输出采用log4j,都需要导入对应的pom依赖,前面我们已经导入。 EmployeeTest.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205import cn.zqiheng.dao.EmployeeDao;import cn.zqiheng.po.Employee;import cn.zqiheng.tools.SqlSessionTool;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.ArrayList;import java.util.List;public class EmployeeTest { /** * 查询数据库中所有数据 * @throws Exception */ @Test public void selectAllTest() throws Exception{ SqlSession session = null; try{ //获取sessin会话 session = SqlSessionTool.getSession(); //通过代理获取EmployeeDao对象 EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); List<Employee> list= employeeDao.getAllEmployee(); System.out.println(list); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 向数据库中插入数据 * @throws Exception */ @Test public void selectTest1() throws Exception{ SqlSession session = null; try{ session = SqlSessionTool.getSession(); //创建接口对象。这个是sessiono对象通过动态代理自动帮我们创建的。Proxy代理 EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); Employee employee = new Employee(4,"李四",false,"北京"); //调用EmployeeDao方法,插入数据 int i = employeeDao.insertEmployee(employee); System.out.println(i); //提交事务 session.commit(); }catch(Exception e){ e.printStackTrace(); }finally { //关闭会话 SqlSessionTool.close(session); } } /** * 根据id查询指定员工 */ @Test public void selectOneTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); Employee employee = employeeDao.getOneById(1); System.out.println(employee); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 修改员工信息 */ @Test public void updateEmployeeTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); Employee employee = new Employee(4,"Tom",true,"Japan"); employeeDao.updateEmployee(employee); session.commit(); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 根据指定id 删除一个员工信息 */ @Test public void deleteEmployeeTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); employeeDao.deleteEmployee(3); session.commit(); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 多条件查询测试 * 通过姓名模糊匹配和指定性别查询员工集合 */ @Test public void queryByNameAndGenderTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); List<Employee> list = employeeDao.queryListByNameAndGender("张",false); System.out.println(list); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 动态语句查询测试 */ @Test public void queryListTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); Employee employee = new Employee(); employee.setName("张"); employee.setAddress("成都");// List list= employeeDao.queryListByIf(employee);// List list= employeeDao.queryListByChoose(employee);// List list= employeeDao.queryListByTrim(employee); List list= employeeDao.queryListByWhere(employee); System.out.println(list); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } } /** * 批量删除操作测试 * 使用list或数组作为参数传入 */ @Test public void deleteByListOrArrayTest(){ SqlSession session = null; try{ session = SqlSessionTool.getSession(); EmployeeDao employeeDao = session.getMapper(EmployeeDao.class); /** * 通过List作为入参,批量删除数据 */ List list = new ArrayList(); list.add(5); list.add(6); int i = employeeDao.deleteByList(list); System.out.println("i = "+i); /** * 通过数组作为入参,批量删除数据 */ int[] arrays = {7,8,9}; int j = employeeDao.deleteByArray(arrays); System.out.println("j = "+j); session.commit(); }catch (Exception e){ e.printStackTrace(); }finally { SqlSessionTool.close(session); } }}   部分测试结果如下图:   上面各段代码简单的实现了MyBatis对数据库的CRUD操作,并介绍了相关用到的比较重要的方法和xml标签,想要深入学习,请参考上面给出的MyBatis参考文档。   这篇文章就简单的记录到这里,下篇文章将记录MyBatis比较重要的几个标签以及自定义返回数据封装和MyBatis缓存技术介绍。]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Mybatis</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Mybatis</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Struts框架技术(一)]]></title>
<url>%2F2018%2F10%2F11%2FStruts%E6%A1%86%E6%9E%B6%E6%8A%80%E6%9C%AF%2F</url>
<content type="text"><![CDATA[  前言:Struts是Apache软件基金会赞助的一个开源项目,它最初是Jakarta项目中的一个子项目。Struts2是以Webwork设计思想为核心,吸收原Struts的优点而形成的,旨在帮助程序员更为方便地运用MVC模式来开发JavaEE应用。总之,Struts就是封装了Servlet中的各种对象以及请求/响应操作,减少对Web容器的依赖。 1. MVC 基本思想1.1 MVC思想概述  MVC并不是Java语言所特有的设计思想,也并不是Web应用所特有的思想,它是所有面向对象程序设计语言都应该遵守的规范。   MVC思想将一个应用分成三个基本部分:Model(模型)、View(视图)、Controller(控制器),这三个部分以最少的耦合来协同工作,从而提高应用的可扩展性和可维护性。   在经典的MVC模式中,事件由控制器处理,控制器根据事件的类型改变模型或试图,反之亦然。具体地说,每个模型对应一系列的试图列表,这种对应关系通常采用注册来完成,即:把多个试图注册到同一个模型,当模型发生改变时,模型向所有注册过的视图发送通知,接下来,视图从对应的模型中获得信息,然后完成视图显示的更新。从设计模式的角度看,MVC思想非常类似于观察者模式,但与观察者模式存在少许差别:观察者模式下观察者和被观察者可以是两个互相对等的对象,但与MVC思想而言,被观察者往往只是单纯的数据体,而观察者则是单纯的视图页面。 相对于早期的MVC思想,Web模式下的MVC思想则又存在一些变化,对于一个普通应用程序,可以将视图注册给模型,当模型数据发生变化时,即时通知视图页面发生改变;而对于Web应用而言,即使将多个JSP页面注册给一个模型,当模型发生变化时,模型无法主动发送消息给JSP页面(因为Web应用都是基于请求/响应模式的),只有当用户请求浏览该页面时,控制器才负责调用模型数据来更新JSP页面。   下图为经典MVC开发模式架构图。 模型:用于封装与应用程序业务逻辑相关的数据及对数据的处理方法。 视图:视图是用户看到并与之交互的界面。 控制器:控制器起到不同层次间的组织作用,用于控制应用程序的流程。它处理事件并做出响应,“事件”包括用户的行为和数据模型上的改变。   下图为Web模式下MVC开发模式架构图。 MVC特点。 多个视图可以对应一个模型。按MVC设计模式,一个模型对应多个视图,可以减少代码的复制及代码的维护量,一旦模型发生改变,也易于维护。 模型返回的数据与显示逻辑分离。模型数据可以应用任何的显示技术,例如,使用JSP页面、Velocity模板或者直接产生Excel文档等。 应用被分隔为三层,降低了各层之间的耦合,提供了应用的可扩展性。 概念明确,层次清晰,编写方便。 MVC更符合软件工程化管理的精神。不同的层各司其职,每一层的组件具有相同的特征,有利于通过工程化和工具化产生管理程序代码。 总结: MVC 是一种开发模式。 Web三层架构(Controller、Service、Dao)是一种开发习惯,是前人留下的经验的传承。 两种方式在开发Web应用中往往需要结合使用。 1.2 MVC实现方式  传统的JavaEE开发采用JSP+Servlet+JavaBean的方式来实现MVC,但它有一个缺陷:程序员在编写程序时必须继承HttpServlet类、覆盖doGet()和doPost()方法,严格遵守Servlet代码规范编写程序。形如: LoginServlet.java12345678910111213package com.heng.srvlet;import java.io.*;import javax.servlet.*;import javax.servlet.http.*;public class LoginServlet extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ //... } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException{ doGet(request,response); }}   以上这些繁琐的代码与程序本身要实现的功能无关,仅是Java语言Servlet编程接口(API)的一部分。在开发中一旦暴露Servlet API就会大大增加编程的难度,为了屏蔽这种不必要的复杂性,减少用MVC开发Java EE的工作量,人们发明了Struts2框架。用Struts2实现的MVC系统与传统的用Servlet编写的MVC系统相比,两者在结构上的区别如下图所示(注:此图为引用Java EE使用教程(第二版)中MVC系统的两种实现方式)。   由上图可见,Struts2的解决方案是:使用Struts2框架代替了原Servlet部分作为控制器,而业务逻辑处理的功能由用户自定义编写Action去实现,与Struts2的控制器核心相分离。这样就进一步降低了系统中各部分组件的耦合度,也大大降低了编程难度。 2. 简单的Struts2项目开发过程2.1 新建JavaWeb项目,加载Struts2的包依赖  传统的Java EE项目Struts2框架开发,需要自己手动导入Struts2的包支持依赖。大部分的时候,使用Struts2的Java EE应用并不需要用到Struts2的全部特性,开发Struts2程序只需要用到lib下的9个jar包,包括: (1)传统的Struts2的5个基本类库。 struts2-core-XXX.jar:Struts2框架核心类库 xwork-core-XXX.jar:Xwork项目,Struts2就是在它的基础上构建的 ognl-XXX.jar:OGNL表达式语言 commons-logging-XXX.jar:用于能够插入任何其他的日志系统 freemarker-XXX.jar:所有的UI标记模板 (2)附加的4个库。 commons-io-XXX.jar commons-lang-XXX.jar javassist-XXX.GA.jar commons-fileupload-XXX.jar   注:本次实例程序开发采用的是Idea+Maven构建项目,因此我们不需要手动导包,在pom.xml中加入如下依赖即可。 pom.xml1234567<dependencies> <dependency> <groupId>org.apache.struts</groupId> <artifactId>struts2-core</artifactId> <version>2.5.13</version> </dependency></dependencies> 2.2 在web.xml中配置Struts2核心Filter  重点:由于Web应用是基于请求/响应架构的应用,所以不管哪个MVC Web框架,都需要在web.xml中配置该框架的核心Servlet或Filter,这样才可以让该框架加入Web应用中。   在web.xml中加入下面代码来配置Struts2的核心过滤器(Filter)。 web.xml12345678910<!--定义Struts2的核心Filter--><filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><!--让Struts2的核心Filter拦截所有请求--><filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern></filter-mapping> 2.3 实现控制器Action  基于Struts2框架的Java EE应用程序使用自定义的Action(控制器)来处理深层业务逻辑,完成用户想要完成的功能。本例定义名为“login”的控制器,判断登陆用户和密码的正确性。 LoginAction.java1234567891011121314151617181920212223242526272829303132333435package cn.zqiheng.action;public class LoginAction { /** * 属性驱动传值 */ private String username; private String password; public String execute() throws Exception{ System.out.println("用户名:"+username+" 密码:"+password); if(username.equals("heng") && password.equals("123")){ return "success"; } return "error"; } 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; }} 2.4 配置struts.xml  在编写好了Action(控制器)的代码之后,还需要进行配置才能让Struts2识别Action。在resources中新建文件struts.xml。 struts.xml1234567891011<struts> <package name="user" namespace="/" extends="struts-default"> <action name="login" class="cn.zqiheng.action.LoginAction"> <result name="success">/welcome.jsp</result> <result name="error">/error.jsp</result> </action> </package> </struts> 2.5 编写JSP页面index.jsp123456789101112<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><body><form action="login" method="post"> 用户名:<input type="text" name="username"><br> 密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password"><br> <input type="submit" value="提交"></form></body></html> welcome.jsp123456789<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>登陆成功界面</title></head><body><h1>您好!欢迎使用本系统。</h1></body></html> error.jsp123456789<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>登陆失败</title></head><body><h1>登陆失败</h1></body></html> 2.6 配置Tomcat服务器并启动应用  项目结构如下图所示。   项目运行结果如下图所示。]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Struts2</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Struts2</tag>
</tags>
</entry>
<entry>
<title><![CDATA[JDBC]]></title>
<url>%2F2018%2F10%2F01%2FJDBC%2F</url>
<content type="text"><![CDATA[  前言:JDBC的全称为Java DataBase Connectivity,是一套面向对象的应用程序接口(API),制定了统一的访问各类关系数据库的标准接口,为各个数据库厂商提供了标准接口的实现。通过JDBC技术,开发人员可以用纯Java语言和标准的SQL语句编写完整的数据库应用程序,并且真正地实现了软件地跨平台性。在JDBC技术问世之前,各家数据库厂商执行各自地一套API,使得开发人员访问数据库非常麻烦,特别是在更换数据库时,需要修改大量代码,十分不方便。JDBC地发布获得了巨大地成功,很快就成为了Java访问数据库地标准,并且获得了几乎所有数据库厂商地支持。 1. JDBC简介  JDBC是一种底层API,在访问数据库时需要在业务逻辑中直接嵌入SQL语句。由于SQL语句是面向关系的,依赖于关系模型,所以JDBC传承了简单直接地优点。需要注意的是,JDBC不能直接访问数据库,必须依赖于数据库厂商提供的JDBC驱动程序,通常情况下使用JDBC完成以下操作。 同数据库建立连接; 向数据库发送SQL语句; 处理从数据库返回的结果。   JDBC的优点。 JDBC与ODBC十分相似,便于软件开发人员理解; JDBC开发简单,可以使开发人员专注于业务逻辑的开发; JDBC API是面向对象的; JDBC支持多种关系数据库。   JDBC的缺点。 通过JDBC访问数据库时速度将受到一定影响; 虽然JDBC API时面向对象的,但通过JDBC访问数据库依然是面向关系的; JDBC提供了对不同厂家的产品的支持,这将对数据源带来影响。 2. JDBC中常用的接口  JDBC提供了许多接口和类,通过这些类和接口,可以实现与各种数据库的连接和操作。 2.1 驱动程序接口Driver  每种数据库的驱动程序都应该提供一个实现java.sql.Driver接口的类,简称Driver类,在加载Driver类时,应该创建自己的实例并向java.sql.DriverManager类注册该实例。通常情况下通过java.lang.Class类的静态方法forName(String className),加载要连接数据库的Driver类,该方法的入口参数为要加载Driver类的完整包名。成功加载后,会将Driver类的实例注册到DriverManager类中,如果加载失败,将抛出ClassNotFoundException异常,即未找到指定Driver类的异常。 2.2 驱动程序管理器DriverManager  java.sql.DriverManager类负责管理JDBC驱动程序的基本服务,是JDBC的管理层,作用于用户和驱动程之间,负责跟踪可用的驱动程序,并在数据库和驱动程序之间建立连接。另外,DriverManager类也处理诸如驱动程序登陆时间限制及登陆和跟踪消息的显示等工作。成功加载Driver类并在DriverManager类中注册后,DriverManager类即可用来建立数据库连接。   当调用DriverManager类的getConnection()方法请求建立数据库连接时,DriverManager类将试图定位一个适当的Driver类,并检查定位到Driver类是否可以建立连接。如果可以,则建立连接并返回,如果不可以,则抛出SQLException异常。DriverManager类提供的常用方法如下表所示。 方法名称 功能描述 getConnection(String url,String user,String password) 静态方法,用来获得数据库连接,有3个入口参数,以此为要连接数据库的URL、用户名和密码,返回值类型为java.sql.Connection setLoginTimeout(int seconds) 静态方法,用来设置每次等待建立数据库连接的最长时间 setLogWriter(java.io.PringWriter out) 静态方法,用来设置日志的输出对象 println(String message) 静态方法,用来输出指定消息到当前的JDBC日志流 2.3 数据库连接接口Connection  java.sql.Connection接口负责与特定数据库的连接,在连接的上下文中可以执行SQL语句并返回结果,还可以通过getMetaData()方法获得由数据库提供的相关信息,例如数据表、存储过程和连接功能等信息。Connection接口提供的常用方法如下表所示。 方法名称 功能描述 createStatement() 创建并返回一个Statement实例,通常在执行无参数的SQL语句时创建该实例 prepareStatement() 创建并返回一个PreparedStatement实例,通常在执行包含参数的SQL语句时创建该实例,并对SQL语句进行了预编译处理 prepareCall() 创建并返回一个CallableStatement实例,通常在调用数据库存储过程时创建该实例 setAutoCommit() 设置当前Connection实例的自动提交模式,默认为true,即自动将更改同步到数据库中,如果设置为false,需要通过执行commit()或rollback()方法手动将更改同步到数据库中 getAutoCommit() 查看当前的Connection实例是否处于自动提交模式,如果是则返回true,否则返回false setSavepoint() 在当前事务中创建并返回一个Savepoint实例,前提条件是当前的Connection实例不能处于自动提交模式,否则将抛出异常 releaseSavepoint() 从当前事务中移除指定的Savepoint实例 setReadOnly() 设置当前Connection实例的读取模式,默认为非只读模式,不能在事务当中执行该操作,否则将抛出异常,有一个boolean型的入口参数,设为true则表示开启只读模式,设为false则表示关闭只读模式 isReadOnly() 查看当前的Connection实例对象是否为只读模式,如果是则返回true,否则返回false isClosed() 查看当前的Connection实例是否被关闭,如果被关闭则返回true,否则返回false commit() 将从上一次提交或回滚以来进行的所有更改同步到数据库,并释放Connection实例当前拥有的所有数据库锁定 rollback() 取消当前事务中的所有更改,并释放当前Connection实例拥有的所有数据库锁定;该方法只能在非自动提交模式下使用,如果在自动提交模式下执行该方法,将抛出异常;有一个入口参数为Savepoint实例的重载方法,用来取消Savepoint实例之后的所有更改,并释放对应的数据库锁定 close() 立即释放Connection实例占用的数据库和JDBC资源,即关闭数据库连接 2.4 执行SQL语句接口Statement  java.sql.Statement接口用来执行静态的SQL语句,并返回执行结果。例如,对于insert、update和delete语句,调用executeUpdate(String sql)方法,而select语句则调用executeQuery(String sql)方法,并返回一个永远不能为Null的ResultSet实例。Statement接口提供的常用方法如下表所示。 方法名称 功能描述 executeQuery(String sql) 执行指定的静态SELECT语句,并返回一个不能为null的ResultSet实例 executeUpdate(String sql) 指定指定的静态INSERT、UPDATE或DELETE语句,并返回一个int型数值,为同步更新记录条数 clearBatch() 清除位于Batch的所有SQL语句,如果驱动程序不支持批量处理将抛出异常 addBatch(String sql) 将指定的SQL命令添加到Batch中,String型入口参数通常为静态的INSERT或UPDATE语句,如果驱动程序不支持批量处理将抛出异常 executeBatch() 执行Batch中的所有SQL语句,如果全部执行成功,则返回由更新计数组成的数组,数组元素的排序与SQL语句的添加顺序对应,数组的元素有以下几种情况:1.大于或等于零的数,说明SQL语句执行成功,为影响数据库中行数的更新计数;2. -2,说明SQL语句执行成功,但未得到受影响的行数;3. -3,说明SQL语句执行失败,仅当执行失败后继续执行后面的SQL语句时出项。如果驱动程序不支持批量,或者未能成功执行Batch中的SQL语句之一,将抛出异常 close() 立即释放Statement实例占用的数据库和JDBC资源,即关闭Statement实例 2.5 执行动态SQL语句接口PreparedStatement  java.sql.PreparedStatement接口继承于Statement接口,是Statement接口的扩展,用来执行动态的SQL语句,即包含参数的SQL语句。通过PreparedStatement实例执行的动态SQL语句,将被预编译并保存到PreparedStatement实例中,从而可以反复并且高效地执行该SQL语句。   需要注意的是,在通过setXXX()方法为SQL语句中的参数赋值时,必须通过与输入参数的已定义SQL类型兼容的方法,也可以通过setObject()方法设置各种类型的输入参数。PreparedStatement的使用方法如下。 123456789101112//...String sql = "select * from table_name where id>? and (name=? or name=?)";PreparedStatement ps = connection.prepareStatement(sql);ps.setInt(1,1);ps.setString(2,"zhangsan");ps.setObject(3,"lisi");ResultSet rs = ps.executeQuery();//...   PreparedStatement接口提供的常用方法如下表所示。 方法名称 功能描述 executeQuery() 执行前面包含参数的动态SELECT语句,并返回一个不能为null的ResultSertraline实例 executeUpdate() 执行前面包含参数的动态INEERT、UPDATE或DELECT语句,并返回一个int整数值,为同步更新记录的条数 clearParameters() 清除当前所有参数的值 setXXX() 为指定参数设置XXX型值 close() 立即释放Statement实例所占用的数据库和JDBC资源,即关闭Statement实例 2.6 执行存储过程接口CallableStatement  java.sql.CallableStatement接口继承于PreparedStatement接口,是PreparedStatement接口的扩展,用来执行SQL的存储过程。   JDBC API定义了一套存储过程SQL转义语法,该语法允许对所有RDBMS通过标准方式调用存储过程。该语法定义了两种形式,分别是包含结果参数和不包含结果参数。如果使用结果参数,则必须将其注册为OUT型参数,参数是根据定义位置按顺序引用的,第一个参数索引为1。   为参数赋值的方法使用从PreparedStatement中继承来的setXXX()方法。在执行存储过程之前,必须注册所有OUT参数的类型;它们的值是在执行后通过getXXX()方法检索的。   CallableStatement可以返回一个或多个ResultSet实例。处理多个ResuletSet对象的方法是从Statement中继承来的。 2.7 访问结果集接口ResultSet  java.sql.ResultSet接口类似于一个数据库,通过该接口的实例可以获得检索结果集,以及对数据表的相关信息,例如列名和类型等,ResultSet实例通过执行查询数据库的语句生成。   ResultSet实例具有指向其当前数据行的指针。最初,指针指向第一行记录的前方,通过next()方法可以将指针移动到下一行,因为该方法在没有下一行时将返回false,所以可以通过while循环来迭代ResultSet结果集。在默认的情况下ResultSet对象不可以更新,只有一个可以向前移动的指针,因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。如果需要,可以生成可滚动和可更新的ResutlSet对象。 ResultSet接口提供了从当前行检索不同类型列值的getXXX()方法,均有两个重载方法,可以通过列的索引编号或列的名称检索,通过列的索引编号较为高效,列的索引编号从1开始。对于不同的getXXX()方法,JDBC驱动程序尝试将基础数据转换为与getXXX()方法相应的Java类型,并返回适当的Java类型的值。   ResultSet接口提供的常用方法如下表所示。 方法名称 功能描述 first() 移动指针到第一行;如果结果集为空则返回false,否则返回true;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 last() 移动指针到最后一行;如果结果集为空则返回false,否则返回true;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 previous() 移动指针到上一行;如果存在上一行则返回true,否则返回false;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 next() 移动指针到下一行;指针最初位于第一行之前,第一次调用该方法将移动到第一行;如果存在下一行则返回true,否则返回false beforeFirst() 移动指针到ResultSet实例的开头,即第一行之前;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 afterLast() 移动指针到ResultSet实例的末尾,即最后一行之后;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 absolute() 移动指针到指定行;有一个int型入口参数,正数表示从前向后编号,负数表示从后向前编号,编号均从1开始;如果存在指定行则返回true,否则返回false;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 relative() 移动指针到相对于当前行的指定行;有一个int型入口参数,正数表示向后移动,负数表示向前移动,视当前行为0;如果存在指定行则返回true,否则返回false;如果结果集类型为TYPE_FORWARD_ONLY将抛出异常 getRow() 查看当前行的索引编号;索引编号从1开始,如果位于有效记录行上则返回一个int型索引编号,否则返回0 findColumn() 查看指定列名的索引编号;该方法有一个String型的入口参数,为要查找列的名称,如果包含指定列,则返回int型索引编号,否则将抛出异常 isBeforeFirst() 查看指针是否位于ResultSet实例的开头,即第一行之前,如果是则返回true,否则返回false isAfterLast() 查看指针是否位于ResultSet实例的末尾,即最后一行之后,如果是则返回true,否则返回false isFirst() 查看指针是否位于ResultSet实例的第一行,如果是则返回true,否则返回false isLast() 查看指针是否位于ResultSet实例的最后一行,如果是则返回true,否则返回false close() 立即释放ResultSet实例占用的数据库和JDBC资源,当关闭所属的Statement实例时也将执行此操作 3. JDBC访问数据库过程  在对数据库进行操作时,首先需要连接数据库,在使用JDBC连接数据库大致可分为加载JDBC驱动程序、创建Connection对象的实例、创建Statement对象、执行SQL语句、获得查询结果和关闭连接等6个步骤。 下面一个例子来演示JDBC连接MYSQL数据库并获得查询结果集。 JDBCTest.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081package com.heng.jdbc;import java.sql.Connection;import java.sql.DriverManager;import java.sql.ResultSet;import java.sql.Statement;public class JDBCTest { public static void main(String[] args) { /** * 声明Connection、Statement、ResultSet对象 */ Connection connection = null; Statement statement = null; ResultSet resultSet = null; /** * 数据库连接配置 */ String url = "jdbc:mysql://localhost:3306/jdbctest?useUnicode=true&amp;characterEncoding=utf-8"; String user = "root"; String password = "root"; try{ /** * 第一步:加载Mysql驱动类(需要引入MySQL支持包) */ Class.forName("com.mysql.jdbc.Driver"); /** * 第二步:创建数据库连接 */ connection = DriverManager.getConnection(url,user,password); /** * 第三步:创建Statement对象 */ statement = connection.createStatement(); /** * 第四步:执行SQL语句(数据库已建好,并有表和数据) */ String sql= "SELECT * FROM user ORDER BY id"; resultSet = statement.executeQuery(sql); /** * 第五步:获得查询结果 */ System.out.println("id\t\t姓名\t\t出生日期\t\t\t\t电话\t\t性别"); while (resultSet.next()){ int id = resultSet.getInt(1); String username = resultSet.getString(2); String date = resultSet.getString(3); String phone = resultSet.getString(4); boolean age = resultSet.getBoolean(5); System.out.println(id+"\t\t"+username+"\t\t"+date+"\t\t"+phone+"\t\t"+age); } }catch (Exception e){ e.printStackTrace(); }finally { try{ /** * 第六步:关闭资源 */ if(resultSet != null){ resultSet.close(); } if(statement != null){ statement.close(); } if(connection != null){ connection.close(); } }catch (Exception e){ e.printStackTrace(); } } }}   运行结果: 技巧:通常将负责加载驱动的代码放在static块中,这样做的好处是只有static块所在的类第一次被加载时才加载数据库驱动,避免重复加载驱动程序,浪费计算机资源。 说明:采用上面的顺序关闭的原因在于Connection是一个接口,close()方法的实现方式可能多种多样。如果是通过DriverManager类的getConnection()方法得到的Connection实例,在调用close()方法关闭Connection实例时会同时关闭Statement实例和ResultSet实例。但是通常情况下需要采用数据库连接池,在调用通过连接池得到的Connection实例的close()方法时,Connection实例可能并没有被释放,而是被放回到了连接池中,又被其它连接调用,在这种情况下如果不手动关闭Statement实例和ResultSet实例,它们在Connection中可能会越来越多,虽然JVM的垃圾回收机制会定时清理缓存,但是如果清理的不及时,当数据库连接达到一定数量时,将严重影响数据库和计算机的运行速度,甚至导致软件所系统瘫痪。 4. 数据库操作技术  在开发Web应用程序时,经常需要对数据库进行操作,最常用的数据库操作技术,包括向数据库查询、添加、修改或删除数据库中的数据,这些操作既可以通过静态的SQL语句实现,也可以通过动态的SQL语句实现,还可以通过存储过程实现,具体采用的实现方式要根据实际情况而定。 4.1 查询操作  JDBC提供了两种实现数据库查询的啊方法,一种是通过Statement对象执行静态的SQL语句实现;另一种是通过PreparedStatement对象执行动态的SQL语句实现。由于PreparedStatement类是Statement类的扩展,一个PreparedStatement对象包含一个预编译的SQL语句,该SQL语句可能包含一个或多个参数,这样应用程序可以动态地为其赋值,所以PreparedStatement对象执行地速度比Statement对象快。因此在执行较多地SQL语句时,建议使用PreparedStatement对象。   下面通过两个实例分别应用这两种方式实现数据查询。 查询操作实例1 12345678910/*** 此处省略了创建数据库连接地代码*/Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("select * from tb_user where name = 'zhangsan'");while(rs.next()){ System.out.println("用户名:"+rs.getString(2)+" 密码:"+rs.getString(3));}//关闭连接//... 查询操作实例2 1234567891011/*** 此处省略了创建数据库连接地代码*/PreparedStatement pStmt = conn.prepareStatement("select * from tb_user where name=?");pStmt.setString(1,"zhangsan");ResultSet rs = pStmt.executeQuery();while(rs.next()){ System.out.println("用户名:"+rs.getString(2)+" 密码:"+rs.getString(3));}//关闭连接//... 4.2 添加操作  与查询操作相同,JDBC中也提供了两种实现数据添加操作地方法,一种是通过Statement对象执行静态地SQL语句实现;另一种是通过PerparedStatement对象执行动态地SQL语句实现。 应用Statement对象向数据表tb_user中添加数据 1234//...Statement stmt = conn.createStatement();int rtn = stmt.executeUpdate("insert into tb_user(name,password) values('heng','123')");//... 应用PreparedStatement对象向数据表tb_user中添加数据 123456//...PreparedStatement pStmt = conn.prepareStatement("insert into tb_user(name,passwrod) values(?,?)");pStmt.setString(1,"heng");pStmt.setString(2,"123");int rtn = pStmt.executeUpdate();//.. 4.3 修改操作  与添加操作相同,JDBC中同样提供了两种实现数据修改操作地方法,一种是通过Statement对象执行静态地SQL语句实现;另一种是通过PerparedStatement对象执行动态地SQL语句实现。 应用Statement对象修改数据表中username字段值为”heng”的记录 1234//...Statement stmt = conn.createStatement();int rtn = stmt.executeUpdate("update tb_user set username='zhangsan',password='111' where username='heng'");//... 应用PreparedStatement对象修改数据表中username字段值为”heng”的记录 1234567//...PreparedStatement pStmt = conn.prepareStatement("update tb_user set username=?,passwrod=? where username=?");pStmt.setString(1,"zhangsan");pStmt.setString(2,"111");pStmt.setString(3,"heng");int rtn = pStmt.executeUpdate();//... 4.4 删除操作  实现数据库删除操作也可以通过两种方式实现。一种是通过Statement对象执行静态地SQL语句实现;另一种是通过PerparedStatement对象执行动态地SQL语句实现。 应用Statement对象从数据表中删除username字段值为”zhangsan”的数据 1234//...Statement stmt = conn.createStatement();int rtn = stmt.executeUpdate("delete from tb_user where username='zhangsan'");//... 应用PreparedStatement对象从数据表中删除username字段值为”zhangsan”的数据 12345//...PreparedStatement pStmt = conn.prepareStatemnt("delete from tb_user where username=?");pStmt.setString(1,"zhangsan");int rtn = pStmt.executeUpdate();//... 5. 连接池技术  通常情况下,在每次访问数据库之前都要先建立与数据库的连接,这将消耗一定的资源,并延长了访问数据库的时间,如果是访问量相对较低的系统还可以,如果访问量较高,将严重影响系统的性能。为了解决这一问题,引入了连接池的概念。 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出,对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标,数据库连接池正式针对这个问题提出来的,数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。 5.1 连接池简介  所谓连接池,就是预先建立好一定数量的数据库连接,模拟存放在一个连接池中,由连接池负责对这些数据库连接进行管理。这样,当需要访问数据库时,就可以通过已经建立好的连接访问数据库了,从而避免了每次在访问数据库之前建立数据库连接的开销。   连接池还解决了数据库连接数量限制的问题。由于数据库能够承受的连接数量是有限的,当达到一定程度时,数据库的性能就会下降,甚至崩溃,而池化管理机制,通过有效地使用和调度这些连接池中的连接,则解决了这个问题。   数据库连接池的具体实施办法如下。 1)预先创建一定数量的连接,存放在连接池中; 2)当程序请求一个连接时,连接池是为该请求分配一个空闲连接,而不是去重新建立一个连接;当程序使用完连接后,该连接将重新回到连接池中,而不是直接将连接释放; 3)当连接池中的空闲连接数量低于下限时,连接池将根据管理机制追加创建一定数量的连接;当空闲连接数量高于上限时,连接池将释放一定数量的连接。 注意:在每次用完Connection后,要及时调用Connection对象的close()或dispose()方法显式关闭连接,以便连接可以及时返回到连接池中,非显式关闭的连接可能不会添加或返回到池中。   连接池的优点。 创建一个新的数据库连接所耗费的时间主要取决于网络的速度以及应用程序和数据库服务器的(网络)距离,而且这个过程通常是一个很耗时的过程,而采用数据库连接池后,数据库连接请求则可以直接通过连接池满足,而不需要为该请求重新连接、认证到数据库服务器,从而节省了时间; 提高了数据库连接的重复使用率; 解决了数据库对连接数量的限制。   连接池的缺点。 连接池中可能存在多个与数据库保持连接但未被使用的连接,在一定程度上浪费了资源; 要求开发人员和使用者准确估算系统需要提供的最大数据库连接的数量。 5.2 配置并使用连接池 说明:现在项目开发中,使用第三方开源开源数据库连接池较多,例如DBCP、C3P0等,因此这里不再讲解纯Java代码编写的数据库连接池,别人已经给我们封装好了,直接使用就行。 这里可以参考【孤傲苍狼】https://www.cnblogs.com/xdp-gacl/p/4002804.html这位技术大牛的博文。 5.3 使用服务器提供的数据库连接池  在Web项目开发中,我们有时候会使用到服务器提供给我们的数据库连接池,这里简单介绍以下如何在Tomcat服务器中如何配置数据库连接池。   在配置之前,介绍下JNDI技术,JNDI(Java Nameing and Directory Interface、Java命名和目录接口),它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。   Tomcat服务器创建的数据源时以JNDI资源的形式发布的。在配置数据源时,可以将其配置到Tomcat安装目录下的conf\server.xml文件中,也可以将其配置到Web工程目录下的META-INF\context.xml文件中,建议采用后者,因为这样配置的数据源更有针对性,配置数据源的具体代码如下。 12345678<Context> <Resource name="TestJNDI" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" username="root" password="root" url="jdbc:mysql://localhost:3306/jdbctest" maxActive="8" maxIdle="2" maxWait="6000"/></Context>   在配置数据源时需要配置的元素的属性及其说明如下表所示。 属性名称 说明 name 设置数据源的JNDI名 auth 设置数据源的管理者,有两个可选值Container和Application,Container表示由容器来创建和管理数据源,Application表示由Web应用来创建和管理数据源 type 设置数据源的类型 driverClassName 设置连接数据库的JDBC驱动程序 url 设置连接数据库的路径 username 设置数据库的用户名 password 设置连接数据库的密码 maxActive 设置连接池中处于活动状态的数据库连接的最大数目,0表示不受限制 maxIdle 设置连接池中处于空闲状态的数据库连接的最大数目,0表示不受限制 maxWait 设置连接池中没有处于空闲状态的连接时,请求数据库连接请求的最长等待时间(单位为ms),如果超出该时间将抛出异常,-1表示无限制等待 可通过下面代码来获取连接。123456789101112131415161718//部分省略try{ Context ctx = new InitialContext(); ctx = (Context)ctx.lookup("java:comp/env"); DataSource ds = (DataSource)ctx.lookup("TestJNDI"); Connection conn = ds.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from user"); while(rs.next()){ //... } //关闭资源...}catch(Exception e){ }   JDBC就先记录这些,后面如有补充在更新。]]></content>
<categories>
<category>Java</category>
<category>JDBC</category>
</categories>
<tags>
<tag>Java</tag>
<tag>JDBC</tag>
</tags>
</entry>
<entry>
<title><![CDATA[进程和线程(三)线程的同步、调度、安全]]></title>
<url>%2F2018%2F09%2F28%2F%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%EF%BC%88%E4%B8%89%EF%BC%89%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%90%8C%E6%AD%A5%E3%80%81%E8%B0%83%E5%BA%A6%E3%80%81%E5%AE%89%E5%85%A8%2F</url>
<content type="text"><![CDATA[  前言:前面讲到了线程的使用和创建,这篇主要介绍线程的调度、同步、安全以及其它相关方法。 1. 线程的调度  同一时刻如果有多个线程处于可以运行状态,则它们需要排队等待CPU资源。此时每个线程自动获得一个线程的优先级(priority),优先级高低反映线程的重要或紧急程度。可运行状态的线程按优先级排队,线程调度依据优先级基础上的“先到先服务”原则。   Java中在对线程进行调度时,采用了优先级调度策略,具体策略为:“优先级高的线程应该有更大的获取CPU资源执行的概率,优先级低的线程并不是总不能执行”。也就是说,当前正在执行的线程优先级一般不会比正在准备状态等待执行的线程优先级低。   Java中线程的优先级用1~10之间的整数表示,数值越大优先级越高,默认优先级为5.列如,在没有特别指定的情况下,主线程的优先级为5.另外,对于子线程,其初始优先级与其父线程的优先级相同。当需要改变线程的优先级时,可以通过调用setPriority()方法来实现,下面为该方法的声明: 1public final void setPriority(int newPriority); 该方法时final的,所以在继承Thread类时不能重写该方法。参数newPriority表示需要设置的优先级别,应该是1~10之间的整数。为了便于记忆,Java中也提供了3个常量来表示比较常用的优先级别,如下表所示: 常量 表示的内容 public static final int MAX_PRIORITY 该常量将表示线程可以具有最高的优先级 public static final int NORM_PRIORITY 该常量将表示线程可以具有默认的优先级,即5 public static final int MIN_PRIORITY 该常量将表示线程可以具有最低的优先级   下面给出一个利用优先级对线程进行调度的例子。 123456789101112131415161718192021222324252627282930313233343536373839package com.heng.thread;class MyThread1 extends Thread{ @Override public void run() { for(int i = 1;i < 10;i++){ System.out.print(" <MyThread1>"+Thread.currentThread().getName()+"--"+i); if(i % 3 == 0){ System.out.print("\n"); } } }}class MyThread2 extends Thread{ @Override public void run() { for(int i = 1;i < 10;i++){ System.out.print(" <MyThread2>"+Thread.currentThread().getName()+"--"+i); if(i % 3 == 0){ System.out.print("\n"); } } }}public class test4{ public static void main(String[] args) { Thread thread1 = new MyThread1(); Thread thread2 = new MyThread2(); thread1.setPriority(Thread.MIN_PRIORITY); thread2.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); }}   上述代码中将两个线程分别设置为最低、最高的优先级,然后分别启动执行。在启动时,首先启动优先级低的线程。编译并运行代码,运行结果:   在上图中,优先级高的运行完毕,优先级低的才开始运行,这与前面说的“当前正在执行的线程优先级一般不会比正在准备状态等待执行的线程优先级低”是符合的,但要特别注意,这并不是一定的。 2. 线程的其它方法1)线程睡眠  在线程执行的过程中,调用sleep()方法可以让线程睡眠一段指定的时间,等指定时间到达后,该线程则会苏醒,并进入准备等待执行。这是使正在执行的线程让出CPU的最简单方法之一,其声明如下: 12public static void sleep(long millis)throws InterruptedExceptionpublic static void sleep(long millis,int nanos)throws InterruptedException   sleep()方法被重载了,但上述两个方法都可以使线程进入睡眠状态。参数millis为指定线程将睡眠的毫秒数,参数namos为指定线程额外将睡眠的纳秒数。 1Thread.sleep(3*1000); //调用方法:睡眠3秒   要特别注意 线程醒来将进入准备状态,并不能保证立刻执行,因此指定的时间使线程暂停执行的最小时间。 sleep()使得一个线程进入睡眠状态,但是线程所占有的资源并没有释放(例如:锁资源)。 2)线程等待  Object类中的wait()方法,使线程转到阻塞状态,直到其他线程调用此对象的notify()方法或notifyAll()方法唤醒。当线程执行了一个特定对象的wait()调用时,那个线程被放到与那个对象相关的等待池中。此外,调用wait()方法的线程自动释放对象的锁标志。   注意:wait()和sleep()方法本质上来说是不一样的。sleep()没有释放锁标志、释放资源,而wait()实际上放弃了锁,将资源贡献出来,直到被唤醒。 3)线程让步  Thread.yield();方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。 4)线程加入  join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状体,直到另一个线程运行结束,当前线程再由阻塞转为就绪状态。 5)线程唤醒  Object类中的notify()和notifyAll()方法可唤醒在等待池中的线程。对一个特定对象执行notify()调用时,将从对象的等待池中移走一个任意的线程,并放到锁标志等待池中,那里的线程一直在等待,直到可以获得对象的锁标志。notifyAll()方法将从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。只有锁标志等待池中的线程能获取对象的锁标志,锁标志允许线程从上次因调用wait()而中断的地方继续运行。 注: 自版本1.0开始JavaSE就提供了wait(),notify()以及notifyAll()方法。虽然以上所说的3个方法最常出现的地方是线程的内部,但是这些方法并不是Thread类的成员函数。实际上Java在设计的时候就充分考虑到了同步的问题,因而在设计基础类Object的时候就为其提供了这3个函数。这就意味着编写任何类都可以使用这些方法(当然某些时候这些方法是没有意义的)。 由于wait(),notify()和notifyAll()都是锁级别的操作,所以把他们定义在Object类中(因为锁属于对象级,每个对象都有锁)。 wait、notify、notifyAll只能在同步控制方法或同步控制块里面使用(即搭配synchonized 使用,用于线程同步)。 3. 线程的同步(重点)  在多线程程序中,由于同时有多个线程并发运行,有时会带来严重的问题,甚至引发错误。例如,一个银行账号在同一时刻只能由一个用户操作,如果两个用户同时操作很可能会产出错误(线程安全问题)。为了解决这些问题,在多线程开发中就需要使用一些技术来避免这些错误的发生。下面就讲讲Java使用的同步技术。   同步方法是指用synchronized关键字修饰的方法,其与普通方法的不同是,进入同步方法执行的线程将获得同步方法所属对象的锁,一旦对象被锁,其他线程就不能执行被锁对象的任何同步方法。也就是说,线程在执行同步方法之前,首先试图获得方法所属对象的锁,如果不能获得锁就进入对象的锁等待池等待,直到别的线程释放锁,其获得锁才能执行。 声明同步方法的基本语法。 123synchronized <返回类型> 方法名([参数列表]) [throws <异常序列>]{ //同步方法的方法体} 声明同步代码块的基本语法。 123synchronized(obj){ //代码块}   在使用同步方法时要注意以下几点。 关键字synchronized只能标识方法或代码块,不能标识成员变量,不存在同步的成员变量。 一个对象可以同时由同步与非同步方法,只有进入同步方法执行才需要获得锁,每个对象只有一个锁。若一个对象中有多个同步方法,当某线程在访问其中之一时,其他线程不能访问该对象中的任何同步方法,但可以访问非同步方法。 若线程获得锁后进入睡眠或进行让步,则将带着锁一起睡眠或让步,这种做法将严重影响等待锁的线程的执行,进而影响程序的整体性能。 同步方法退出时,锁将被释放,其他等待的线程可以获得锁。   同步方法实例代码: 123456789101112//标识为同步的方法public synchronized void myFunction(){ System.out.println("我是一个同步的方法!");}/*** 标识静态的同步方法* 静态方法也允许同步,这样就保证了静态方法不会同时被多个线程使用**/public static synchronized void myFunnction1(){ System.out.println("我是一个同步的静态方法!");} 其实静态同步方法在执行前,线程要获取的是方法所在类的锁,在道理上与非静态同步方法要获得方法所属对象的锁是相同的。同一时刻一个类也只能有一个静态同步方法被访问。   同步代码块实例代码: 12345678910class myThread implements Runnable{ private byte[] lock = new byte[0]; //特殊的instance变量 public void fun(){ synchronized(lock){ //doString } }}   上述代码适用于没有明确的对象作为锁,只是想让一段代码块同步时,可以创建一个特殊的instance变量来充当锁,零长度的byte数组对象创建起来比任何对象都经济–查看编译后的字节码,生成零长度的byte[]数组对象只需要3条操作码,而Object lock = new Object();则需要7行操作码。   下面用一断代码来展示前面所讲到的知识。 12345678910111213141516171819202122232425262728293031323334353637383940414243package com.heng.thread;class MyRunnbale implements Runnable{ private int ticket; public MyRunnbale(int ticket){ this.ticket = ticket; } /** * 实现run()方法 */ public synchronized void run() { while (ticket > 0){ System.out.println(Thread.currentThread().getName()+"===》出售一张,余票:"+(--ticket)); try{ //Thread.sleep(1*1000); if(ticket % 2 == 0){ this.wait(); }else{ this.notifyAll(); } }catch (Exception e){ e.printStackTrace(); } } }}public class test3{ private static int ticket_num = 90; public static void main(String args[]){ System.out.println("====================总票数: "+ticket_num+"====================="); Runnable thread = new MyRunnbale(ticket_num); new Thread(thread,"窗口--1").start(); new Thread(thread,"窗口--2").start(); new Thread(thread,"窗口--3").start(); }}   运行结果:   总结: 线程同步的目的是为了保护多个线程访问一个共享资源时对资源造成破坏(同步后只能一个一个操作)。 线程同步方法时通过锁机制来实现的,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法在访问该对象的其他同步方法。 当多个线程等待一个对象锁时,没有获取到锁资源的线程将发生阻塞。 对于静态同步方法,锁时针对这个类的,锁对象是该类的Class对象。]]></content>
<categories>
<category>Java</category>
<category>Java SE</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java SE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Servlet(三)之实现一个简单的备忘录]]></title>
<url>%2F2018%2F09%2F27%2FServlet%EF%BC%88%E4%B8%89%EF%BC%89%E4%B9%8B%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E5%A4%87%E5%BF%98%E5%BD%95%2F</url>
<content type="text"><![CDATA[  前面介绍了Servlet和HttpServlet的相关知识,这一篇主要运用前面的知识做一个简单的备忘录。 需求分析: 首先,用户在填写信息的页面中输入留言信息。 然后,单击“提交”按钮提交表单,根据配置信息,表单将被提交给事先编写好的Servlet,在该Servlet中保存留言信息到application中,并跳转到show.jsp页面显示用户备忘录信息。 1)创建工具JavaBean–MyTools.java。在MyTools中创建了changeHTML()方法、changeTime()方法和toChinese()方法。 MyTools.java12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152package com.heng.tools;import java.text.SimpleDateFormat;import java.util.Date;/** * 工具类,实现转换HTML中特殊字符、格式化时间、解决中文乱码问题。 */public class MyTools { /** * 功能:转换字符串中属于HTML语言中的特殊字符 * @param source * @return */ public static String changeHTML(String source){ String changeStr = ""; changeStr = source.replace("&","&amp;"); //转换字符串中的"&"符号 changeStr = source.replace("","&nbsp;"); //转换字符串中的空格 changeStr = source.replace("<","&lt;"); //转换字符串中的"<"符号 changeStr = source.replace(">","&gt;"); //转换字符串中的">"符号 changeStr = source.replace("\r\n","<br>"); //转换字符串中的回车换行 return changeStr; } /** * 功能:将Date型日期转换成指定格式的字符串形式 * @param date * @return */ public static String changeTime(Date date){ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(date); } /** * 功能:解决通过提交表单产生的中文乱码 * @param str * @return */ public static String toChinese(String str){ if(str == null) str = ""; try{ str = new String(str.getBytes("ISO-8859-1"),"utf-8"); }catch (Exception e){ str = ""; e.printStackTrace(); } return str; }} 2)创建实体类JavaBean–WordSingle.java。 WordSingle.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475package com.heng.model;/** * 备忘录信息 */public class WordSingle { /** * 备忘人 */ private String author; /** * 备忘标题 */ private String title; /** * 备忘内容 */ private String content; /** * 备忘时间 */ private String time; public WordSingle() { } public WordSingle(String author, String title, String content, String time) { this.author = author; this.title = title; this.content = content; this.time = time; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getTime() { return time; } public void setTime(String time) { this.time = time; } @Override public String toString() { return "WordSingle{" + "author='" + author + '\'' + ", title='" + title + '\'' + ", content='" + content + '\'' + ", time='" + time + '\'' + '}'; }} 3)创建用户填写备忘信息的页面index.jsp,在该页面中实现一个表单,并向表单中添加author、title和content字段。 index.jsp123456789101112131415161718192021<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><body><center> <h2>备忘录</h2> <form action="/addWord" method="post"> 备&nbsp;&nbsp;忘&nbsp;&nbsp;人:<input type="text" name="author" size="30"> <br> 备忘标题:<input type="text" name="title" size="30"> <br> 备忘内容:<textarea name="content" rows="7" cols="30"></textarea> <br> <input type="submit" value="提交"> <input type="reset" value="重置"> <a href="show.jsp">查看备忘录信息</a> </form></center></body></html> 4)创建处理用户请求的Servlet–WordServlet.java。在该Servlet中首先获取用户输入的信息,然后调用工具MyTools对获取的信息进行转码、特殊字符处理和格式化当前时间,接着将这些信息封装到WordSingle类对象中,最后从应用上下文中获取存储了所有留言的集合对象,并将封装了信息的WordSingle类对象存储到该集合对象中。 WordSingle.java1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071package com.heng.servlet;import com.heng.model.WordSingle;import com.heng.tools.MyTools;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.io.IOException;import java.util.ArrayList;import java.util.Date;@WebServlet("/addWord")public class WordServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } /** * 表单请求使用POST * @param request * @param response * @throws ServletException * @throws IOException */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response )throws ServletException, IOException { /** * 获取表单提交过来的字段 */ String author = MyTools.toChinese(request.getParameter("author")); String title = MyTools.toChinese(request.getParameter("title")); String content = MyTools.toChinese(request.getParameter("content")); /** * 获取当前时间并格式化时间为指定格式 */ String now = MyTools.changeTime(new Date()); /** * 创建WordSingle对象用来封装获取的信息 */ WordSingle single = new WordSingle(); single.setAuthor(MyTools.changeHTML(author)); single.setTitle(MyTools.changeHTML(title)); single.setContent(content); single.setTime(now); /** * 获取session对象 */ HttpSession session = request.getSession(); //通过session对象获取应用上下文 ServletContext servletContext = session.getServletContext(); //获取存储在应用上下文中的集合对象 ArrayList wordList = (ArrayList) servletContext.getAttribute("wordList"); if(wordList == null){ wordList = new ArrayList(); } //将封装了信息的值存储到集合对象中 wordList.add(single); //将集合对象保存到应用上下文中 servletContext.setAttribute("wordList",wordList); //将请求重定向到show.jsp页面 response.sendRedirect("show.jsp"); }} 5)创建显示留言信息的show.jsp页面。在该页面中将获取存储到应用上下文的wordList集合对象,然后遍历该集合对象输出信息。 show.jsp1234567891011121314151617181920212223242526272829303132333435363738394041424344<%-- Created by IntelliJ IDEA. User: Heng Date: 2018/9/27 Time: 16:02 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="java.util.ArrayList" %><%@ page import="com.heng.model.WordSingle" %><html><head> <title>Title</title></head><body><% ArrayList wordList = (ArrayList) application.getAttribute("wordList"); if(wordList == null || wordList.size()==0 ){%> <h1>没有备忘信息哦!</h1> <a href="index.jsp">我要备忘信息</a><% }else{ for (int i = wordList.size()-1;i>=0;i--){ WordSingle single = (WordSingle) wordList.get(i); %> 备&nbsp;&nbsp;忘&nbsp;&nbsp;人:<%=single.getAuthor()%> <p> 备忘时间:<%=single.getTime()%> <p> 备忘标题:<%=single.getTitle()%> <p> 备忘内容: <textarea rows="7" cols="30" readonly><%=single.getContent()%></textarea> <a href="index.jsp">我要备忘信息</a> <hr width="100%"><% } }%></body></html> 6)最后配置tomcat,启动服务器。 运行结果:]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Servlet</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Servlet</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Servlet(二)之HttpServlet开发]]></title>
<url>%2F2018%2F09%2F27%2FServlet%EF%BC%88%E4%BA%8C%EF%BC%89%E4%B9%8BHttpServlet%E5%BC%80%E5%8F%91%2F</url>
<content type="text"><![CDATA[  前面简单的介绍了Servlet相关的技术特点以及生命周期,下面介绍Servlet常用的开发方法及步骤。   说明:在实际的开发的Web项目中,大多数我们采用Servlet技术开发时,是通过继承的HttpServlet类,而不是实现Servlet接口。HttpServlet类存放在javax.servlet.http包内,是针对使用HTTP协议的Web服务器的Servlet类。HttpServlet类通过执行Servlet接口,能够提供HTTP协议的功能。 1.Servlet创建  创建一个Servlet,通常涉及下列4个步骤。 1)继承HttpServlet抽象类。 2)重载适当的方法,如覆盖(重写)doGet()方法或doPost()方法。 3)如果有HTTP请求信息的话,获取该信息。可通过调用HttpServletRequest类对象的以下3个方法获取。 123getParameterNames() //获取请求中所有参数的名字getParameter() //获取请求中指定参数的值getParameterValues() //获取请求中所有参数的值 4)生成HTTP响应。HttpServletResponse类对象生成响应,并将它返回到发出请求的客户机上。它的方法允许设置“请求”标题和“响应”主体。“响应”对象还包含有getWriter()方法以返回一个PringWriter类对象。   以下为按照上述步骤创建的Servlet类。 MyHttpServelt.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354package com.heng.servlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;/** * 第一步:继承HttpServlet类 */@WebServlet("/helloHttpServlet")public class MyHttpServelt extends HttpServlet { /** * 第二步:重写doGet()或doPost()方法 * @param request * @param response * @throws ServletException * @throws IOException */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 第三步:获取HTTP请求信息 */ String myName = request.getParameter("myName"); /** * 第四步:生成HTTP响应 */ response.setContentType("text/html;charset=gb2312"); response.setHeader("Pragma","No-cache"); response.setDateHeader("Expires",0); response.setHeader("Cache-Control","no-cache"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head><title>一个简单的Servlet程序</title></head>"); out.println("<body>"); out.println("<h1>Servlet程序</h1>"); out.println("<p style='color:red'>"+myName+"您好,欢迎使用本程序!!!"); out.println("</body>"); out.println("</html>"); out.flush(); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { this.doGet(req, resp); }} 运行结果: 2.Servlet的配置创建了Servlet类后,还需要对Servlet进行配置。配置的目的是为了将创建的Servlet注册到Servlet容器之中,以方便Servlet容器对Servlet的调用。 2.1 在web.xml文件中配置Servlet1)Servlet的名称、类和其他选项的配置 web.xml12345678910<servlet> <!--Servlet名称--> <servlet-name>myServlet</servlet-name> <!--Servlet类的全路径--> <servlet-class>com.heng.servlet.MyServelt</servlet-class> <!--Servlet描述信息--> <description>Simple Servlet</description> <!--指定发布时Servlet的名称--> <display-name>Servlet</display-name></servlet> 2)Servlet的映射  在web.xml配置文件中可以给一个Servlet做多个映射,因此,可以通过不同的方法访问这个Servlet。 web.xml123456<servlet-mapping> <!--与上面的Servlet名称一定要保持一致--> <servlet-name>myServlet</servlet-name> <!--请求路径,格式较多。例如/*、/helloServlet、/one/*、*.do、*.action等等--> <url-pattern>/helloServlet</url-pattern></servlet-mapping> 3)初始化参数  Servlet可以配置一些初始化参数。 web.xml123456<servlet> <init-param> <param-name>number</param-name> <param-value>10000</param-value> </init-param></servlet>   在上述代码中,指定number的参数值为10000.在Servlet中可以在init()方法体中通过getInitParameter()方法访问这些初始化参数。 4)启动装入优先权  启动装入优先权通过元素指定。 重点 2.2 采用注解配置Servlet  采用注解配置Servlet,就不需要在web.xml中配置Servlet名和映射地址,基本语法如下。 1234567import javax.servlet.annotation.WebServlet;@WebServlet(urlPatterns = {"/映射地址"},asyncSupported=true|false,loadOnStartup = -1,name = "Servlet名称",displayName = "显示名称",initParams = {@WebInitParam(name = "username",value = "值")})   在上面的语法中,urlPatterns属性用于指定映射地址;asyncSupported属性用于指定是否支持异步操作模式;loadOnStartup属性用于指定Servlet的加载顺序;name属性用于指定Servlet的name属性;displayName属性用于指定该Servlet的显示名;initParams属性用于指定一组Servlet初始化参数。]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Servlet</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Servlet</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Servlet(一)之Servlet技术简介]]></title>
<url>%2F2018%2F09%2F27%2FServlet%EF%BC%88%E4%B8%80%EF%BC%89%E4%B9%8BServlet%E6%8A%80%E6%9C%AF%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[  引言:Servlet是在JSP之前就存在的运行在服务端的一种Java技术,它是用Java语言编写的服务器端程序。在JSP技术出现之前,Servlet被广泛地应用来开发动态的Web应用程序。 1. Servlet技术简介  Servlet是一种独立于平台和协议的服务器端的Java技术,可以用来生成动态地Web页面。Servlet是使用Java Servlet应用程序设计接口(API)及相关类和方法的Java程序。Java语言能够实现的功能,Servlet基本上都能实现(除了图形界面外)。Servlet主要是用于处理客户端传来的HTTP请求,并返回一个响应。通常所说的Servlet就是指HttpServlet,用于处理HTTP请求,其能够处理的请求有doGet()、doPost()、service()等方法。在开发Servlet时,可以直接继承javax.servlet.http.HttpServlet。   在Servlet3.0以前的版本中,只能在web.xml文件中配置Servlet,而在Servlet 3.0中除了在web.xml文件中配置以外,还提供了利用注解来配置Servlet。 2. Servlet技术功能  Servlet通过创建一个框架来扩展服务器的能力,以提供在Web上进行请求和响应的服务。当客户机发送请求至服务器时,服务器可以将请求信息发送给Servlet,并让Servlet建立起服务器返回给客户机的响应。当启动Web服务器或客户机第一次请求服务器时,可以自动装入Servlet,之后,Servlet继续运行直到其他客户机发出请求。Servlet的功能涉及范围很广,主要功能如下: 创建并返回一个包含基于客户请求性质的动态内容完整的HTML页面; 创建可嵌入到现有HTML页面中的一部分HTML页面; 与其它服务器资源进行通信; 用多个客户机处理连接,接收多个客户机的输入,并将结果传递到多个客户机上。例如:Servlet可以是多名参与者的游戏服务器; 将订制的处理提供给所有服务器的标准程序。 3. Servlet技术特点  Servlet技术带给程序员最大的优势是它可以处理客户端传来的HTTP请求,并返回一个响应。总的来说,Servlet技术具有以下特点。 高效。在服务器上仅有一个Java虚拟机在运行,它的优势在于当多个来自客户端的请求进行访问时,Servlet为每个请求分配一个线程而不是进程。 方便。Servlet提供了大量的实用工具例程,例如处理很难完成的HTML表单数据、读取和设置HTTP头、处理Cookie和跟踪会话等。 跨平台。 功能强大。 灵活性和可扩展性。 共享数据。Servlet之间通过共享数据可以很容易地实现数据库连接池。 安全。 4.Servlet生命周期  在Servlet地整个生命周期中,Servlet地处理过程如下图所示。   图中所示各步骤的说明如下。 第一步:用户通过客户端浏览器请求服务器,服务器加载Servlet,并创建一个Servlet实例; 第二步:容器调用Servlet的init()方法; 第三步:容器调用service()方法,并将HttpServletRequest和HttpServletResponse对象传递给该方法,在service()方法中处理用户请求; 第四步:在Servlet中请求处理结束后,将结果返回给容器; 第五步:容器将结果返回给客户端进行显示; 第六步:当Web容器关闭时,调用destroy()方法销毁Servlet实例。   注:初始化init()和销毁destroy()方法只执行一次。   下面一段代码采用web.xml配置的Servlet实现(这里我用的时IDEA,请自行导入Servlet依赖): MyServlet.java12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061package com.heng.servlet;import javax.servlet.*;import java.io.IOException;import java.io.PrintWriter;public class MyServelt implements Servlet { /** * 初始化调用init()方法(只调用一次) * @param servletConfig * @throws ServletException */ @Override public void init(ServletConfig servletConfig) throws ServletException { System.out.println("init()方法被调用。。。"); } /** * 处理客户端请求并做出响应 * @param servletRequest * @param servletResponse * @throws ServletException * @throws IOException */ @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { System.out.println("service()方法被调用。。。"); servletResponse.setContentType("text/html;charset=gb2312"); PrintWriter out = servletResponse.getWriter(); out.println("<h1 style='color:red'>hello Servlet</h1>"); out.println("<h2 style='color:red'>Servlet生命周期实例</h2>"); } /** * 当Web容器关闭时调用destroy()方法,(只调用一次) */ @Override public void destroy() { System.out.println("destroy()方法被调用。。。"); } /** * 获取Servlet相关配置属性(用的较少) * @return */ @Override public ServletConfig getServletConfig() { return null; } /** * 获取Servlet相关信息(用的较少) * @return */ @Override public String getServletInfo() { return null; }} web.xml12345678910111213141516<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app> <servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.heng.servlet.MyServelt</servlet-class> </servlet> <servlet-mapping> <servlet-name>myServlet</servlet-name> <url-pattern>/helloServlet</url-pattern> </servlet-mapping> </web-app>   运行结果:   采用注解方式开发是比较简单的,不需要在web.xml中写一大堆配置代码,这里用一句话就可以代替上面的web.xml配置文件,只需要在MyServlet.java的类上加一个注解即可。 1234@WebServlet("/helloServlet")public class MyServelt implements Servlet { //其它代码和上面一致,只是不需要配置web.xml}]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Servlet</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Servlet</tag>
</tags>
</entry>
<entry>
<title><![CDATA[进程和线程(二)线程的创建]]></title>
<url>%2F2018%2F09%2F26%2F%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%EF%BC%88%E4%BA%8C%EF%BC%89%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%88%9B%E5%BB%BA%2F</url>
<content type="text"><![CDATA[  引言:前面讲到了进程和线程的概念,接下来就记录下线程是如何创建并启动的。 1.线程的实现方式  Java中的线程有两方面的含义:一是一条独立的执行线索,二是java.lang.Thread类或是其子类的对象。在Java中开发自己的线程有两种方式,包括继承Thread类与实现Runnable接口。 1)继承Thread类  若一个类直接或间接继承自Thread类,则该类对象便具有了线程的能力。这是最简单的开发线程的方式,采用此方法最重要的是重写继承的run()方法。基本语法如下: 12345class <类名> extends Thread{ public void run(){ //doString }}   上述格式中,run()方法中编写的是线程所要执行的任务代码,一旦线程启动,run()方法中的代码将成为一条独立的执行线索。run()方法是可以重载的,但重载的该方法,不再具有成为一条执行线索的能力,在开发中注意不能写错。 2)实现Runnable接口  由于Java中采用的是单一继承,一个类只能唯一的继承与另一个类。如果只能通过继承Thread类来定义自己的线程,在实际的开发中有很多限制。因此,Java中提供了一个名为Runnable(java.lang.Runnable)的接口,此接口中有一个具有如下申明的抽象方法: 1public abstract void run();   这样,实现了Runnable接口的类中同样也就具有了描述线程任务的run()方法,此run()方法也可以在一定的条件下成为一条独立的执行线索。 无论使用哪种方式,都可以通过一定的操作得到一条独立的执行线索,然而二者之间不是完全相同的,下面对二者之间的异同进行了比较。继承Thread类的方法虽然最简单,但继承了该类就不能够再继承别的类,这在有些情况下会严重影响开发。其实,很多情况下只是希望自己的类具有线程的能力,扮演线程的角色,而自己的类还需要继承其他类。实现Runnable接口既不影响继承其他类,也不影响实现其他接口,只是实现Runnable接口的类多扮演了一种角色,多了一种能力而已,灵活性更好。 2.线程的创建  对于继承Thread类与实现Runnable接口两种不同方式,在创建线程对象这一步是有区别的。 1)继承Thread类方式  继承Thread的类,在创建线程对象时非常简单。其继承了Thread类,因此其自身的对象便是线程对象,在创建线程对象时只需要创建自身的对象即可。 12//继承Thread的类创建线程对象,其中MyThread类继承与Thread类Thread thread = new MyThread();   继承Thread的类创建线程对象的方式与别的类创建对象的方式完全一样。 2)实现Runnable接口方式  对于实现Runnable接口的类,其自身的对象并不是一个线程,只是在该类中通过实现run()方法指出了线程需要完成的任务。然而,若想得到一个线程,必须创建Thread类或其子类的对象,这时就需要使用Thread类的特定构造器来完成这个工作,下表为Thread类的常用构造器: 构造器声明 功能 public Thread() 该构造器将构造一个新的线程对象,该对象启动后将运行自身的run()方法,并且该对象具有默认的名称。 public Thread(Runnable target) 参数target为指定的Runnable实现类,该构造器将构造一个新的线程对象,当该对象启动后将执行指定的target中的run()方法,该对象具有默认的名称。 public Thread(Runnable target,String name) 参数target为指定的Runnable实现类,参数name为指定的名称,该构造器将构造一个新的线程对象,当该对象启动后将执行指定target中的run()方法,该对象具有指定的名称。 public Thread(String name) 参数name为指定的名称,该构造器将构造一个新的线程对象,该对象启动后将运行自身的run()方法,并且该对象具有指定的名称。   从Thread类的构造器列表中可以看出,当创建线程对象时,只需要首先创建实现Runnable接口的类的对象,然后将此对象的引用传递给Thread类构造器即可,这中方式实际上是在告诉我们线程对象要执行的任务(run()方法)在哪里。   下面的例子说明了这个问题,代码如下: 1234//创建实现Runnable接口的类的对象Runnable myRunnable = new MyRunnable();//创建Thread对象,将第一步创建的对象的引用作为构造器参数Thread thread = new Thread(myRunnable);   当然,实现Runnable接口的类的对象可以被同时传递给多个线程对象,如下面的代码: 123456//创建实现Runnable接口的类的对象Runnable myRunnable = new MyRunnable();//创建几个Thread对象,将第一步创建的对象的引用作为构造器参数Thread thread1 = new Thread(myRunnable);Thread thread2 = new Thread(myRunnable);Thread thread3 = new Thread(myRunnable);   上述代码也就意味着这几个线程对象启动后将执行完全相同的任务。另外,Thread类本身也实现了Runnable接口,因此Thread类及其子类的对象也可以作为target参数传递给新的线程对象。 当线程对象创建完成后,其还是一个普通的对象,并没有成为一条对立的执行线索。想让其成为独立的执行线索必须启动,在没有启动的情况下,开发人员可以像调用其他对象的方法一样调用线程对象中的任何可见方法。run()方法也可以作为普通方法一样调用,但调用了run()方法并不代表新建了执行线索,run()方法还是在调用它的线程中执行。   下面一段代码简单的描述线程的创建和启动: 12345678910111213141516171819202122232425262728293031323334package com.heng.thread;public class test1 { static class MyThread extends Thread{ @Override public void run() { System.out.println("我是一个继承Thread的线程"); } } static class MyRunnable implements Runnable{ public void run() { System.out.println("我是一个实现Runable接口的线程"); } } public static void main(String args[]){ /** * 创建MyThread实例对象,并用start()方法启动线程。 */ Thread thread1 = new MyThread(); thread1.start(); /** * 创建MyRunnable实例对象,将引入参数传递给Thread的构造器中,并用start()方法启动线程。 */ Runnable myRunnable = new MyRunnable(); Thread thread2 = new Thread(myRunnable); thread2.start(); System.out.println("main 方法"); }}   运行结果:   今天的线程内容就先讲到这里,下节内容我们继续围绕线程,讲解线程的常用方法、线程的同步、线程的安全、悲观锁/乐观锁以及我们为什么使用多线程开发。]]></content>
<categories>
<category>Java</category>
<category>Java SE</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java SE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[进程和线程(一)概念]]></title>
<url>%2F2018%2F09%2F22%2F%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B%EF%BC%88%E4%B8%80%EF%BC%89%E6%A6%82%E5%BF%B5%2F</url>
<content type="text"><![CDATA[  引言:随着多核CPU的问世,使得多线程程序在开发中占有了更重要的位置。但是什么是进程?什么是线程?它们之间到底又有什么关系呢?下面就来详细的来介绍一下它们的概念。 1.什么是进程?  简单一句话,进程是正在运行的程序的实例。进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程是基本的分配单元,也是基本的执行单元。   进程的概念主要有两点: 进程是一个实体,每一个进程都有它自己的地址空间。一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。 进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。   特征 动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的; 并发性:任何进程都可以同其他进程一起并发执行; 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位; 异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进; 结构特征:进程由程序、数据和进程控制块三部分组成。   状态  进程执行时的间断性,决定了进程可能具有多种状态。事实上,运行中的进程可能具有以下三种基本状态。 1)就绪状态(Ready):  进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。 2)运行状态(Running):  进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。 3)阻塞状态(Blocked):  由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理器资源分配给该进程,也无法运行。 2.什么是线程?  线程的引入: 60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;二是由于对称多处理机(SMP)出现,可以满足多个运行单位,而多个进程并行开销过大。因此在80年代,出现了能独立运行的基本单位——线程(Threads)。   一个进程包含一个或多个线程。线程是程序运行最小的调度单位。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。   多线程编程可以是程序具有多条并发执行线索,就像日常工作中由多人同时合作完成一个任务。这在很多情况下可以改善程序的响应性能,提高资源的利用效率,在多核CPU年代,这显得尤为重要。然而滥用多线程也有可能给程序带来意想不到的错误,减低程序执行的效率。   使用多线程开发优点: 在多核CPU系统中,使用线程可以提高程序响应速度,提高CPU和内存的利用率; 多线程开发可以将耗时操作放入子线程,提高效率; 资源利用率更好; 程序设计在某些情况下更简单,改善程序结构,降低耦合性; 程序响应更快; 可以随时停止任务; 等等…   使用多线程开发缺点: 等候共享资源时造成程序的运行速度变缓慢。这些共享资源主要是独占性的资源,如打印机等; 对线程进行管理要求额外的CPU开销。线程的使用会给系统带来上下文切换的额外负担; 在某些情况下可能会造成线程的死锁。即较长时间的等待或者资源竞争以及死锁等; 对共享变量同时进行读写的不可预测性错误等等。   总之有优点肯定就会有缺点,毕竟谁也不能做到十全十美。后面会讲到Java开发中是如何避免这些缺点的。   线程的五种状态 1)新建状态  使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态(new thread)。 2)可运行状态  使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,是该线程处于可运行状态(Runnable)。 3)运行中状态  Java运行系统通过调度选中一个Runnable的线程,使其占有CPU并转为运行中状态(Running)。此时,系统真正执行线程的run()方法。 4)阻塞状态  一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)。 5)死亡状态  线程结束后是死亡状态(Dead)。   OK,基础概念知识就先写到这里,后面会讲解到如何建立一个线程,以及多线程编程。]]></content>
<categories>
<category>Java</category>
<category>Java SE</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java SE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[系统重装后如何恢复Hexo博客]]></title>
<url>%2F2018%2F09%2F20%2F%E7%B3%BB%E7%BB%9F%E9%87%8D%E8%A3%85%E5%90%8E%E5%A6%82%E4%BD%95%E6%81%A2%E5%A4%8DHexo%E5%8D%9A%E5%AE%A2%2F</url>
<content type="text"><![CDATA[说明由于暑假电脑出了故障,重装了系统。导致本地电脑搭建的Github+Hexo博客不能正常使用,也由于时间原因到现在才有时间来恢复博客(不会断的)。现在简单记录下是如何恢复已有的Hexo博客。 恢复步骤 重点: 重装系统前的博客有备份(整个文件,也就是与最后一次上传到GitHub保持一致,包括配置文件。PS:养成备份的好习惯。)。 第一步:恢复本地按照搭建博客步骤重新安装Git,Node.js,Hexo。可参考《GitHub + Hexo 搭建》,执行到4-1操作步骤后,进入原有hexo文件目录内输入如下命名: hexo s 如果服务器启动成功,并在浏览器中输入http://localhost:4000可以看到博客,则表明本地恢复成功。如果不成功,检查Git,Node.js,Hexo安装是否正确,重来一遍。 第二步:恢复同步GitHub功能第一步正确执行后,接下来我们就要开始恢复博客上传到远程Github上的功能了。可参考《GitHub + Hexo 搭建》,从4.5步骤开始,从新配置用户名和邮箱。 在Blog文件夹目录内右击打开Git Bash Here窗口,输入以下命令: git config --global user.name "GitHub用户名" git config --global user.email "GitHub邮箱" 重点:重新生成SSH KEY(系统重装后是没有的,需要重新生成),GitHub需要一个密钥才能与本地相连接,执行以下命名,并且连续按3次回车生成密钥(注:C 为大写): ssh-keygen -t rsa -C "邮箱地址" 后面会有一个地址,可以看到。就是我们生成的密钥保存地址。即:C:/Users/用户名/.ssh文件中。打开这个目录找到 id_rsa.pub 文件,复制全部内容。 在网页打开GitHub,依次点击 我的头像 - Settings - SSH and GPG keys - New SSH key,将刚刚复制的密钥内容粘贴到key的输入框(可以将原来的密钥删除),然后点击Add Key,配置成功。   最后,测试一下,按照以前的方式随便写一篇文章上传一下,如何成功了,那么我们就大功告成了!!!]]></content>
<categories>
<category>Hexo</category>
<category>个人博客搭建</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Spring系列之SpringMVC入门]]></title>
<url>%2F2018%2F06%2F27%2FSpring%E7%B3%BB%E5%88%97%E4%B9%8BSpringMVC%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[说明:本章主要介绍了springMVC的基础知识和一个简单的登陆过程实例 1. 初识SpringMVC什么是MVC MVC是一种Web程序设计模式、软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写 先给一张官网图: 再看下这张图: 2. 什么是SpringMVC SpringMVC 是一个基于mvc的一种web应用框架,是Spring的一个模块。 与Spring无缝连接,不需要配置。 再看这张图: 化重点: 前端控制器(DispatchServlet) 接收客户端请求,返回响应结果,相当于中央处理转发器。 映射器(HandleMapping) 根据请求的URL查找Handle。 适配器(HandleAdapter) 按照特定规则(HandleAdapter要求的规则)执行Handle。 处理器(Handle )(需要我们编写Controller) 处理业务逻辑,返回ModelAndView。 视图解析器(ViewResolve) 解析处理器返回的视图,然后返回View 3. 一个登陆的实例先给个项目结构截图: 3.1 新建一个项目注: 这里我们使用的是IDEA 2018 开发工具(Eclipse也可以,环境搭好就行) 新建一个Maven -> webapp 项目,然后在src/main下面新建 java和resources目录,并标记为根目录,配置Tomcat等等。 具体可参考: 在IDEA上搭建Spring项目 3.2 导入jar包在pom.xml文件下导入依赖包,代码如下: pom.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384<!--版本控制--><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <slf4j-version>1.7.25</slf4j-version> <spring.version>5.0.4.RELEASE</spring.version></properties><dependencies> <!-- spring相关包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <!-- 日志相关包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j-version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j-version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-jdk14</artifactId> <version>${slf4j-version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency></dependencies> 3.3 在web.xml中配置前端控制器(DispatchServlet)web.xml123456789101112131415161718192021222324252627<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" ><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_3.0.xsd" version="3.0"> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--下面的contextConfigLocation配置springMVC加载的配置文件,映射器、处理器、视图解析器的文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!--访问DispatchServlet对应的路径--> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app> 3.4 在resources目录下新建一个 springmvc-servlet.xml 配置文件注: 要与web.xml 文件中加载的配置文件名一致 springmvc-servlet.xml1234567891011121314151617181920212223<?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" 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.xsd"> <!-- 扫描controller--> <!--扫描"com.heng.controller"包下所有的类--> <context:component-scan base-package="com.heng.controller"/> <!-- 配置视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- jsp所在的位置(前缀)--> <property name="prefix" value="/" /> <!-- jsp文件的后缀--> <property name="suffix" value=".jsp" /> </bean></beans> 附 log4j.properties 配置代码(同样在resources目录下): log4j.properties1234567891011121314###setloglevels###INFOlog4j.rootLogger=INFO,stdout,Elog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target=System.outlog4j.appender.stdout.layout=org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE}%5p %c{1}:%L-%m%nlog4j.logger.org.quartz=INFOlog4j.appender.E=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.E.File=logs/mylogs.loglog4j.appender.E.DatePattern=yyyy-MM-dd'.log'log4j.appender.E.Threshold=INFOlog4j.appender.E.layout=org.apache.log4j.PatternLayoutlog4j.appender.E.layout.ConversionPattern=%-d{yyyy-MM-ddHH\:mm\:ss}[%c][%t\:%r]-[%p]%m%n 3.5 Controller编写 在java目录下新建包 com.heng.controller 新建一个UserController类 代码如下: UserController.java1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950@Controller@SessionAttributes("user") //表示:user 这个值需要写到httpsession中public class UserController { /** * 访问根路径请求的映射 * */ @RequestMapping("/") public String main(Map<String ,Object> map){ //如果session中没有保存用户的信息,则需要跳转到login页面 if(map.get("user") == null){ return "redirect:/login"; } return "main"; } /** * 加载登录页面执行的映射 * */ @RequestMapping(value = "/login",method = RequestMethod.GET) public String login(){ return "login"; } /** * 登录请求URL执行的映射 * */ @RequestMapping(value = "/login",method = RequestMethod.POST) public String login(String userName, String password, Map<String,Object> map){ if("admin".equals(userName) && "123".equals(password)){ map.put("user",userName); return "redirect:/"; } map.put("msg","验证失败,请重新登录!"); return "login"; } /** * 退出请求URL执行的映射 * */ @RequestMapping("/logout") public String logout(Map<String,Object> map, SessionStatus status){ //从session中删除用户信息 map.remove("user"); //更新session status.setComplete(); //重定向到login页面 return "redirect:/login"; }} 6. 附 jsplogin.jsp 代码如下: login.jsp123456789101112131415161718192021222324252627282930<%-- Created by IntelliJ IDEA. User: Heng Date: 2018/6/21 Time: 14:18 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><% String basePath = request.getContextPath(); String msg = (String)request.getAttribute("msg");%><html><head> <title>登录</title></head><body> <form action="<%=basePath%>/login" method="post"> <label for="userName">用户名:<input type="text" name="userName" id="userName"/></label><br/> <label for="password">密&nbsp;&nbsp;码:<input type="password" name="password" id="password"/></label><br/> <input type="submit" value="登录"/> <input type="reset" value="重置"> </form> <h3><%=msg == null ? "" : msg%></h3></body></html> main.jsp 代码如下: main.jsp12345678910111213141516171819202122<%-- Created by IntelliJ IDEA. User: Heng Date: 2018/6/21 Time: 14:27 To change this template use File | Settings | File Templates.--%><%@ page contentType="text/html;charset=UTF-8" language="java" %><% String basePath = request.getContextPath();%><html><head> <title>主页</title></head><body> <h2> Hello Spring!</h2> <form action="<%=basePath%>/logout"> <input type="submit" value="退出登录"/> </form></body></html> OK,到此简单的一个SpringMVC入门小程序就完成了]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Spring</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IDEA 常用快捷键]]></title>
<url>%2F2018%2F06%2F23%2FIDEA-%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%2F</url>
<content type="text"><![CDATA[IDEA 现在是越来越火的一款开发工具,很多公司都在使用它来开发软件。具体简介就不讲了,下面说一些IDEA 开发工具常用的一些快捷键,能帮助我们高效地开发。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101Ctrl + Alt + S 打开设置选项Ctrl + / 注释 、 取消注释(单行注释)Ctrl + Shift + / 注释 、取消注释(多行注释)Ctrl + N 查找类文件Ctrl + Shift + N 查找文件Ctrl + Y 删除当前行Ctrl + D 复制当前行到下一行Ctrl + X 剪切当前行(也有删除当前行的作用)Ctrl + V 粘贴Ctrl + R 替换Ctrl + F 在当前文件中查找 (使用 F3 在查找到的多个结果中切换)Ctrl + Shift + F 在指定范围查找(更详细)Ctrl + P 查看一个函数可以使用的参数 / 查看一个属性有哪些可用的值 等等Ctrl + Shift + 1, 2, 3, 4 在某一行打标签。结合 Ctrl + 1, 2, 3, 4 可以快速定位到某一标记行。 适用于行数特别多的文件。Ctrl + delete 删除一个连续的单位。比如,单词、连在一起的单词、连在一起的汉字等。Ctrl + W 选择一个单位。连续按的话,会不断扩展选择的范围。Ctrl + 左右箭头(导航键左右键) 按照单位跳转。Ctrl + (-/+) 收起或者展开一段代码块(比如,一块注释或者一个方法太长,可以按Ctrl + - 将它收起来)Ctrl + E 打开最近的文件Ctrl + Shift + E 打开最近编辑过的文件Ctrl +Tab 直接打开最近的文件Ctrl + F12 查看当前文件的内部成员 或者说 查看本文档的结构Ctrl + G 跳转到指定行Ctrl + F4 关闭当前文件Ctrl + B 打开一个类(java)的声明,其他如html jsp文件也是类似。相当于在eclipse中编辑java类文件时,按住Ctrl键并单击类名从而进入类的声明。Ctrl + H 查看当前类的继承关系(按Shift + Esc 关闭)Ctrl + Shift 查看一个方法的继承关系(按Shift + Esc 关闭)Ctrl + Q 查看说明文档Ctrl + Alt + L 格式化代码Ctrl + Shift + I 查看类体或者方法体Ctrl + Alt + O 自动导包 Ctrl + Alt + Enter 在当前行上面开启新的一行Ctrl + Shift + 上下箭头 上下移动代码块(对比Alt + Shift + 上下箭头) Ctrl + Shift + U 切换大小写Ctrl + Shift + V 选择性粘贴(剪切板有多个选项)Ctrl + Shift + C 复制当前文件的路径Ctrl + Shift + J 合并下一行到当前行Ctrl + Shift + (-/+) 折叠或者展开所有代码块Ctrl + Shift + Space 自动提示Alt + Enter 根据提示自动导入包Alt + 左右箭头 在当前打卡的多个文件之间跳转Alt + / 自动补全Alt + 鼠标选区 可以选择方形区域(列模式)Alt + Home 锁定到导航栏目,按导航键可以直接打开文件Alt + Insert 插入构造方法、toString方法等Alt + F7 查看方法在哪里被调用了Shift + F6 重命名 (比如,选中名为 thread 的变量,按Shift + F6 进行重命名,可以对本类中所有原来名为thread的变量进行重命名)Shift + Enter 在当前行下面开启新的一行Shift + Home / End 选择光标到行首/行尾的区域Shift + 导航箭头 自由选择Shift + F11 查看书签]]></content>
<categories>
<category>开发工具</category>
</categories>
<tags>
<tag>开发工具</tag>
</tags>
</entry>
<entry>
<title><![CDATA[(二)Spring系列之Spring的事务]]></title>
<url>%2F2018%2F06%2F23%2F%EF%BC%88%E4%BA%8C%EF%BC%89Spring%E7%B3%BB%E5%88%97%E4%B9%8BSpring%E7%9A%84%E4%BA%8B%E5%8A%A1%2F</url>
<content type="text"><![CDATA[  前言:Spring的事务管理不需要与任何特定的事务API耦合。对不同的持久层访问技术,编程式事务提供了一致的事务编程风格,通过模板化操作一致性地管理事务。声明式事务基于Spring AOP实现,但并不需要开发者真正精通AOP技术,亦可容易地使用Spring地声明式事务管理。 1. Spring支持的事务策略  Java EE应用的传统事务有两种策略:全局事务和局部事务。全局事务由应用服务器管理,需要底层服务器的JTA(Java Transaction API)支持。局部事务和底层技术所采用的持久化技术有关,当采用JDBC持久化技术时,需要使用Connection对象来操作事务;采用Hibernate持久化技术时,需要使用Session对象来操作事务。   全局事务可以跨多个事务性资源(典型例子使关系数据库和消息队列);使用局部事务,应用服务器不需要参与事务管理,因此不能保证跨多个事务性资源的事务的正确性。   Spring事务策略是通过PlatformTransactionManager接口体现的,该接口是Spring事务策略的核心。该接口的源代码如下:123456789public interface PlatformTransactionManager{ //获得目前的事务 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; //提交事务 void commit(TransactionStatus status) throws TransactionException; //事务回滚 void rollback(TransactionStatus status) throws TransactoinException;}   PlatformTransactionManager是一个与任何事务策略分离的接口,随着底层不同事务策略的切换,应用必须采用不同的实现类。应用程序面向与平台无关的接口编程。   Spring的事务机制是一种典型的策略模式,PlatformTransactionManager代表事务管理接口,但它并不知道底层到底如何管理事务,它只要求事务管理需要提供开始事务(getTransaction())、提交事务(commit())和回滚事务(rollback())三个方法,但具体如何实现则交个其实现类来完成——不同的实现类则代表不同的事务管理策略。   在PlatformTransactionManager接口内,包含一个getTransaction(TransactionDefinition definition)方法,该方法根据TransactionDefinition参数返回一个TransactionStatus对象。TransactionStatus对象表示一个事务,TransactionStatus被关联在当前执行的线程上。   TransactionDefinition接口定义了一个事务的规则,该接口必须指定如下几个属性值。123456789101112public interface TransactionDefinition{ //获得事务传播行为(通常,在事务总执行的代码都会在当前事务中运行) int getPropagationBehavior(); //获得事务的隔离层次(当前事务和其它事务的隔离程度) int getIsoIationLevel(); //判断事务是否超时(事务在超时前能运行多久) int getTimeout(); //判断是否为只读事务(只读事务不修改任何数据) boolean isReadOnly(); //返回一个事务的名称 String getName();}   TransactionStatus代表事务本身,它提供了简单的控制事务执行和查询事务状态的方法,这些方法在所有的事务API中都是相同的。接口源代码如下。123456789public interface TransactionStatus{ //判断事务是否为新建的事务 boolean isNewTransaction(); //设置事务回滚 void setRollbackOnly(); //查询事务是否已有回滚标志 boolean isRollbackOnly(); //...}   Spring具体的事务管理由PlatformTransactionManager的不同实现类来完成。在Spring容器中配置PlatformTransactionManager Bean时,必须针对不同的环境提供不同的实现类。实际上,Spring提供了如下两种事务管理方式。 编程式事务管理:以编程的方式在业务逻辑中编写事务控制代码。即使使用Spring的编程式事务,程序也可以直接获取容器中的transactionManager Bean,该Bean总是PlatformTransactionManager的实例,所以可以通过该接口提供的三个方法来开始事务、提交事务和回滚事务。 声明式事务管理:无须在Java程序中书写任何事务操作代码,而是通过在XML配置文件中为业务组件配置事务代理(AOP代理的一种),AOP为事务代理所织入的增强处理也有Spring提供——在目标方法执行之前,织入开始事务;在目标方法之后,织入结束事务。 注:通常都推荐采用声明式事务策略,使用声明式事务策略的优势十分明显。 声明式事务能大大降低开发者的代码书写量。 应用程序代码无须任何事务处理代码,可以更加专注于业务逻辑的实现。 Spring可对任何POJO的方法提供事务管理,而且Spring的声明式事务管理无须容器的支持,可在任何环境下使用。 EJB的CMT无法提供声明式回滚规则;而通过配置文件,Spring可指定事务在遇到特定异常时自动回滚。 由于Spring采用AOP的方式管理事务,因此,可以在事务回滚动作中插入用户自己的动作,而不仅仅时执行系统默认的回滚。]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Spring</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title><![CDATA[(一)Spring系列之Spring技术介绍]]></title>
<url>%2F2018%2F06%2F20%2F%EF%BC%88%E4%B8%80%EF%BC%89Spring%E7%B3%BB%E5%88%97%E4%B9%8BSpring%E6%8A%80%E6%9C%AF%E4%BB%8B%E7%BB%8D%2F</url>
<content type="text"><![CDATA[  前言:Spring框架由Rod Johnson开发,2004年发布了Spring框架的第一个版本。经过十多年的发展,Spring已经发展成为JavaEE开发中最重要的框架之一。对于一个Java开发开发者来说,Spring已经成为必须掌握的技能。不仅如此,围绕Spring,以Spring为核心还衍生出了一系列框架,如SpringBoot、SpringCloud、SpringSecurity等,Spring越来越强大,带给开发者越来越多的便捷。 Spring官网 1. Spring简介  Spring是一个从实际开发中提取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率。   Spring为企业应用的开发提供了一个轻量级的解决方案。该解决方案包括:基于依赖注入(IoC)的核心机制、基于AOP的声明式事务管理、与多种持久层技术的整合,以及优秀的Web MVC框架等。Spring致力于JavaEE应用各层的解决方案,而不是仅仅专注于某一层的方案。可以说:Spring是企业应用开发的“一站式”选择,Spring 贯穿表现层、业务层、持久层。Spring并不是想取代那些已有的框架,而是以高度的开放性与它们无缝整合。   Spring的优点。 Spring的IoC容器简化开发,方便解耦。 低侵入式设计,代码的污染极低。 AOP编程的支持,将一些通用任务如安全、事务、日志等进行集中式处理。 声明式事务的支持。 方便程序的测试。 方便集成各种优秀的框架。 降低Java EE API的使用难度。 2. Spring的使用  Spring的两大核心功能:IoC(控制反转)、AOP(面向切面编程)。 2.1 使用Spring管理Bean  Spring核心容器:Spring核心容器就是一个超级大工厂,所有的对象(包括数据源、Hibernate SessionFactory等基础性资源)都会被当成Spring核心容器管理的对象——Spring把容器中的一切对象统称为Bean。只要是一个Java类,Spring就可以管理该Java类,并将它当成Bean。即:对于Spring框架来说,一切Java对象都是Bean。application.xml12345678910111213141516<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 在Spring核心配置文件中加入一个bean标签,就可以将该类放入到Spring容器中管理 id:bean的唯一标识 class:类所在路径(包名+类名),如 cn.zqiheng.dao.UserDao --> <!--配置名为userDao的Bean,其实现类是cn.zqiheng.dao.UserDao--> <bean id="userDao" class="cn.zqiheng.dao.UserDao"/></beans>   实际上,配置文件中的<bean…/>元素默认以反射方式来调用该类无参数的构造器,以如下元素为例:1<bean id="userDao" class="cn.zqiheng.dao.UserDao"/>   Spring框架解析该<bean…/>元素后将可以得到两个字符串,其中idStr的值为 userDao(解析<bean…/>元素的id属性得到的值),classStr的值为 cn.zqiheng.dao.UserDao (解析<bean…/>元素的class属性得到的值)。   也就是说,Spring底层会执行如以下格式的代码:1234567String idStr = ...; //解析<bean.../>元素的id属性得到该字符串值为"userDao"String classStr = ...; //解析<bean.../>元素的class属性得到该字符串值为"cn.zqiheng.dao.UserDao"Class clazz = Class.forName(classStr);Object obj = clazz.newInstance();//container代表Spring容器container.put(idStr,obj);   上面代码就是最基本的反射代码,Spring框架通过反射根据class属性指定的类名创建了一个Java对象,并以id属性的值为key,将该对象放入Spring容器(Map)中——这个Java对象就成为了Spring容器中的Bean。   结论:在Spring配置文件中配置Bean是,class属性的值必须是Bean实现类的完整类名(包名+类名),不能是接口,不能是抽象类(除非有特殊配置),否则Spring无法使用反射创建该类的实例。 2.2 IoC(控制反转)  Spring最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。 2.2.1 基于xml配置的使用方法  这里我们新建一个Spring_IoCDemo的普通java项目来演示。 (1)新建Maven Java项目 (2)导入Spring开发依赖架包 pom.xml1234567<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.0.RELEASE</version> </dependency></dependencies> (3)这里我们建立两个类(Axe、Person),即人类使用斧子砍柴。 Axe.java1234567package cn.zqiheng.ioctest;public class Axe { public String chop(){ return "使用斧头砍柴"; }} Person.java123456789101112131415161718192021package cn.zqiheng.ioctest;public class Person { //这里我们使用了Axe类的chop()方法。即有了对象依赖关系 //传统的方法是需要 new 一个对象实例,才能调用该对象的方法。 //现在使用Spring的方式就不需要我们这么做了,接下来就是Spring依赖注入的一个实例 private Axe axe; //使用属性setter方法注入依赖 public void setAxe(Axe axe) { this.axe = axe; } public void useAxe(){ System.out.println("我打算去砍点柴火!!!"); //调用axe的chop()方法 //表明Person对象依赖于axe对象 System.out.println(axe.chop()); }} (4)在resources资源文件下,新建Spring核心配置文件,并在配置文件中管理上面新建的两个类。 application.xml123456789101112131415161718192021<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--将类纳入Spring容器中--> <bean id="axe" class="cn.zqiheng.ioctest.Axe"/> <bean id="person" class="cn.zqiheng.ioctest.Person"> <!-- property:bean标签的子元素。 属性: name:决定执行哪个setter方法, value:如果传入参数是基本类型及其包装类、String等类型,则使用value属性指定传入参数 ref:如果以容器中其它Bean作为传入参数,则使用ref属性指定传入参数。 --> <!--这里的意思是,在Person类中,我们依赖了Axe的对象,因此需要依赖上面我们定义id为"axe"的Bean--> <property name="axe" ref="axe"/> </bean></beans> (5)测试类 IoCTest.java1234567891011121314151617181920212223242526package cn.zqiheng.test;import cn.zqiheng.ioctest.Person;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.context.support.FileSystemXmlApplicationContext;public class IoCTest { public static void main(String[] args) { /** * 通过Spring提供的方法读取xml文件,加载Spring核心容器 * ClassPathXmlApplicationContext:从类加载路径下搜素配置文件,并根据配置文件来创建Spring容器(在java开发中,推荐使用此种方式。在Javaweb开发中配置在web.xml中即可) * FileSystemXmlApplicationContext:从文件系统的相对路径或绝对路径下取搜素配置文件,并根据配置文件来创建Spring容器 */ ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml"); //ApplicationContext context = new FileSystemXmlApplicationContext("application.xml"); // 获取id为person的Bean //此处getBean返回的是 Object 对象,因此需要强制转型 Person person = (Person) ctx.getBean("person"); person.useAxe(); }}   运行结果如下。   通过上面简单的例子,可以看出使用Spring框架之后最大的改变之一是:程序不再使用new调用构造器创建Java对象,所有的java对象都由Spring容器负责创建。 2.2.1 基于注解的使用方法  上面的方式,我们使用的是xml配置方式来实现Spring的功能,下面来讲解使用注解方式的使用。 (1)因为注解需要使用AOP的方法,因此需要在pom.xml加入下面的依赖文件 pom.xml12345678910111213141516<!--AOP切面编程导入依赖--><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.1</version></dependency><dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version></dependency><dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version></dependency> (2)在xml配置文件中开启注解 applicaion.xml12345678910<?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" 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.xsd"> <!--开启注解,扫描注解类所在的包cn.zqiheng.ioctest 及其下面的所有子包--> <context:component-scan base-package="cn.zqiheng.ioctest"/></beans> (3)使用注解 Axe.java12345678910111213package cn.zqiheng.ioctest;import org.springframework.stereotype.Component;//@Component 注解:标注一个普通的Spring Bean类。// 即只要有这个注解的类,Spirng都会把它纳入到容器中管理//指定 id 的名称为 axe1@Component("axe1")public class Axe { public String chop(){ return "使用斧头砍柴"; }} Person.java12345678910111213141516171819202122232425262728293031package cn.zqiheng.ioctest;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Component;import javax.annotation.Resource;@Componentpublic class Person { //@Autowired 注解来指定自动装配,@Autowired可以修饰setter方法、普通方法、实例变量和构造器等。 //当使用 @Autowired 标注方法是,默认采用byType(类型)自动装配策略, //在这种策略下,如果符合自动装配类型的候选Bean实例有多个,这个时候就可能引起异常, // 为了实现精确的自动装配,Spring提供了@Qualifier注解,允许使用Bean的id来执行自动装配 //注:此处可以使用 @Resource(name = "axe1") 代替下面两个注解 @Autowired @Qualifier("axe1") private Axe axe; //使用属性setter方法注入依赖 public void setAxe(Axe axe) { this.axe = axe; } public void useAxe(){ System.out.println("我打算去砍点柴火!!!"); //调用axe的chop()方法 //表明Person对象依赖于axe对象 System.out.println(axe.chop()); }}   提示:使用@Qualifier注解的意义并不是很大,如果程序使用@Autowired和@Qualifier实现精确的自动装配,还不如直接使用@Resource注解执行依赖注入。 (4)单元测试  结果就不看了,就是根据上面xml文件配置改过来的,两个可以对比下,结果肯定是一样的。只是注解简化的开发过程,代码清晰明了,不需要在xml文件中写入大量配置Bean的元素。 Spring开发中常用注解: @Component:标注一个普通的Spring Bean组件类。 @Controller:标注一个控制器组件类。 @Service:标注一个业务逻辑组件类。 @Repository:标注一个DAO组件类。   上面四个注解本质功能是一样的,都是标注一个Spring Bean,但是为了各司其职,还是使用不同的注解表明不同层的功能更好。在Spring未来的版本中,@Controller、@Servi和@Reposi也许还能携带更多语义,因此,如果需要在JavaEE应用中使用这些标注时,应尽量考虑使用这些注解来代替通用的@Compon标注。 @Scope:指定Bean实例的作用域。 @Resource:根据id注入配置依赖。(位于javax.annotaion包下) @Autowired:根据类型自动装配。 @Qualifier:配合@Autowired注解使用。(指定Bean的id) @Value:注入基本数据值。可参考 @Value用法 @PostConstruct:用于修饰Bean中的方法,修饰的方法是Bean的初始化方法。 @PreDestroy:用于修饰Bean中的方法,修饰的方法是Bean销毁之前的方法。 @DependsOn:用于强制初始化其它Bean。 @Lazy:用于修饰一个方法,指定该方法对应的Bean是否需要延迟初始化。 2.2.3 Spring注入方式 构造注入:通过<constructor-arg…/>元素驱动Spring执行带参数的构造器。 设值注入:通过<property…/>元素驱动Spring执行setter方法。 p命名空间注入:简化设值注入。(用的较少) c命名空间注入:简化构造注入。(用的较少)可以参考:c/p命名空间注入 util命名空间注入:使用较少,后面写一篇文件在来介绍。   在开发过程中,上面几种注入方式在穿插使用。几种方式依赖注入并没有绝对的好坏,只是适应的场景有所不同。 注:Spring的IoC中还是有很多内容,这里就不再一一记录。 可参考:w3cschool   总结:注解方式并不是为了取代xml文件配置方式,只是为了简化xml配置文件,不至于xml太过臃肿,这两种方式应该结合使用,相辅相成。 2.3 AOP(面向切面编程)  AOP(Aspect Orient Programming),也就是面向切面编程,作为面向对象编程的一种补充,已经成为了一种比较成熟的编程方式。AOP和OOP互为补充,面向对象编程将程序分解成各个层次的对象,而面向切面编程将程序运行过程分解成各个切面。   AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在Java EE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。 2.3.1 AspectJ技术简介  AspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能。AspectJ是最早的、功能比较强大的AOP实现之一,对整套AOP机制都有较好的实现,很多其它语言的AOP实现,也借鉴或采用了AspectJ中的很多设计。在Java领域中,AspectJ中的很多语法结构基本上已成为AOP领域的标准。   从Spring2.0开始,Spring AOP已经引入了AspectJ的支持,并允许直接使用AspectJ进行AOP编程,而Spring自身的AOP API也努力与AspectJ保持一致。即使不用Spring框架,也可以直接使用AspectJ进行AOP编程。   AOP的基本概念。   AOP从程序进行角度考虑程序的流程,提取业务处理过程的切面。AOP面向的是程序运行中各个步骤,希望以更好的方式来组合业务处理的各个步骤。AOP框架并不与特定的代码耦合,AOP框架能处理程序执行中特定的切入点(Pointcut),而不与某个具体类耦合。AOP框架具有如下两个特征。 各步骤之间的良好隔离性。 源代码无关性。   AOP的基本术语。 切面(Aspec):切面用于组织多个Advice,Advice放在切面中定义。 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用。 增强处理(Advice):AOP框架在特定的切入点执行的增强处理。处理有“around”、“before”和”after“等类型。 切入点(Pointcut):可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。 引入:将方法或字段添加到被处理的类中。Spring允许将新的接口引入到任何被处理的对象中。 目标对象:被AOP框架进行增强处理的对象,也被成为被增强的对象。 AOP代理:AOP框架创建的对象,简单地说,代理就是对目标对象地加强。Spring中的AOP代理可以是JDK动态代理,也可以是cglib代理。前者为实现接口的目标对象的代理,后者为不实现接口的目标对象的代理。 织入(Weaving):将增强处理添加到目标对象中,并创建一个被增强的对象(AOP代理)的过程就是织入。织入有两种实现方式——编译时增强(如AspectJ)和运行时增强(如Spring AOP)。   由前面的介绍知道,AOP代理就是由AOP框架生成的一个对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但AOP代理中的方法与目标对象的方法存在差异——AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。 2.3.2 Spring的AOP支持  Spring中的AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也有IoC容器负责管理。因此,AOP代理可以直接使用容器中的其它Bean实例作为目标,这种关系可有IoC容器的依赖注入提供。Spring默认使用Java动态代理来创建AOP框架,这样就可以为任何接口实例创建代理了。Spring也可以使用cglib代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用cglib代理。   Spring目前仅支持将方法调用作为连接点(Joinpoint),如果需要把对成员变量的访问和更新也作为增强处理的连接点,则可以考虑使用AspectJ。   Spring AOP开发编程中,需要程序员参与的只有三个部分。 定义普通业务组件。 定义切入点,一个切入点可能横切多个业务组件。 定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作。   其中第一部分是最平常不过的事情。那么进行AOP编程的关键就是定义切入点和定义增强处理。一旦定义了合适的切入点和增强处理,AOP框架将会自动生成AOP代理,而AOP代理的方法大致有如下公式: AOP代理的方法 = 增强方法 + 目标对象的方法 2.3.3 基于xml配置的AOP编程  这里我就模拟一个简单的CRUD操作环境来对AOP编程的演示。 (1)新建Maven Java项目 (2)导入项目pom.xml依赖 pom.xml123456789101112131415161718192021222324252627282930313233343536373839<dependencies> <!--Spring核心包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.0.RELEASE</version> </dependency> <!--切面编程导入依赖--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.1</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.5</version> </dependency> <!--junit单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--spring整合junit测试工具--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.0.0.RELEASE</version> <scope>test</scope> </dependency> </dependencies> (3)持久层、业务层代码  模拟用户进行删除、添加、查询的操作业务,将DAO接口实现类中的方法作为连接点,配置在xml中的连接点即为切入点。 IUserDao.java123456789101112131415161718192021222324package cn.zqiheng.dao;/** * 持久层Dao接口 */public interface IUserDao { /** * 模拟一个增加用户的方法 */ void addUser(); /** * 模拟一个删除用户的方法 * @param id */ Integer deleteUser(long id); /** * 根据用户的id获取用户的姓名,实际上基本不会这么做,这里只是模拟一个异常产生 * @param id * @return */ String getUserNameById(long id) throws Exception;}   Dao接口实现类,模拟数据库的CRUD操作。 UserDaoImpl.java1234567891011121314151617181920212223242526272829303132333435package cn.zqiheng.dao.impl;import cn.zqiheng.dao.IUserDao;import org.springframework.stereotype.Repository;import java.util.ArrayList;import java.util.List;@Repositorypublic class UserDaoImpl implements IUserDao { public void addUser() { System.out.println("调用addUser方法,增加一个用户"); } public Integer deleteUser(long id) { System.out.println("调用deleteUser方法,删除一个用户,id为:"+id); return (int)id; } public String getUserNameById(long id) throws Exception { try{ List<String> names = new ArrayList<String>(); names.add("张三"); names.add("李四"); names.add("王五"); names.add("Heng"); return names.get((int)id); }catch (Exception e){ throw new Exception("越界异常!!!"); } }}   业务层接口。 IUserService.java123456789101112131415161718192021222324package cn.zqiheng.service;/** * 业务层Service接口 */public interface IUserService { /** * 模拟一个删除用户的业务 */ void addUser(); /** * 模拟一个根据id删除用户的业务 * @param id 用户id */ Integer deleteUser(long id); /** * 模拟一个根据id查找用户的名字的业务 * @param id 用户id * @return 用户名 */ String getUserName(long id);}   业务层接口实现类,依赖DAO接口实现类,需要依赖注入。 UserServiceImpl.java123456789101112131415161718192021222324252627282930313233package cn.zqiheng.service.impl;import cn.zqiheng.dao.IUserDao;import cn.zqiheng.service.IUserService;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserServiceImpl implements IUserService { @Resource private IUserDao userDao; public void setUserDao(IUserDao userDao) { this.userDao = userDao; } public void addUser() { userDao.addUser(); } public Integer deleteUser(long id) { return userDao.deleteUser(id); } public String getUserName(long id){ try{ return userDao.getUserNameById(id); }catch (Exception e){ } return "请输入正确的id"; }} (4)自定义增强类   定义我们自己需要的增强方法,即和业务逻辑实现本身没有关系的方法抽出来。 MyAdvice.java1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980package cn.zqiheng.aop;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import java.util.Arrays;/** * 自定义增强类:其中定义增强的方法 */public class MyAdvice { /** * 定义一个普通方法作为Advice方法 * 在用户执行添加和删除前,检测用户权限 * 前置增强(Before) */ public void checkRoot(JoinPoint joinPoint){ System.out.println("=========================before前置增强:模拟用户权限操作========================="); System.out.println("=========================before前置增强:目标对象:"+joinPoint.getTarget()+" 执行目标方法:"+joinPoint.getSignature().getName()+" 参数列表:"+Arrays.toString(joinPoint.getArgs())); } /** * 定义一个普通方法作为Advice方法 * 在添加和删除方法正常执行后,打印操作成功语句 * 后置增强(AfterReturning) */ public void success(){ System.out.println("=========================after-returning后置增强:方法成功执行========================="); } /** * 定义一个普通方法作为Advice方法 * 在添加和删除方法执行后,记录操作日志 * 最终增强(After) */ public void log(){ System.out.println("=========================after最终增强:模拟日志记录操作========================="); } /** * 定义一个普通方法作为Advice方法 * 在目标方法方式异常后,执行此方法 * 异常增强(AfterThrowing) * @param ex 形参用于访问目标方法中抛出的异常 */ public void doRecover(Throwable ex){ System.out.println("=========================after-throwing后置增强:发生异常========================="); System.out.println("=========================after-throwing后置增强:目标方法抛出的异常:"+ex); System.out.println("=========================after-throwing后置增强:模拟Advice对异常的修复========================="); } /** * 定义一个普通方法作为Advice方法 * 审核事务方法:在目标方法执行前后都增强 * 环绕增强(Around) * @param joinPoint 必须的参数,ProceedingJoinPoint内的proceed()方法才能执行目标方法 */ public Object processTx(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("=========================around环绕增强:执行目标方法之前,模拟开始事务========================="); System.out.println("目标对象:"+joinPoint.getTarget()+" 执行目标方法:"+joinPoint.getSignature().getName()+" 参数列表:"+Arrays.toString(joinPoint.getArgs())); //访问执行目标方法的参数 Object[] args = joinPoint.getArgs(); //当执行目标方法的参数存在,且第一个参数是字符时 if(args !=null && args.length > 0 && args[0].getClass() == String.class){ //修改目标方法调用参数的第一个参数 args[0] = "【增强的前缀】" + args[0]; } /** * 重点:执行目标方法,并保存目标方法执行后的返回值 */ Object rvt = joinPoint.proceed(args); System.out.println("=========================around环绕增强:执行目标方法之后,模拟结束事务========================="); //如果rvt的类型是Integer,将rvt改为它的平方 if(rvt != null && rvt instanceof Integer){ rvt = (Integer)rvt * (Integer) rvt; } return rvt; }} (5)核心xml配置   基于xml配置的AOP编程的核心就是在Spring的配置文件中。这里简单的介绍下概念。在下面注释中有详细解释。 aop:before.../:配置Before增强处理。(前置增强)aop:after.../:配置After增强处理。(最终增强)aop:after-returning.../:配置AfterReturning增强处理。(后置增强)aop:after-throwing.../:配置AfterThrowing增强处理。(异常增强)aop:around.../:配置Around增强处理。(环绕增强) application.xml12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788<?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:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启组件注解扫描,扫描cn.zqiheng及下面的子包--> <context:component-scan base-package="cn.zqiheng"/> <!--使用xml配置方式,将增强类纳入Spring容器中,也可以直接使用注解,为了方便演示,这里全部使用xml配置方法--> <bean id="myAdvice" class="cn.zqiheng.aop.MyAdvice"/> <!-- aop:命名空间增强处理配置 --> <aop:config> <!--定义切入点 addUser(..) 方法作为切入点--> <aop:pointcut id="addUser" expression="execution(* cn.zqiheng.dao.impl.UserDaoImpl.addUser(..))"/> <!--定义切入点 delete*(..) 方法作为切入点,只要再cn.zqiheng.dao.impl包下以 delete 开头的方法都可以切入--> <aop:pointcut id="deleteUser" expression="execution(* cn.zqiheng.dao.impl.*.delete*(..))"/> <!-- 定义切面:(为普通业务组件织入的处理动作) 两种方式: 1.< aop:aspect>:定义切面(切面包括增强和切点) 2.< aop:advisor>:定义通知器(通知器跟切面一样,也包括增强和切点) 区别: 1.< aop:aspect>定义切面时,只需要定义一般的bean就行,而定义< aop:advisor>中引用的增强时,自定义增强类必须实现Advice接口。 2.< aop:aspect>大多用于日志,缓存,< aop:advisor>大多用于事务管理。 --> <!-- order:表示切面的优先级为1--> <aop:aspect ref="myAdvice" order="1"> <!-- 前置增强: method = 增加方法(id为myAdvice的实列对象中的方法 pointcut-ref = 被增强对象【即目标方法】(上面定义的切入点) --> <aop:before method="checkRoot" pointcut-ref="addUser"/> <!-- 前置增强——aop:before:作用于目标方法执行前织入。 --> <aop:before method="checkRoot" pointcut-ref="deleteUser"/> </aop:aspect> <!--定义另一个切面--> <aop:aspect ref="myAdvice"> <!-- 后置增强:——aop:after-returning:在目标方法正常完成后被织入。 returning:该属性只对after-returning元素有效,用于指定一个形参名,AfterReturning增强处理方法可通过该形参访问目标方法的返回值 --> <aop:after-returning method="success" pointcut-ref="addUser"/> <aop:after-returning method="success" pointcut-ref="deleteUser"/> </aop:aspect> <!--再定义一个切面--> <aop:aspect ref="myAdvice"> <!-- 最终增强 ——aop:after:不管方法目标方法如何结束(包括成功完成和遇到异常两种情况),它都会被织入。这种增强通常用于释放资源。 --> <aop:after method="log" pointcut-ref="addUser"/> <aop:after method="log" pointcut-ref="deleteUser"/> </aop:aspect> <!--定义一个异常拦截的切面--> <aop:aspect ref="myAdvice"> <!--定义切入点:对所有的方法异常进行切入--> <aop:pointcut id="throwableRecover" expression="execution(* cn.zqiheng.*.*.*(..))"/> <!-- 异常增强——aop:after-throwing:主要用于处理程序中未处理的异常。 throwing:该属性只对after-throwing元素有效,用于指定一个形参名,AfterThrowing增强处理方法可通过该形参访问目标方法所抛出的异常。 --> <aop:after-throwing method="doRecover" pointcut-ref="throwableRecover" throwing="ex"/> </aop:aspect> <!--定义一个环绕通知的切面--> <aop:aspect ref="myAdvice"> <!-- 环绕增强——aop:around:功能比较强大的增强处理。 Around增强处理既可以在执行目标方法之前织入增强操作,也可以在执行目标方法之后织入操作。 Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标方法的执行。 --> <aop:around method="processTx" pointcut-ref="deleteUser"/> </aop:aspect> </aop:config></beans> (6)Junit单元测试 AopTest.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748package cn.zqiheng.test;import cn.zqiheng.service.IUserService;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.annotation.Resource;//spring和junit整合//加上这个两个注解,表示我们的spring的容器已经初始化了@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = "classpath:application.xml")public class AopTest { @Resource private IUserService userService; public void setUserService(IUserService userService) { this.userService = userService; } /** * 单元测试:模拟增加一个用户 */ @Test public void test1(){ userService.addUser(); } /** * 单元测试,模拟删除一个用户 */ @Test public void test2(){ userService.deleteUser(2); } /** * 单元测试:模拟根据id查找一个用户的名称 * 模拟产生一个异常,通过增强方法处理 */ @Test public void test3(){ String userName= userService.getUserName(5); System.out.println("用户名:"+userName); }}   这里简单的写了三个单元测试。下面来看看运行结果。 (1)测试结果一 (2)测试结果二 (3)输入正常id测试结果 (3)输入越界id测试结果 2.3.4 基于注解的AOP编程  上面采用的是xml配置来实现AOP编程,为了简化配置,下面采用注解方式来实现与上面同样的功能。   AspectJ允许使用注解定义切面、切入点和增强处理,而Spring框架则可识别并根据这些注解来生成AOP代理。Spring只是使用了和AspectJ5一样的注解,但并没有使用AspectJ的编译器或者织入器,底层依然使用的是Spring AOP,依然是在运行时动态生成AOP代理,并不依赖于AspectJ的编译器或者织入器。   为了启用Spring对@AspecJ切面配置的支持,并保证Spring容器中的目标Bean被一个或多个切面自动增强,必须在Spring配置文件中配置如下代码。application.xml1234<!--开启组件注解扫描,扫描cn.zqiheng及下面的子包--><context:component-scan base-package="cn.zqiheng"/><!--开启注解实现AOP切面编程支持--><aop:aspectj-autoproxy/>   如果不打算使用Spring的XML Schema配置方式,则应该在Spring配置文件中增加如下片段启用@AspectJ支持。(与上面的方式选择一种即可。)application.xml12<!-- 启动@AspectJ支持 --><bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator"/>   上面配置文件中的AnnotationAwareAspectJAutoProxyCreator是一个Bean后处理器,该Bean后处理器将会为容器中的所有Bean生成AOP代理。   下面我们就来使用注解开发的方式来取代上面的xml配置方式(注:Dao层、Service层代码没有改变,改变的只是xml配置文件和自定义的MyAdvice增强类。)。 (1)使用注解方式实现AOP的xml文件 application.xml123456789101112<?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:aop="http://www.springframework.org/schema/aop" 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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--开启组件注解扫描,扫描cn.zqiheng及下面的子包--> <context:component-scan base-package="cn.zqiheng"/> <!--开启注解实现AOP切面编程支持--> <aop:aspectj-autoproxy/></beans>   没错,就是这短短的两句话即可。 (2)修改MyAdvice自定义增强类 MyAdvice.java123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109package cn.zqiheng.aop;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;import java.util.Arrays;/** * 自定义增强类:其中定义增强的方法 */@Component@Aspectpublic class MyAdvice { /** * 集中配置切入点 */ @Pointcut(value = "execution(* cn.zqiheng.dao.impl.UserDaoImpl.addUser(..))") public void addUserPoint(){ } @Pointcut(value = "execution(* cn.zqiheng.dao.impl.*.delete*(..))") public void deletePoint(){} /** * 定义一个普通方法作为Advice方法 * 在用户执行添加和删除前,检测用户权限 * 前置增强(Before) */ //使用注解方式实现AOP编程,以cn.zqiheng.dao.impl包下的addUser()和以delete开头的方法为切入点,增强方法为注解下面的checkRoo @Before(value = "addUserPoint() || deletePoint()") public void checkRoot(JoinPoint joinPoint){ System.out.println("=========================注解:before前置增强:模拟用户权限操作========================="); System.out.println("注解:before前置增强:目标对象:"+joinPoint.getTarget()+" 执行目标方法:"+joinPoint.getSignature().getName()+" 参数列表:"+Arrays.toString(joinPoint.getArgs())); } /** * 定义一个普通方法作为Advice方法 * 在添加和删除方法正常执行后,打印操作成功语句 * 后置增强(AfterReturning) */ @AfterReturning(value = "addUserPoint()") public void success(){ System.out.println("=========================注解:after-returning后置增强:方法成功执行========================="); } /** * 定义一个普通方法作为Advice方法 * 在添加和删除方法执行后,记录操作日志 * 最终增强(After) */ @After(value = "addUserPoint()") public void log(){ System.out.println("=========================注解:after最终增强:模拟日志记录操作========================="); } /** * 定义一个普通方法作为Advice方法 * 在目标方法方式异常后,执行此方法 * 异常增强(AfterThrowing) * @param ex 形参用于访问目标方法中抛出的异常 */ //注解实现异常增强,value:切入点;throwing:表示返回的异常 @AfterThrowing( value = "execution(* cn.zqiheng.*.*.*(..))",throwing = "ex") public void doRecover(Throwable ex){ System.out.println("=========================注解:after-throwing后置增强:发生异常========================="); System.out.println("=========================注解:after-throwing后置增强:目标方法抛出的异常:"+ex); System.out.println("=========================注解:after-throwing后置增强:模拟Advice对异常的修复========================="); } /** * 定义一个普通方法作为Advice方法 * 审核事务方法:在目标方法执行前后都增强 * 环绕增强(Around) * @param joinPoint 必须的参数,ProceedingJoinPoint内的proceed()方法才能执行目标方法 */ //注解实现环绕增强的实现,环绕增强是一个强大的增强处理,可以完成上述所有增强的实现。 @Around(value = "deletePoint()") public Object processTx(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("=========================注解:around环绕增强:执行目标方法之前,模拟开始事务(前置增强)========================="); System.out.println("目标对象:"+joinPoint.getTarget()+" 执行目标方法:"+joinPoint.getSignature().getName()+" 参数列表:"+Arrays.toString(joinPoint.getArgs())); try{ //访问执行目标方法的参数 Object[] args = joinPoint.getArgs(); //当执行目标方法的参数存在,且第一个参数是字符时 if(args !=null && args.length > 0 && args[0].getClass() == String.class){ //修改目标方法调用参数的第一个参数 args[0] = "【增强的前缀】" + args[0]; } /** * 重点:执行目标方法,并保存目标方法执行后的返回值 */ Object rvt = joinPoint.proceed(args); //目标方法执行时 System.out.println("=========================注解:around环绕增强:执行目标方法之后,模拟结束事务(后置增强)========================="); //如果rvt的类型是Integer,将rvt改为它的平方 if(rvt != null && rvt instanceof Integer){ rvt = (Integer)rvt * (Integer) rvt; } return rvt; }catch (Throwable e){ System.out.println("=========================注解:around环绕增强:执行目标方法的时候,模拟异常处理(异常增强)========================="); }finally { System.out.println("=========================注解:around环绕增强:执行目标方法之后,模拟日志揭露(最终增强)========================="); } return ""; }} (3)测试   使用AOP编程只做了两步。第一步修改了xml配置文件;第二步修改了MyAdvice.java代码。其它都没有变,因此其它代码和xml中代码一致。下面是测试结果。 (1)测试结果一 (2)测试结果二 (3)输入正常id测试结果 (3)输入越界id测试结果 总结: 1)Around增强处理可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值。 2)Around增强处理的功能虽然强大,但通常需要在线程安全的环境下使用。 3)如果使用普通的Before增强处理、AfterReturning增强处理就能解决的问题,就没有必要使用Around增强处理了。如果需要目标方法执行之前和之后共享某种状态数据,尤其是需要改变目标方法的返回值使,则应该考虑使用Around增强处理。 4)当使用Around增强处理时,需要将第一个参数定义为ProceedingJionPoin类型,该类型时JoinPoint类型的子类。 注意:  使用aop:config.../方式进行xml配置时,可能与Spring的自动代理方式相冲突。因此建议:要么全部使用aop:config.../xml配置方式,要么全部使用自动代理方式,不要两者混合使用。   Spring简单的介绍和使用就先记录到这里!]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
<category>Spring</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title><![CDATA[在IDEA上搭建Spring+SpringMVC+Hibernate项目]]></title>
<url>%2F2018%2F06%2F19%2F%E5%9C%A8IDEA%E4%B8%8A%E6%90%AD%E5%BB%BASpring%2BSpringMVC%2BHibernate%E9%A1%B9%E7%9B%AE%2F</url>
<content type="text"><![CDATA[说明:本章主要介绍如何在idea上搭建一个简单的基于Maven的Spring+SpringMVC+hibernate项目。并没有细分各个业务逻辑层次划分。 参考博文 大家如果想深入剖析项目搭建步骤可参考上面博客。 重点:准备工作 安装好jdk1.8以上版本、配好环境变量 安装好Tomcat(我是Tomcat9) 安装好maven (也可选择IDEA自带的) 安装好IDEA(上篇博客已经介绍了完整版的idea安装和破解,大家可以参考。) 一.创建一个新项目1. 点击左上角 File -> new project -> maven ->webapp。 2. 填项目名称 3. 选择maven版本 4. 进入页面后在右下角会弹出一个对话框,选择导入依赖包(没有则不管) 5. 这是默认建好的目录 二. 项目建好过后,我们就要进行配置了1.使用pom.xml导入依赖包,先考虑你要做什么项目,需要什么包,这个自己好好想一下,其实基本的包都类似,主要的是版本不同因为我们是用Maven搭建项目的,只需要在根目录下的pom.xml文件中加入需要的依赖包版本信息,就可以自动下载导入所有我们需要的包(这个还是很贴心的): pom.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239<!--版本号--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <spring.version>4.3.1.RELEASE</spring.version> </properties> <!-- 依赖从这开始 --> <dependencies> <!--日志包--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <!--j2ee相关包 servlet、jsp、jstl--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--mysql驱动包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> </dependency> <!-- 添加Hibernate依赖 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>3.6.0.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/javassist/javassist --> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>3.12.1.GA</version> </dependency> <!--spring相关包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <!--其他需要的包--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!-- <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.0</version> </dependency> --> <!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.7.0</version> </dependency> <!-- 导入dbcp的jar包,用来在applicationContext.xml中配置数据库 --> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.2.2</version> </dependency> </dependencies> <build> <finalName>ssm</finalName> <resources> <!--表示把java目录下的有关xml文件,properties文件编译/打包的时候放在resource目录下--> <resource> <directory>${basedir}/src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> <resource> <directory>${basedir}/src/main/resources</directory> </resource> </resources> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.20.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> 2. maven是自动导包,导完过后我们就可以看到我们需要的依赖包已经全部导入了 三. SpringMVC配置1. 先建好需要我们手动建立的必须文件夹,在界面右上角点击 Project Structure -> Modules 建立所缺少的java、resources文件夹,并标记属性。 建好后的目录: 2. 配置web.xml如果生成的web.xml版本不是3.0的,需要手动改为3.0同时需要在resources下创建spring/spring-mvc.xml文件,因为在web.xml配置中我们需要引用,先建立文件夹,后面再配置 web.xml 代码如下: web.xml1234567891011121314151617181920212223242526272829303132<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!--welcome pages--> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!--配置springmvc DispatcherServlet--> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!--resources标注的文件夹下需要新建一个spring文件夹--> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping></web-app> 3. 配置spring-mvc.xml现在我们创建的java目录下创建的基础包com.test.controller包,在spring-mvc.xml中需要指明这个包,然后在这个包下创建的Controller类使用的@Controller注解才会生效。 spring-mvc.xml 代码如下: spring-mvc.xml123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960<?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-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--启用spring的一些annotation --> <context:annotation-config/> <!-- 自动扫描com.test.controller包,使SpringMVC认为包下用了@controller注解的类是控制器 --> <context:component-scan base-package="com.test.controller"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 配置注解驱动 可以将request参数与绑定到controller参数上 --> <mvc:annotation-driven/> <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀(如果最后一个还是表示文件夹,则最后的斜杠不要漏了) 使用JSP--> <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/views/"/><!--设置JSP文件的目录位置--> <property name="suffix" value=".jsp"/> </bean> <!-- springmvc文件上传需要配置的节点--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="20971500"/> <property name="defaultEncoding" value="UTF-8"/> <property name="resolveLazily" value="true"/> </bean> <!-- 使用jackjson,默认将返回对象转换为 JSON,如果前面没有在pom文件中添加jackson包的不需要这个配置 --> <bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/plain;charset=UTF-8</value> </list> </property> </bean> <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="stringConverter" /> <ref bean="jsonConverter" /> </list> </property> </bean></beans> 4. 基本配置弄好了,接下来我们就来配置服务器 然后点OK 5. 接口现在com.test.controller下创建UserController类 然后创建com.test.entity包,在包下创建一个UserEntity类 UserController.java1234567891011121314@Controller@RequestMapping("/sshTest")//Contoller下所有接口统一入口public class UserController { //映射一个action @RequestMapping("/getuser") @ResponseBody//表示直接输出返回内容,不进行jsp或html跳转,本例是为了写接口,这里直接返回json public UserEntity getUser() { //创建一个UserEntity,直接返回 UserEntity user = new UserEntity("heng", "123456"); return user; }} UserEntity.java1234567891011121314151617181920212223242526272829public class UserEntity { private String username; private String password; public UserEntity(String username, String password) { this.username = username; this.password = password; } public UserEntity() { } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; }} 注:这里大家可以自己百度下如何设置自动导包和手动导包,以及类属性的get和set方法,以及构造方法怎么生成的。 6. 启动Tomcat服务器,然后打开浏览器,输入 http://localhost:8080/sshTest/getuser 查看是否成功。 springMVC集成完毕,接下来就是hibernate的配置了 四. Hibernate配置先把我们需要用的窗口调出来 view-> 1. 配置hibernate.cfg.xml首先我们要创建hibernate.cfg.xml文件,idea提供了直接自动生成hibernate配置文件的功能。点击右上角的 Project Structure -> Modules -> + -> Hibernate 然后点OK,结果如下图: hibernate.cfg.xml 代码如下: hibernate.cfg.xml123456789101112131415161718192021222324<?xml version='1.0' encoding='utf-8'?><!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"><hibernate-configuration> <session-factory> <!--配置连接数据库的基本信息--> <property name="connection.username">root</property> <property name="connection.password">root</property> <!--mysql驱动--> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/test</property> <!-- 配置 Hibernate 的基本信息 --> <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <!-- 指定自动生成数据表的策略 --> <property name="hbm2ddl.auto">update</property> </session-factory></hibernate-configuration> 2. hibernate配置文件生成了,接下来我们就使用hibernate的自动映射出数据库实体类的功能,这里idea都提供了可视化操作,非常简单,我们要先为idea也配置一个数据库连接,点击Database。 结果如下: 到这里idea的MySQL连接也配置好了,接下来就可以直接使用可视化工具创建hibernate实体类,点击Persistence,右键配置好的hibernate–>Generate Persitence Mapping–>by Database schema 接下来这张表是我自己已经创建的表: 注:想要参考如何建表,请参考最后附录 接下来就可以看到我们自动生成的持久化注解类: 到这里我们的hibernate的集成也已经完成,接下来做一次对studentInfo表的查询操作,并将查询到的数据以json的形式返回在浏览器上。 1.新建com.test.dao包2.创建一个StudentDao的逻辑类,用于对studentInfo表的增删改查 StudnetDao.java1234567891011121314151617181920212223242526272829303132333435public class StudentDao { /** * 查询表中所有数据 * */ public List<StudentinfoEntity> query() { Session session = null; List<StudentinfoEntity> list = null; try { //实例化Configuration,这行代码默认加载hibernate.cfg.xml文件 Configuration conf = new Configuration().configure(); //以Configuration创建SessionFactory SessionFactory sf = conf.buildSessionFactory(); //实例化Session session = sf.openSession(); String hql = "from StudentinfoEntity"; Query query = session.createQuery(hql); list = query.list(); } catch (HibernateException e) { e.printStackTrace(); return null; } finally { if (session != null) { session.close(); } } return list; }} 接下来就在包controller下创建一个StudentController.java类,调用StudentDao接口实现查询学生信息: StudentController.java123456789101112131415@Controller@RequestMapping("/student")//Contoller下所有接口统一入口public class StudentController { //映射一个action @RequestMapping("/studentList") @ResponseBody public List<StudentinfoEntity> getUser() { StudentDao dao = new StudentDao(); //查询studentInfo的所有数据,返回json格式 return dao.query(); }} 五. OK,最后一步重启Tomcat服务器,在浏览器地址栏,输入 http://localhost:8080/student/studentList 附录:注:我写的数据库名为test,自己改 studenetInfo表创建: 123456789101112131415SET FOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for studentinfo-- ----------------------------DROP TABLE IF EXISTS `studentinfo`;CREATE TABLE `studentinfo` ( `id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `passwrod` varchar(255) NOT NULL, `age` int(11) NOT NULL, `address` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8; 数据插入: 12345INSERT INTO `studentinfo` VALUES (1, '恒', '123', 20, '成都');INSERT INTO `studentinfo` VALUES (2, 'aaaa', '123', 20, '北京');INSERT INTO `studentinfo` VALUES (3, 'bbbb', '123', 20, '上海');INSERT INTO `studentinfo` VALUES (4, 'cccc', '123456', 20, '深圳');]]></content>
<categories>
<category>Java</category>
<category>Java EE</category>
</categories>
<tags>
<tag>Java</tag>
<tag>Java EE</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IDEA开发工具安装与破解]]></title>
<url>%2F2018%2F06%2F19%2FIDEA%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7%E5%AE%89%E8%A3%85%E4%B8%8E%E7%A0%B4%E8%A7%A3%2F</url>
<content type="text"><![CDATA[说明:本章专门介绍Idea开发工具的安装与破解(当然你要是有钱任性,也可以不用破解) 安装步骤1. 进入IDEA官网下载安装包 官网链接 1.1在首页点击download下载安装包,注意我们这里是下载完整版(Ultimate),因为社区办不支持Java EE、Spring开发。 1.2安装过程都是傻瓜式安装,没有其它需求一直next就行。(注意:其中有个环节选择64位版就行,其它可以不用选(.java .kt 等等)) 2.安装完成后配置2.1 主题有默认黑色,还有其它可自定义2.2 激活 首先,编辑C:\Windows\System32\drivers\etc下的hosts文件,在文件最后加入下面一行: 0.0.0.0 account.jetbrains.com 如果你没有权限修改,先将hosts文件剪切到桌面,在桌面上打开文件,在进行添加上面代码。然后在剪切回去。 在激活界面选择Activation code方式激活,并填入下列激活码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZvciBlZHVjYXRpb25hbCB1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJSUzAiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJXUyIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IlJEIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJEQyIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IkRCIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUk0iLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJETSIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IkFDIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiRFBOIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUFMiLCJwYWlkVXBUbyI6IjIwMTgtMTAtMTQifSx7ImNvZGUiOiJDTCIsInBhaWRVcFRvIjoiMjAxOC0xMC0xNCJ9LHsiY29kZSI6IlBDIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In0seyJjb2RlIjoiUlNVIiwicGFpZFVwVG8iOiIyMDE4LTEwLTE0In1dLCJoYXNoIjoiNjk0NDAzMi8wIiwiZ3JhY2VQZXJpb2REYXlzIjowLCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-Gbb7jeR8JWOVxdUFaXfJzVU/O7c7xHQyaidCnhYLp7v32zdeXiHUU7vlrrm5y9ZX0lmQk3plCCsW+phrC9gGAPd6WDKhkal10qVNg0larCR2tQ3u8jfv1t2JAvWrMOJfFG9kKsJuw1P4TozZ/E7Qvj1cupf/rldhoOmaXMyABxNN1af1RV3bVhe4FFZe0p7xlIJF/ctZkFK62HYmh8V3AyhUNTzrvK2k+t/tlDJz2LnW7nYttBLHld8LabPlEEjpTHswhzlthzhVqALIgvF0uNbIJ5Uwpb7NqR4U/2ob0Z+FIcRpFUIAHEAw+RLGwkCge5DyZKfx+RoRJ/In4q/UpA==-MIIEPjCCAiagAwIBAgIBBTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE1MTEwMjA4MjE0OFoXDTE4MTEwMTA4MjE0OFowETEPMA0GA1UEAwwGcHJvZDN5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQC9WZuYgQedSuOc5TOUSrRigMw4/+wuC5EtZBfvdl4HT/8vzMW/oUlIP4YCvA0XKyBaCJ2iX+ZCDKoPfiYXiaSiH+HxAPV6J79vvouxKrWg2XV6ShFtPLP+0gPdGq3x9R3+kJbmAm8w+FOdlWqAfJrLvpzMGNeDU14YGXiZ9bVzmIQbwrBA+c/F4tlK/DV07dsNExihqFoibnqDiVNTGombaU2dDup2gwKdL81ua8EIcGNExHe82kjF4zwfadHk3bQVvbfdAwxcDy4xBjs3L4raPLU3yenSzr/OEur1+jfOxnQSmEcMXKXgrAQ9U55gwjcOFKrgOxEdek/Sk1VfOjvS+nuM4eyEruFMfaZHzoQiuw4IqgGc45ohFH0UUyjYcuFxxDSU9lMCv8qdHKm+wnPRb0l9l5vXsCBDuhAGYD6ss+Ga+aDY6f/qXZuUCEUOH3QUNbbCUlviSz6+GiRnt1kA9N2Qachl+2yBfaqUqr8h7Z2gsx5LcIf5kYNsqJ0GavXTVyWh7PYiKX4bs354ZQLUwwa/cG++2+wNWP+HtBhVxMRNTdVhSm38AknZlD+PTAsWGu9GyLmhti2EnVwGybSD2Dxmhxk3IPCkhKAK+pl0eWYGZWG3tJ9mZ7SowcXLWDFAk0lRJnKGFMTggrWjV8GYpw5bq23VmIqqDLgkNzuoog== 注:如果该激活码有问题可到 http://idea.lanyus.com/ ,获取激活码 最后:完成安装、可以建一个简单的java项目测试一下安装是否成功…]]></content>
<categories>
<category>开发工具</category>
</categories>
<tags>
<tag>开发工具</tag>
</tags>
</entry>
<entry>
<title><![CDATA[GitHub + Hexo 搭建]]></title>
<url>%2F2018%2F06%2F15%2FGitHub%2BHexo%E6%90%AD%E5%BB%BA%2F</url>
<content type="text"><![CDATA[说明这里还是简单的记录一下基于GitHub+Hexo+Next的个人博客搭建把,防止以后忘了… 搭建步骤 1. Git下载与安装Git是目前非常受欢迎的一个开源的分布式版本控制系统,用于敏捷高效地处理任何或大或小的项目。具体的一些介绍和使用的不具体写了,自己看教程。 菜鸟教程链接官网下载链接 2. GitHub 账号注册gitHub是一个面向开源及私有软件项目的托管平台,因为只支持git 作为唯一的版本库格式进行托管,故名gitHub。作为一个程序猿,现在基本都有一个GitHub账号,来托管自己的代码,如果你没有那就Out了。 官方地址链接 3. Node.js 下载与安装Node.js也是目前特别火的一个后台服务端脚本语言。简单的说就是运行在服务端的JavaScript。基于Chrome JavaScript运行时建立的一个平台。运行速度快、性能非常好。 菜鸟教程链接官网下载链接 4. 搭建个人博客Hexo 安装Hexo,在本地电脑随一个磁盘新建一个文件夹,比如取名Blog来存放自己的博客。在文件夹内右击点击Git Bash Here进入命令行窗口,执行以下代码: 1npm install -g hexo-cli 初始化Hexo,得到hexo文件夹,用于存放Hexo博客的所有文件。还是在刚刚那个窗口中执行以下代码: 1hexo init hexo 配置Hexo,安装hexo依赖文件,生成部署文件,分别执行以下代码: 123cd hexonpm installhexo generate 继续在命令行窗口输入以下代码,启动服务器(hexo server == hexo s): 1hexo server 随便打开一个浏览器,地址栏输入 http://localhost:4000/ ,如果成功显示Hexo页面,则搭建成功。 将Hexo 博客部署到GitHub中 新建一个仓库,命名为:username.github.id 在本地命令行配置Git: 在Blog文件夹目录右击打开Git Bash窗口,输入以下命名 git config -- global user.name "GitHub 用户名" git config -- global user.email "GitHub 邮箱" 重点:生成 SSH KEY,其实就是生成一个公钥和密钥,GitHub需要一个密钥才能与本地相连接,执行以下命名,并且连续按3次回车生成密钥(注:C 为大写): ssh-keygen -t rsa -C "邮箱地址" 后面会有一个地址,可以看到。就是我们生成的密钥保存地址。即:C:/Users/用户名/.ssh文件中。打开这个目录找到 id_rsa.pub 文件,复制全部内容。 在网页打开GitHub,依次点击 我的头像 - Settings - SSH and GPG keys - New SSH key,将刚刚复制的密钥内容粘贴到key的输入框,然后点击Add Key,配置成功。 修改hexo 文件下的_config.yml (站点配置文件),修改 deploy 属性代码,将本地 hexo 项目托管到 GitHub 上,如下所示: deploy: type: git #部署的类型 repository: [email protected]:zqiheng/zqiheng.github.io.git # 仓库地址 branch: master #分支名称 message: hexo deploy #提交信息 然后执行下面的命令,安装hexo-deployer-git插件,快速把代码托管到GitHub上面 npm install hexo-deployer-git --save 最后执行下列命令,将hexo项目托管到GitHub平台上 hexo clean hexo generate hexo deploy 或: hexo clean hexo g hexo d 然后在浏览器输入:https://username.github.io/ 访问,可以看到自己部署的博客 最后说一下Hexo全局配置文件,具体代码如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293# Hexo Configuration## Docs: https://hexo.io/docs/configuration.html## Source: https://github.com/hexojs/hexo/# Site 站点信息配置title: Heng Bolg #站点名subtitle: 世上本没有路,走的人多了也就有了路... #站点副标题description: 主要记录自己的学习记录与心得,在校学生一枚... #站点信息简介keywords:author: 恒 #站点作者language: zh-CN #站点语言,default 默认是英语, zh-CN 是中文timezone: #时区,不填avatar: /images/logo.png #图片# URL 博客地址## If your site is put in a subdirectory, set url as 'http://yoursite.com/child' and root as '/child/'url: https://zqiheng.github.ioroot: /permalink: :year/:month/:day/:title/permalink_defaults:# Directory 目录设置source_dir: source #源文件public_dir: public #生成的网页文件tag_dir: tags #标签archive_dir: archives #归档category_dir: categories #分类code_dir: downloads/code #i18n_dir: :lang #国际化skip_render:# Writing 文章布局new_post_name: :title.md # File name of new postsdefault_layout: post #默认模板titlecase: false # 标题转换成大写external_link: true # 新标签页里打开连接filename_case: 0render_drafts: falsepost_asset_folder: falserelative_link: falsefuture: truehighlight: #代码块设置 enable: true #语法高亮 line_number: true #显示行号 auto_detect: false tab_replace: # Home page setting 页设置# path: Root path for your blogs index page. (default = '')# per_page: Posts displayed per page. (0 = disable pagination)# order_by: Posts order. (Order by date descending by default)index_generator: path: '' per_page: 4 #每页文章数量 order_by: page #-date # Category & Tag 分类和标签default_category: uncategorizedcategory_map:tag_map:# Date / Time format 日期/格式## Hexo uses Moment.js to parse and display date## You can customize the date format as defined in## http://momentjs.com/docs/#/displaying/format/date_format: YYYY-MM-DDtime_format: HH:mm:ss# Pagination 归档显示## Set per_page to 0 to disable paginationper_page: 10pagination_dir: page# Extensions 扩展## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: next# Deployment 站点部署到GitHub上## Docs: https://hexo.io/docs/deployment.htmldeploy: type: git #部署的类型 repository: [email protected]:zqiheng/zqiheng.github.io.git # 仓库地址 branch: master #分支名称 message: hexo #提交信息#查找search: path: search.xml field: post format: html limit: 10000 5. Next下载与配置—更新时间:2018.06.17— 下载Next主题,在Hexo目录下右击进入Git Bash Here 窗口,输入下列命令 1git clone https://github.com/iissnan/hexo-theme-next themes/next 启动主题 当克隆下载完成后,打开站点配置文件(hexo目录下的_config.yml),找到 theme字段,并将其值更改为next 1234# Extensions 扩展## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: next 重新启动服务器,在浏览器地址栏输入http://localhost:4000访问,检测配置是否成功。 主题文件配置,目录为(hexo\themes\next)下的_config.yml文件。 我使用的主题样式是: 12345# Schemes#scheme: Muse#scheme: Mistscheme: Pisces#scheme: Gemini 配置左侧导航栏菜单按钮,在主题配置文件中,找到menu属性,做如下配置: 123456789menu: home: / || home #首页,后面的表示图标 categories: /categories/ || th #分类 archives: /archives/ || archive #归档 tags: /tags/ || tags #标签 about: /about/ || user #关于 #schedule: /schedule/ || calendar #sitemap: /sitemap.xml || sitemap #commonweal: /404/ || heartbeat 为标签categories、tags、about,创建相应的界面,代码如下: 123hexo new page 'categories'hexo new page 'tags'hexo new page 'about' 在 source目录下,会看到刚刚生成的三个文件夹,依次打开文件夹,给每个index.md文件添加上type字段。 配置左侧导航栏搜索按钮 (1) 安装 hexo-generator-searchdb ,在Git Bash Here窗口执行以下代码: 1npm install hexo-generator-searchdb --save (2) 打开全局配置文件,在最后新增下面代码; 12345search: path: search.xml field: post format: html limit: 10000 (3) 打开主题配置文件,找到local_search属性,开启本地搜索功能 1234567local_search: enable: true # if auto, trigger search by changing input # if manual, trigger search by pressing enter key or search button trigger: auto # show top n results per article, show all results by setting to -1 top_n_per_article: 1 (4) 重新部署项目,并发布到GitHub上 123hexo cleanhexo ghexo d   基本配置就是这样,其它功能后面再补充]]></content>
<categories>
<category>Hexo</category>
<category>个人博客搭建</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hello Hexo]]></title>
<url>%2F2018%2F06%2F15%2FHello-Hexo%2F</url>
<content type="text"><![CDATA[Hexo简介:简而言之,Hexo 是一个基于 Node.js 的静态博客程序,可以方便的生成静态网页托管在github和Heroku上。其作者是来自台湾的tommy351大神。Hexo 因其界面简洁、美观且对各类人群(不只是程序猿)友好而广受欢迎,声望不亚于大名鼎鼎的WordPress。 说明为什么需要一个博客对于个人网站来说,没有比博客更合适的形式了。在博客中,文章才是最主要的,一切都显得主次分明,干净利落。相比之下,论坛中主题和回复鱼龙混杂,阅读体验非常差。同时,博客比论坛的数据库小很多,便于维护。 为什么是静态博客很多人选择在虚拟主机或vps上面搭建动态博客。独立博客如此麻烦的维护工作,能不能减轻一些呢?正如阮一峰前辈所说,blogger分为三个阶段。最开始,是门户博客。之后,是独立博客。最后,觉得独立博客自己管理起来费劲,便找个别人来管的空间,自己负责写就好。如果我们能够找到这样的空间,在自己保留最大控制权前提下,由别人托管,会省去不少事情。 静态博客编译之后是纯html页面,优点就是支持它的环境十分好找,例如github、gitcafe等站点都支持静态页面托管,自然是我们的首选了。 但是静态博客并非没有缺点。动态博客更新文章时,脚本是不变的,只需要更新数据库。静态博客要频繁改动文件,不支持增量式上传的东西,比如ftp,就难于管理。此外,还要十分熟悉git各种命令,才能部署页面。]]></content>
<categories>
<category>Hexo</category>
<category>简介</category>
</categories>
<tags>
<tag>Hexo</tag>
</tags>
</entry>
</search>