使用Spring Tools写一个Spring Boot的小例子

例子来源于《Spring Boot实战》

环境:Java8+Eclipse Oxygen
安装插件:Help -> Eclipse MarketPlace -> 搜索 【Spring Tools】
之后按照正常的流程安装下来就好

如果已经安装了将会显示【Installed】

安装成功后可能会出现下次打开时候插件不见的情况,我是重新用了一个eclipse以管理员身份打开重新安装,以后都要用管理员身份进入可以解决这个问题。

创建项目

File -> New -> other -> Spring Starter Project

之后下一步,如果出现如下图的情况并且你用浏览器访问http://start.spring.io
是没有问题的那就关掉New Spring Starter Project页面在打开几次就好

网络不好的情况
正常的情况,红框框处可以填入你自己的项目相关信息

之后下一步,选择H2/JPA/Thymeleaf和Web,第一次没有记录的话可以再下部的列表中找到


之后点下一步点完成,之后等一下让电脑把项目创建好,创建好之后将会得到一个目录如下的项目

接下来我们来实现一个用户书单管理的小系统
首先贴出我的pom.xml内容,由于用到了lombok所以要在eclipse把lombok.jar放在eclipse的目录下并进行一些配置

<?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.slience.manning</groupId>
    <artifactId>readinglist</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>spring-boot-demo2</name>
    <description>阅读Demo</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.4.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-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

实体类Book

使用@Data就可以不用写set和get方法了

package readinglist;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import lombok.Data;
@Data
@Entity
public class Book {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String reader;
    private String isbn;
    private String title;
    private String author;
    private String description;
}


定义仓库接口ReadingListRepository

我们使用Spring Data JPA,所以我们可以使用其中的JpaRepository接口,Spring Data会自动实现这些接口,很神奇

package readinglist;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ReadingListRepository extends JpaRepository<Book, Long> {
    List<Book> findByReader(String reader);
}

控制器ReadingListController

package readinglist;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/")
public class ReadingListController {
    @Autowired
    private ReadingListRepository readingListRepository;
    
    //通过@PathVariable来获取url中的参数
    @RequestMapping(value="/{reader}", method=RequestMethod.GET)
    public String readerBooks(@PathVariable("reader") String reader, Model model) {
        List<Book> readingList = readingListRepository.findByReader(reader);
        if(readingList != null) {
            model.addAttribute("books", readingList);
        }
        return "readingList";
    }
    
    @RequestMapping(value="/{reader}", method=RequestMethod.POST)
    public String addTpReadingList(@PathVariable("reader") String reader, Book book) {
        //book实体中的其他参数已经在前端进行设置了
        book.setReader(reader);
        //save方法是通过继承JpaRepository得到的
        readingListRepository.save(book);
        return "redirect:/{reader}";
    }
}

前端网页readingList.html

此文件放在src/main/resources下的templates下

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"></meta>
<title>Reading List</title>
<link rel="stylesheet" th:href="@{/style.css}"></link>
</head>
<body>
    <h2>Your Reading List</h2>
    <div th:unless="${#lists.isEmpty(books)}">
        <dl th:each="book : ${books}">
            <dt class="bookHeadline">
                <span th:text="${book.title}">Title</span> by <span
                    th:text="${book.author}">Author</span> (ISBN: <span
                    th:text="${book.isbn}">ISBN</span>)
            </dt>

            <dd class="bookDescription">
                <span th:if="${book.description}" th:text="${book.description}">
                    Description </span> <span th:if="${book.description eq null}"> No
                    description available </span>
            </dd>
        </dl>
    </div>
    <div th:if="${#lists.isEmpty(books)}">
        <p>You have no books in your book list</p>
    </div>
    <hr />
    <h3>Add a book</h3>
    <form method="POST">
        <label for="title">Title:</label> 
        <input type="text" name="title" size="50"></input>
        <br /> 
        <label for="author">Author:</label> 
        <input type="text" name="author" size="50"></input>
        <br /> 
        <label for="description">Description:</label>
        <br />
        <textarea name="description" cols="80" rows="5"></textarea>
        <br /> 
        <input type="submit"></input>
    </form>
</body>
</html>

前端样式

此文件放在src/main/resources下的static内

@charset "UTF-8";

body {
    background-color: #cccccc;
    font-family: arial, helvetica, sans-serif;
}

.bookHeadline {
    font-size: 12pt;
    font-weight: bold;
}

.bookDescription {
    font-size: 10pt;
}

label {
    font-weight: bold;
}

做好之后我们的项目应该是结构应该是这样的

之后我们右键项目->Run as -> Spring Boot App
控制台将会输出这些东西

启动好之后打开浏览器访问http://localhost:8080/yourname
将会显示如下页面

可以正常添加书单,那么我们就弄好了一个小Spring Boot的项目

添加Spring Security保护我们的项目

在pom.xml中添加如下

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

之后我们再运行项目将会提示你输入用户名密码

默认的用户名是user,密码是随机的,启动时会在控制台输出

Using default security password: 3bffb49c-d50c-4127-946b-db0d989f1b51

这样虽然可以让我们的程序更加安全但是这种安全配置不符合实际的需求,我们需要一些自定义的配置,接下来我们就来写Spring Secuirty配置
(注:实际运行起来并没有达到书中的效果,我以后熟悉了再来看看是什么原因)

通过写WebSecurityConfigurerAdapte配置类来自定义安全配置

我们想要一个用户读者实体用以登录,登录之后才可以进行相关操作,所以我们先写读者实体类Reader.java

package readinglist;

import java.util.Arrays;
import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.Data;
@Data
@Entity
public class Reader implements UserDetails {
    private static final long serialVersionUID = 1L;
    @Id
    private String username;
    private String fullname;
    private String password;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //授予READER权限
        return Arrays.asList(new SimpleGrantedAuthority("READER"));
    }
    @Override
    public boolean isAccountNonExpired() {
        //设置账号不过期
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        //设置账号不加锁
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        //设置证书不过期
        return true;
    }
    @Override
    public boolean isEnabled() {
        //设置不禁用
        return true;
    }
    
    
}

通过使用@Entity注解说明这是个持久化数据实体,通过实现UserDetails接口中的方法,Reader就能代表Spring Security中的用户了,接下来我们写持久化读者信息仓库接口

package readinglist;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ReaderRepository extends JpaRepository<Reader, String> {

}

通过继承JpaRepository,Spring Data JPA会在运行的时候自动创建18个基本的早错Reader实体的方法,接下来我们就可以写WebSecurityConfigurerAdapte配置类了

package readinglist;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private ReaderRepository readerRepository;
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //访问"/"(也就是ReadingListController)需要有READER角色
        http.authorizeRequests().antMatchers("/").access("hasRole('READER')")
            //访问其他路径开放访问权限
            .antMatchers("/**").permitAll()
            .and()
            //设置登录表单的路径
            .formLogin().loginPage("/login").failureUrl("/login?error=ture");
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new UserDetailsService() {
            
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                return readerRepository.findOne(username);
            }
        });
    }
}

推荐阅读更多精彩内容