包含拦截器、使用自定义的HQL和部署到外部tomcat
准备工作
需要在eclipse 使用STS插件,创建一个spring boot项目
需要使用的dependencies主要是:MySql、JDBC、JPA、Rest Repositories 和 Web
还需要另外加lombok的依赖
实体类
用户、用户发布的文章和msg数据实体(用来传给前端的,没有实际的表)
用户实体User.java
package slience.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Table(name="t_user")
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
private String pwd;
private int age;
public User(String name, String pwd, int age) {
this.pwd = pwd;
this.name = name;
this.age = age;
}
}
数据实体Msg.java
package slience.entity;
import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Component
public class Msg {
private String message;
private int code;
private Object data;
public void set(String message, int code, Object data) {
this.message = message;
this.code = code;
this.data = data;
}
}
文章实体Article.java
package slience.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Data
@Entity
@Table(name="t_article")
@RequiredArgsConstructor
@NoArgsConstructor
public class Article {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
@NonNull
private String title;
@NonNull
@ManyToOne
private User user;
@NonNull
@Column(columnDefinition="varchar(9527) default '' comment '文章内容'")
private String content;
public Article(long id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
}
文章实体中使用通过使用@NonNull和@RequiredArgsConstructor来自动生成指定参数的构造函数,这样做如果在其他地方去取某些数据——被@NonNull标注的属性的内容为空的数据,可能会报错。
DAO层
通过创建继承了PagingAndSortingRepository的接口来实现基本的增删改查和分页,可以直接在接口声明自定义的数据查询方法
用户Repository
package slience.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import slience.entity.User;
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
//不必实现此方法,Spring会自动通过生成,前提是User实体中有Name属性
public User findByName(String name);
}
文章Repository
package slience.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import slience.entity.Article;
import slience.entity.User;
public interface ArticleRepository extends PagingAndSortingRepository<Article, Long> {
//使用自己的HQL语句,使用select new Article(id,title,content)来过滤Article实体中的User属性
//这样做的时候Article实体必须要有接收(id,title,content)这三个参数的构造函数
@Query("select new Article(id,title,content) from Article a where a.user=:user")
public Page<Article> findByUser(@Param("user") User user, Pageable pageable);
}
Controller层
BaseController基本控制器,我比较喜欢把一些共用的东西放在BaseController中,实际开发中可以看项目需求酌情设置,controller中使用到的Code和TokenUtil都是一些静态的工具类,用以标识请求状态和生成token的,没啥用,你们自己替换或者直接去掉就好。
package slience.controller;
import org.springframework.beans.factory.annotation.Autowired;
import slience.entity.Msg;
import slience.repository.ArticleRepository;
import slience.repository.UserRepository;
public class BaseController {
@Autowired
protected Msg msg;
@Autowired
protected UserRepository userRepository;
@Autowired
protected ArticleRepository articleRepository;
}
UserController.java
package slience.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;
import slience.entity.Msg;
import slience.entity.User;
import slience.util.Code;
import slience.util.TokenUtil;
@RestController
@RequestMapping(value="/user")
public class UserController extends BaseController {
@RequestMapping(value="/register", method=RequestMethod.POST)
public Object register(@RequestParam String name, @RequestParam String pwd, @RequestParam int age) {
//验证用户名是否重复
User user = userRepository.findByName(name);
if(user != null) {
msg.set("用户名重复", Code.ERROR, null);
} else {
user = new User(name, pwd, age);
user = userRepository.save(user);
String token = TokenUtil.create(user.getId());
msg.set("注册成功", Code.SUCCESS, token);
}
return msg;
}
/**
* 用户登录
* @param name
* @param Pwd
* @return
*/
@RequestMapping(value="/login", method=RequestMethod.POST)
public Object login(@RequestParam String name, @RequestParam String pwd) {
User user = userRepository.findByName(name);
if(user != null) {
if(user.getPwd().equals(pwd)) {
//别把密码发到前端
user.setPwd(null);
Map<String , Object> result = new HashMap<>();
result.put("token", TokenUtil.create(user.getId()));
result.put("user", user);
msg.set("登录成功", Code.SUCCESS, result);
} else {
msg.set("密码错误", Code.ERROR, null);
}
} else {
msg.set("未找到此用户", Code.ERROR, null);
}
return msg;
}
/**
* 修改用户信息
* @param session
* @param name
* @return
*/
@RequestMapping(value="/update", method=RequestMethod.POST)
public Msg update(@SessionAttribute User user, @RequestParam(required=false) String name) {
boolean isChange = false;
if(name != null) {
user.setName(name);
isChange = true;
}
if(isChange) {
user = userRepository.save(user);
user.setPwd(null);
msg.set("修改成功", Code.SUCCESS, user);
} else {
msg.set("没有要修改的数据", Code.ERROR, null);
}
return msg;
}
@RequestMapping(value="/test", method=RequestMethod.GET)
public Object test() throws Exception {
System.out.println("test");
User user = userRepository.findOne((long) 1);
msg.set("ok", Code.SUCCESS, user);
return msg;
}
}
ArticleController.java
package slience.controller;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;
import slience.entity.Article;
import slience.entity.Msg;
import slience.entity.User;
import slience.util.Code;
@RestController
@RequestMapping(value="/article")
public class ArticleController extends BaseController {
/**
* 创建文章
* @param user
* @param title
* @param content
* @return
* @throws Exception
*/
@RequestMapping(value="/create", method=RequestMethod.POST)
public Msg create(@SessionAttribute User user, @RequestParam String title, @RequestParam String content) throws Exception {
Article article = new Article(title, user, content);
article = articleRepository.save(article);
msg.set("创建成功", Code.SUCCESS, null);
return msg;
}
/**
* 更新文章
* @param user
* @param id
* @param title
* @param content
* @return
*/
@RequestMapping(value="/update", method=RequestMethod.POST)
public Msg update(@SessionAttribute User user, @RequestParam long id,
@RequestParam(required=false) String title, @RequestParam(required=false) String content) throws Exception {
Article article = articleRepository.findOne(id);
if(article != null) {
if(article.getUser().getId() == user.getId()) {
//是作者本人
boolean isChange = false;
if(title != null && !article.getTitle().equals(title)) {
article.setTitle(title);
isChange = true;
}
if(content != null && !content.equals(article.getContent())) {
article.setContent(content);
isChange = true;
}
if(isChange) {
articleRepository.save(article);
msg.set("修改成功", Code.SUCCESS, null);
} else {
msg.set("没有要修改的内容", Code.ERROR, null);
}
} else {
msg.set("您不是本文的作者不能进行修改", Code.ERR_AUTH, null);
}
} else {
msg.set("未找到此文章", Code.ERROR, null);
}
return msg;
}
@RequestMapping(value="/{id}", method=RequestMethod.POST)
public Msg findByUser(@PathVariable("id") long userId, @RequestParam int page, @RequestParam int size) {
User user = userRepository.findOne(userId);
if(user != null) {
Page<Article> articles = articleRepository.findByUser(user,new PageRequest(page, size, new Sort(Sort.Direction.DESC, "id")));
msg.set("查询成功", Code.SUCCESS, articles);
} else {
msg.set("未找到此用户", Code.ERROR, null);
}
return msg;
}
}
过滤器
此过滤器主要实现了将请求中的token转化成用户实体并捆绑到session中(弄到request会造成前后实体不同的原因,在update用户的时候发现的,因为update的时候也可以用Repository的save,如果是同一个是update,如果是新的就是save,我最初是在过滤器中拿到user实体放在request里的结果造成了每次update都是创建一个新的用户)
package slience.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import slience.entity.Msg;
import slience.entity.User;
import slience.repository.UserRepository;
import slience.util.Code;
import slience.util.HttpTool;
import slience.util.TokenUtil;
/**
* 权限拦截器
* @author slience
*
*/
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private Msg msg;
@Autowired
private UserRepository userRepository;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("验证token");
String token = request.getParameter("token");
if(token != null) {
long userId = TokenUtil.verify(token);
if(userId > 0) {
User user = userRepository.findOne(userId);
if(user != null) {
request.getSession().setAttribute("user", user);
return true;
} else {
msg.set("没有找到指定用户", Code.ERR_AUTH, null);
}
} else if(userId == 0) {
msg.set("token超时,请重新登录", Code.ERR_AUTH, null);
} else {
msg.set("token有误", Code.ERR_AUTH, null);
}
} else {
msg.set("请先登录", Code.ERR_AUTH, null);
}
HttpTool.jsonMsg(response, msg);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
项目配置
在这里主要是引入过滤器和防止跨域
WebConfiguration.java
package slience.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import slience.interceptor.AuthInterceptor;
@Configuration
public class WebConfiguration extends WebMvcConfigurerAdapter {
@Bean
public AuthInterceptor authInterceptor() {
return new AuthInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor()).addPathPatterns("/**")
//不过滤的路径
.excludePathPatterns("/user/register", "/user/login", "/user/test");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
//防止跨域
registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
}
}
执行类Application.java
package slience;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
@SpringBootApplication
public class SpringMvcTest2Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringMvcTest2Application.class);
}
public static void main(String[] args) {
SpringApplication.run(SpringMvcTest2Application.class, args);
}
}
这样应该就可以用嵌入式的tomcat运行项目了,接下来我们要使用外部的tomcat来运行,在pom.xml中修改packaging从jar改为war,在dependencies中添加(2017-10-18日更新:其实不加也可以)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
完整的pom.xml是这样的
<?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>com.momo.slience</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring-mvc-test-2</name>
<description>自己测试</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
我们可以再src/main/webapp文件下创建一个index.html弄一个简单的主页
接下来我们找到Java Resources下的src/main/resouces文件夹中的application.properties中添加如下语句
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring-boot-test
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.ddl-auto=create-drop
分别指定驱动类、链接地址、mysql用户名、用户密码和每次启动都会重新创建一次数据库(酌情添加)
弄好完整的项目目录是这样的
之后我们对着项目右键->Run as ->Run On Server,第一次好像要自己选运行的服务器组件(如果是要用Spring boot内置的tomcat就选Run as ->Spring Boot App)
出现刚刚的主页并且控制台没有什么奇怪的东西就说明配置成功了