牛客社区(四)
一、拦截器
拦截器有三个方法 preHandle、postHandle、afterHandle。
- preHandle:在Controller之前执行
- postHandle:在Controller之后执行
- afterHandle:在TemplateEngine之后执行
①编写自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
②注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginRequiredInterceptor loginRequiredInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginRequiredInterceptor)
.excludePathPatterns("/**/*.css","/**/*.js","/**/*.png","/**/*.jpg","/**/*.jpeg");
}
③再controller添加拦截器注解
@LoginRequired
@RequestMapping(path = "/setting",method = RequestMethod.GET)
public String getSetting(){
return "/site/setting";
}
@LoginRequired
@RequestMapping(path = "/upload",method = RequestMethod.POST)
public String uploadHeader(MultipartFile headerImage, Model model){
if (headerImage == null){
model.addAttribute("error","您还没有选择照片");
return "/site/setting";
}
String filename = headerImage.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
if (StringUtils.isBlank(suffix)){
model.addAttribute("error","文件的格式不正确");
return "/site/setting";
}
//生成随机文件名
filename = CommunityUtil.generateUUID() + suffix;
//确定文件存放的路径
File dest = new File(uploadPath+"/"+filename);
try {
//存储文件
headerImage.transferTo(dest);
} catch (IOException e) {
logger.error("上传文件失败:"+e.getMessage());
throw new RuntimeException("文件上传失败,服务器发生异常!",e);
}
//更新当前用户的头像的路径(web路径)
//http://localhost:8080/community/user/header/xxx.png
User user = hostHolder.getUser();
String headerUrl = domain + contextPath + "/user/header/" + filename;
userService.updateHeader(user.getId(),headerUrl);
return "redirect:/index";
}
二、上传文件
@LoginRequired
@RequestMapping(path = "/upload",method = RequestMethod.POST)
public String uploadHeader(MultipartFile headerImage, Model model){
if (headerImage == null){
model.addAttribute("error","您还没有选择照片");
return "/site/setting";
}
String filename = headerImage.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
if (StringUtils.isBlank(suffix)){
model.addAttribute("error","文件的格式不正确");
return "/site/setting";
}
//生成随机文件名
filename = CommunityUtil.generateUUID() + suffix;
//确定文件存放的路径
File dest = new File(uploadPath+"/"+filename);
try {
//存储文件
headerImage.transferTo(dest);
} catch (IOException e) {
logger.error("上传文件失败:"+e.getMessage());
throw new RuntimeException("文件上传失败,服务器发生异常!",e);
}
//更新当前用户的头像的路径(web路径)
//http://localhost:8080/community/user/header/xxx.png
User user = hostHolder.getUser();
String headerUrl = domain + contextPath + "/user/header/" + filename;
userService.updateHeader(user.getId(),headerUrl);
return "redirect:/index";
}
@RequestMapping(path = "/header/{fileName}",method = RequestMethod.GET)
public void getHeader(@PathVariable("fileName")String fileName, HttpServletResponse response){
//服务器存放路径
fileName = uploadPath + "/" + fileName;
//文件的后缀
String suffix = fileName.substring(fileName.lastIndexOf("."));
//响应图片
response.setContentType("/image/"+suffix);
try(
//加这里会自动关闭
FileInputStream fis = new FileInputStream(fileName);
ServletOutputStream os = response.getOutputStream();
){
byte[] buffer = new byte[1024];
int b = 0;
while((b = fis.read(buffer))!=-1){
os.write(buffer,0,b);
}
} catch (IOException e) {
logger.error("读取头像失败:"+e.getMessage());
}
}
三、检查登录状态
四、过滤敏感词
@Component
public class SensitiveFilter {
private static final Logger logger = LoggerFactory.getLogger(SensitiveFilter.class);
//替换符
private static final String REPLACEMENT = "****";
//根节点
private TrieNode rootNode = new TrieNode();
/**
* 当容器实例化这个bean以后调用这个构造器,这个方法会被自动调用
* SensitiveFilter在服务启动的时候就会初始化
*/
@PostConstruct
public void init(){
/**
* 在try()里创建流对象,它会自动的finally
*/
try(
InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive-words.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
){
String keyword;
while((keyword = reader.readLine())!=null){
//添加到前缀树
this.addKeyword(keyword);
}
}catch (IOException e){
logger.error("加载敏感词文件失败"+e.getMessage());
}
}
//将一个敏感词添加到前缀树中
private void addKeyword(String keyword){
TrieNode tempNode = rootNode;
for (int i = 0; i < keyword.length(); i++) {
char c = keyword.charAt(i);
TrieNode subNode = tempNode.getSubNode(c);
if (subNode == null){
//初始化子节点
subNode = new TrieNode();
tempNode.addSubNode(c,subNode);
}
//指向子节点,进入下一轮循环
tempNode = subNode;
//设置结束标识
if (i == keyword.length() - 1){
tempNode.setKeywordEnd(true);
}
}
}
/**
* 过滤敏感词
* @param text 待过滤文本
* @return 过滤后的文本
*/
public String filter(String text){
if (StringUtils.isBlank(text)){
return null;
}
//指针1
TrieNode tempNode = rootNode;
//指针2
int begin = 0;
//指针3
int position = 0;
//结果
StringBuilder sb = new StringBuilder();
while(position < text.length()){
char c = text.charAt(position);
//跳过符号
if (isSymbol(c)){
//若指针1处于根节点,将此符号计入结果,让指针2向下走一步
if (tempNode == rootNode){
sb.append(c);
begin++;
}
// 无论符号在开头或中间,指针3都向下走一步
position++;
continue;
}
//检查下级节点
tempNode = tempNode.getSubNode(c);
if (tempNode == null){
//以begin开头的字符串不是敏感词
sb.append(text.charAt(begin));
//进入下一个位置
position = ++begin;
//重新指向根节点
tempNode = rootNode;
}else if (tempNode.isKeywordEnd()){
//发现敏感词,将begin~position字符串替换掉
sb.append(REPLACEMENT);
//进入下一个位置
begin = ++position;
//重新指向根节点
tempNode = rootNode;
}else{
//检查下一个字符
position++;
}
}
//将最后一批字符计入结果
sb.append(text.substring(begin));
return sb.toString();
}
//判断是否为符号
private boolean isSymbol(Character c){
// 0X2e80~0x9FFF 是东亚文字范围
return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c> 0x9FFF);
}
//前缀树
private class TrieNode{
//关键词结束标识
private boolean isKeywordEnd = false;
//子节点(key是下级字符,value是下级节点)
private Map<Character,TrieNode> subNodes = new HashMap<>();
public boolean isKeywordEnd(){
return isKeywordEnd;
}
public void setKeywordEnd(boolean keywordEnd){
isKeywordEnd = keywordEnd;
}
//添加子节点
public void addSubNode(Character c,TrieNode node){
subNodes.put(c,node);
}
//获取子节点
public TrieNode getSubNode(Character c){
return subNodes.get(c);
}
}
}
五、发布帖子
六、显示帖子
七、事务管理
1.常见并发异常
1.第一类丢失更新
某一个事务的回滚,导致另外一个事务已更新的暑假丢失了。
2.第二类丢失更新
某一事物的提交,导致另外一个事务已更新的数据丢失了。
3.脏读
某一个事务,读取了另外一个事务未提交的数据。
4.不可重复读
某一个事务,对同一个数据前后读取的结果不一致。
5.幻读
某一个事物,对同一个表前后查询到的行数不一致。
2.事务隔离级别
3.实现机制
4.Spring事务管理
- 声明式事务
- 通过XML配置,声明某方法的事务特征。
- 通过注解,声明某方法的事务特征。
- 编程式事务
- 通过TransactionTemplate管理事务,并通过它执行数据库的操作。
八、添加评论
①编写controller
@Controller
@RequestMapping("/comment")
public class CommentController {
@Autowired
private CommentService commentService;
@Autowired
private HostHolder hostHolder;
@RequestMapping(path = "/add/{discussPostId}",method = RequestMethod.POST)
public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment){
comment.setUserId(hostHolder.getUser().getId());
comment.setStatus(0);
comment.setCreateTime(new Date());
commentService.addComment(comment);
return "redirect:/discuss/detail/"+discussPostId;
}
}
Q.E.D.