Spring boot 小示例项目

包含拦截器、使用自定义的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)
出现刚刚的主页并且控制台没有什么奇怪的东西就说明配置成功了

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 121,055评论 16 134
  • 文 by / 林本托 Tips做一个终身学习的人。 Spring Boot 初体验 Spring Boot 包含了...
    码匠安徒生阅读 785评论 1 12
  • 八月未央,转瞬间秋的脚步悄然走近。思绪还驻在一个盛夏的热烈里,那个明媚的季节却即将到来,那些清朗高远的日子,丰盈着...
    华枝春满5339阅读 184评论 1 4
  • “愿得一人心,白首不相离。” 不知道是不是快到情人节的原因,这两天一直翻来翻去的默念这句话。 得心,愿白首....
    two姑娘阅读 88评论 0 0
  • 啦啦啦啦
    光影城市阅读 58评论 0 0