• 问题发布
• HTML/敏感词过滤
• 多线程
• 问题发布
1:增加提问;
@RequestMapping(value = "/question/add", method = {RequestMethod.POST})
@ResponseBody
public
String
addQuestion
(
@RequestParam
(
"title"
)
String
title
,
@RequestParam
(
"content"
)
String
content
)
{
try
{
Question
question
=
new
Question
()
;
question
.
setContent
(
content
)
;
question
.
setCreatedDate
(
new
Date
())
;
question
.
setTitle
(
title
)
;
if
(
hostHolder
.
getUser
()
==
null
)
{
question
.
setUserId
(
WendaUtil
.
ANONYMOUS_USERID
)
;
// return WendaUtil.getJSONString(999);
}
else
{
question
.
setUserId
(
hostHolder
.
getUser
()
.
getId
())
;
}
if
(
questionService
.
addQuestion
(
question
)
>
0
)
{
return
WendaUtil
.
getJSONString
(
0
)
;
}
}
catch
(
Exception
e
)
{
logger
.
error
(
"增加题目失败"
+
e
.
getMessage
())
;
}
return
WendaUtil
.
getJSONString
(
1
,
"失败"
)
;
}
2:
• HTML/敏感词过滤
1: HTML恶意代码;
防止用户提交问题给服务器,然后前端显示的时候,直接通过浏览器转义后,暴漏各种信息
;
直接调用方法:
HtmlUtils.htmlEscape(question.getTitle()));
HtmlUtils.htmlEscape(question.getContent()));
2:敏感词过滤;通过字典树算法实现对于敏感词的过滤,是一个算法;
@Service
public class
SensitiveService
implements
InitializingBean
{
private static final
Logger
logger
=
LoggerFactory
.
getLogger
(
SensitiveService
.
class
)
;
/**
* 默认敏感词替换符
*/
private static final
String
DEFAULT_REPLACEMENT
=
"敏感词"
;
private class
TrieNode
{
/**
* true 关键词的终结 ; false 继续
*/
private boolean
end
=
false
;
/**
* key下一个字符,value是对应的节点
*/
private
Map
<
Character
,
TrieNode
>
subNodes
=
new
HashMap
<>
()
;
/**
* 向指定位置添加节点树
*/
void
addSubNode
(
Character
key
,
TrieNode
node
)
{
subNodes
.
put
(
key
,
node
)
;
}
/**
* 获取下个节点
*/
TrieNode
getSubNode
(
Character
key
)
{
return
subNodes
.
get
(
key
)
;
}
boolean
isKeywordEnd
()
{
return
end
;
}
void
setKeywordEnd
(
boolean
end
)
{
this
.
end
=
end
;
}
public int
getSubNodeCount
()
{
return
subNodes
.
size
()
;
}
}
/**
* 根节点
*/
private
TrieNode
rootNode
=
new
TrieNode
()
;
/**
* 判断是否是一个符号
*/
private boolean
isSymbol
(
char
c
)
{
int
ic
=
(
int
)
c
;
// 0x2E80-0x9FFF 东亚文字范围
return
!
CharUtils
.
isAsciiAlphanumeric
(
c
)
&&
(
ic
<
0x2E80
||
ic
>
0x9FFF
)
;
}
/**
* 过滤敏感词
*/
public
String
filter
(
String
text
)
{
if
(
StringUtils
.
isBlank
(
text
))
{
return
text
;
}
String
replacement
=
DEFAULT_REPLACEMENT
;
StringBuilder
result
=
new
StringBuilder
()
;
TrieNode
tempNode
=
rootNode
;
int
begin
=
0
;
// 回滚数
int
position
=
0
;
// 当前比较的位置
while
(
position
<
text
.
length
())
{
char
c
=
text
.
charAt
(
position
)
;
// 空格直接跳过
if
(
isSymbol
(
c
))
{
if
(
tempNode
==
rootNode
)
{
result
.
append
(
c
)
;
++
begin
;
}
++
position
;
continue
;
}
tempNode
=
tempNode
.
getSubNode
(
c
)
;
// 当前位置的匹配结束
if
(
tempNode
==
null
)
{
// 以begin开始的字符串不存在敏感词
result
.
append
(
text
.
charAt
(
begin
))
;
// 跳到下一个字符开始测试
position
=
begin
+
1
;
begin
=
position
;
// 回到树初始节点
tempNode
=
rootNode
;
}
else if
(
tempNode
.
isKeywordEnd
())
{
// 发现敏感词, 从begin到position的位置用replacement替换掉
result
.
append
(
replacement
)
;
position
=
position
+
1
;
begin
=
position
;
tempNode
=
rootNode
;
}
else
{
++
position
;
}
}
result
.
append
(
text
.
substring
(
begin
))
;
return
result
.
toString
()
;
}
private void
addWord
(
String
lineTxt
)
{
TrieNode
tempNode
=
rootNode
;
// 循环每个字节
for
(
int
i
=
0
;
i
<
lineTxt
.
length
()
; ++
i
)
{
Character
c
=
lineTxt
.
charAt
(
i
)
;
// 过滤空格
if
(
isSymbol
(
c
))
{
continue
;
}
TrieNode
node
=
tempNode
.
getSubNode
(
c
)
;
if
(
node
==
null
)
{
// 没初始化
node
=
new
TrieNode
()
;
tempNode
.
addSubNode
(
c
,
node
)
;
}
tempNode
=
node
;
if
(
i
==
lineTxt
.
length
()
-
1
)
{
// 关键词结束, 设置结束标志
tempNode
.
setKeywordEnd
(
true
)
;
}
}
}
@Override
public void
afterPropertiesSet
()
throws
Exception
{
rootNode
=
new
TrieNode
()
;
try
{
InputStream
is
=
Thread
.
currentThread
()
.
getContextClassLoader
()
.
getResourceAsStream
(
"SensitiveWords.txt"
)
;
InputStreamReader
read
=
new
InputStreamReader
(
is
)
;
BufferedReader
bufferedReader
=
new
BufferedReader
(
read
)
;
String
lineTxt
;
while
((
lineTxt
=
bufferedReader
.
readLine
())
!=
null
)
{
lineTxt
=
lineTxt
.
trim
()
;
addWord
(
lineTxt
)
;
}
read
.
close
()
;
}
catch
(
Exception
e
)
{
logger
.
error
(
"读取敏感词文件失败"
+
e
.
getMessage
())
;
}
}
public static void
main
(
String
[]
argv
)
{
SensitiveService
s
=
new
SensitiveService
()
;
s
.
addWord
(
"色情"
)
;
s
.
addWord
(
"好色"
)
;
System
.
out
.
print
(
s
.
filter
(
"你好X色**情XX"
))
;
}
}
• 多线程
1. extends Thread,重载run()方法
2. implements Runnable(),实现run()方法
new Thread(
new Runnable() {
@Override
public void run() {
Random random =
new Random();
for (
int i = 0; i < 10; ++i) {
sleep( random.nextInt( 1000 ) );
System.out.println(
String.format( "T%d : %d", tid, i ) );
}
}
}
String.valueOf( i ) ).start();
3:Synchronized-内置锁
1. 放在方法上会锁住所有synchronized方法
2. synchronized(obj) 锁住相关的代码段
public static void testSynchronized1() {
synchronized (
obj) {
Random random =
new Random();
for (
int i = 0; i < 10; ++i) {
sleep( random.nextInt( 1000 ) );
}
}
}
4:BlockingQueue 同步队列
支持两个附加操作的
Queue
,这两个操作是:获取元素时等待队列变为非空,以及存储元素时等待空间变得可用。
BlockingQueue
方法以四种形式出现,对于不能立即满足但可能在将来某一时刻可以满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,第二种是返回一个特殊值(
null
或
false
,具体取决于操作),第三种是在操作可以成功前,无限期地阻塞当前线程,第四种是在放弃前只在给定的最大时间限制内阻塞。
![](%E7%AC%AC%E4%BA%94%E8%AF%BE%EF%BC%9A%E9%97%AE%E9%A2%98%E7%9A%84%E5%8F%91%E5%B8%83%EF%BC%8C%E6%95%8F%E6%84%9F%E8%AF%8D%E8%BF%87%E6%BB%A4%EF%BC%8C%E5%A4%9A%E7%BA%BF%E7%A8%8B%E9%97%AE%E9%A2%98_files/Image.png)
5:
ThreadLocal
1. 线程局部变量。即使是一个static成员,每个线程访 问的变量是不同的。
2. 常见于web中存储当前用户到一个静态工具类中,在 线程的任何地方都可以访问到当前线程的用户。
3. 参考HostHolder.java里的users
6:Executor
1. 提供一个运行任务的框架。
2. 将任务和如何运行任务解耦。
3. 常用于提供线程池或定时任务服务
ExecutorService
service
=
Executors
.
newFixedThreadPool
(
2
)
;
service
.
submit
(
new
Runnable
()
{
@Override
public void
run
()
{
for
(
int
i
=
0
;
i
<
10
; ++
i
)
{
sleep
(
1000
)
;
System
.
out
.
println
(
"Execute %d"
+
i
)
;
}
}
}
)
;
7:Future
public static void testFuture() {
ExecutorService service = Executors.newSingleThreadExecutor();
Future <Integer> future = service.submit( new Callable <Integer>() {
@Override
public Integer call() throws Exception {
sleep( 1000 ); //throw new IllegalArgumentException(" 一个异常 "); return 1; } }); service.shutdown();
try {
System.out.println( future.get() ); //System.out.println(future.get(100, TimeUnit.MILLISECONDS)); } catch (Exception e) { e.printStackTrace(); }
}