入门条件
• Java 8已安装。
• 任何Java IDE(最好是STS或IntelliJ IDEA)。
• 使用HTML,CSS和JavaScript,基本了解基于Java和Spring的Web开发和UI开发。
背景
在本文中,我将尝试使用Java 8和Spring Boot创建一个小型端到端Web应用程序。
我选择了SpringBoot,因为它更容易配置并且可以很好地与其他技术堆栈配合使用。我还使用了REST API和SpringData JPA以及H2数据库。
我使用 Spring Initializer 添加所有依赖项,并使用我的所有配置创建一个空白的工作项目。
我使用Maven作为构建工具,但也可以使用Gradle。
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.example</groupId>
<artifactId>bootdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>bootDemo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.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-devtools</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-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.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在UI部分,我使用了AngularJS和BootStrap CSS以及基本的JS,CSS和HTML。
这是一个非常简单的可用于创建Web应用程序的项目。
结构
实施
让我们从SpringBootApplication类开始。
@SpringBootApplication
public class BootDemoApplication {
@Autowired
UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(BootDemoApplication.class, args);
}
}
我们现在创建Controller。
@Controller
public class HomeController {
@RequestMapping("/home")
public String home() {
return "index";
}
}
这将作为我们SPA的主页。现在我们创建一个Controller来处理一些REST调用。
@RequestMapping("/user")
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping(Constants.GET_USER_BY_ID)
public UserDto getUserById(@PathVariable Integer userId) {
return userService.getUserById(userId);
}
@RequestMapping(Constants.GET_ALL_USERS)
public List < UserDto > getAllUsers() {
return userService.getAllUsers();
}
@RequestMapping(value = Constants.SAVE_USER, method = RequestMethod.POST)
public void saveUser(@RequestBody UserDto userDto) {
userService.saveUser(userDto);
}
}
在这里,我们有不同的方法来处理来自客户端的不同测试调用。
我在Controller中安装了一个Service类 UserService。
public interface UserService {
UserDto getUserById(Integer userId);
void saveUser(UserDto userDto);
List < UserDto > getAllUsers();
}
@Service
public class UserServiceimpl implements UserService {
@Autowired
UserRepository userRepository;
@Override
public UserDto getUserById(Integer userId) {
return UserConverter.entityToDto(userRepository.getOne(userId));
}
@Override
public void saveUser(UserDto userDto) {
userRepository.save(UserConverter.dtoToEntity(userDto));
}
@Override
public List < UserDto > getAllUsers() {
return userRepository.findAll().stream().map(UserConverter::entityToDto).collect(Collectors.toList());
}
}
在典型的Web应用程序中,通常有两种类型的数据对象:DTO(通过客户端进行通信)和实体(通过DB进行通信)。
DTO
public class UserDto {
Integer userId;
String userName;
List<SkillDto> skillDtos= new ArrayList<>();
public UserDto(Integer userId, String userName, List<SkillDto> skillDtos) {
this.userId = userId;
this.userName = userName;
this.skillDtos = skillDtos;
}
public UserDto() {
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<SkillDto> getSkillDtos() {
return skillDtos;
}
public void setSkillDtos(List<SkillDto> skillDtos) {
this.skillDtos = skillDtos;
}
}
public class SkillDto {
Integer skillId;
String SkillName;
public SkillDto(Integer skillId, String skillName) {
this.skillId = skillId;
SkillName = skillName;
}
public SkillDto() {
}
public Integer getSkillId() {
return skillId;
}
public void setSkillId(Integer skillId) {
this.skillId = skillId;
}
public String getSkillName() {
return SkillName;
}
public void setSkillName(String skillName) {
SkillName = skillName;
}
}
Entity
@Entity
public class User implements Serializable{
private static final long serialVersionUID = 0x62A6DA99AABDA8A8L;
@Column
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Integer userId;
@Column
private String userName;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<Skill> skills= new LinkedList<>();
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public List<Skill> getSkills() {
return skills;
}
public void setSkills(List<Skill> skills) {
this.skills = skills;
}
public User() {
}
public User(String userName, List<Skill> skills) {
this.userName = userName;
this.skills = skills;
}
}
@Entity
public class Skill {
@Column
@GeneratedValue(strategy = GenerationType.AUTO)
@Id
private Integer skillId;
@Column
private String skillName;
@ManyToOne
private User user;
public Skill(String skillName) {
this.skillName = skillName;
}
public Integer getSkillId() {
return skillId;
}
public void setSkillId(Integer skillId) {
this.skillId = skillId;
}
public String getSkillName() {
return skillName;
}
public void setSkillName(String skillName) {
this.skillName = skillName;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Skill() {
}
public Skill(String skillName, User user) {
this.skillName = skillName;
this.user = user;
}
}
对于数据库操作,我们使用SpringData JPA:
@Repository
public interface UserRepository extends JpaRepository<User, Integer>{
}
@Repository
public interface SkillRepository extends JpaRepository<Skill, Integer>{
}
在默认情况下,扩展JpaRepository 提供了大量的CRUD操作,也可以使用它来创建自己的查询方法。
为了转换DTO - >Entity和Entity - > DTO,我创建了一些基本的转换器类。
public class UserConverter {
public static User dtoToEntity(UserDto userDto) {
User user = new User(userDto.getUserName(), null);
user.setUserId(userDto.getUserId());
user.setSkills(userDto.getSkillDtos().stream().map(SkillConverter::dtoToEntity).collect(Collectors.toList()));
return user;
}
public static UserDto entityToDto(User user) {
UserDto userDto = new UserDto(user.getUserId(), user.getUserName(), null);
userDto.setSkillDtos(user.getSkills().stream().map(SkillConverter::entityToDto).collect(Collectors.toList()));
return userDto;
}
}
public class SkillConverter {
public static Skill dtoToEntity(SkillDto SkillDto) {
Skill Skill = new Skill(SkillDto.getSkillName(), null);
Skill.setSkillId(SkillDto.getSkillId());
return Skill;
}
public static SkillDto entityToDto(Skill skill) {
return new SkillDto(skill.getSkillId(), skill.getSkillName());
}
}
现在让我们关注UI部分。
使用Angular时,我们需要遵循一些指导原则。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Main Page</title>
</head>
<body ng-app="demo">
<hr/>
<div class="container" ng-controller="UserController">
<div class="row">
<label>User</label> <input type="text" ng-model="userDto.userName" class="input-sm spacing"/>
<label>Skills</label> <input type="text" ng-model="skills" ng-list class="input-sm custom-width spacing"
placeholder="use comma to separate skills"/>
<button ng-click="saveUser()" class="btn btn-sm btn-info">Save User</button>
</div>
<hr/>
<div class="row">
<p>{{allUsers | json}}</p>
</div>
<hr/>
<div class="row" ng-repeat="user in allUsers">
<div class="">
<h3>{{user.userName}}</h3>
<span ng-repeat="skill in user.skillDtos" class="spacing">{{skill.skillName}}</span>
</div>
</div>
</div>
</body>
<script src="js/lib/angular.min.js"></script>
<script src="js/lib/ui-bootstrap-tpls-2.5.0.min.js"></script>
<script src="js/app/app.js"></script>
<script src="js/app/UserController.js"></script>
<script src="js/app/UserService.js"></script>
<link rel="stylesheet" href="css/lib/bootstrap.min.css"/>
<link rel="stylesheet" href="css/app/app.css"/>
</html>
在创建HTML时,不要忘记导入所需的JS和CSS文件。
app.js
'use strict'
var demoApp = angular.module('demo', ['ui.bootstrap', 'demo.controllers',
'demo.services'
]);
demoApp.constant("CONSTANTS", {
getUserByIdUrl: "/user/getUser/",
getAllUsers: "/user/getAllUsers",
saveUser: "/user/saveUser"
});
UserController.js
'use strict'
var module = angular.module('demo.controllers', []);
module.controller("UserController", ["$scope", "UserService",
function($scope, UserService) {
$scope.userDto = {
userId: null,
userName: null,
skillDtos: []
};
$scope.skills = [];
UserService.getUserById(1).then(function(value) {
console.log(value.data);
}, function(reason) {
console.log("error occured");
}, function(value) {
console.log("no callback");
});
$scope.saveUser = function() {
$scope.userDto.skillDtos = $scope.skills.map(skill => {
return {
skillId: null,
skillName: skill
};
});
UserService.saveUser($scope.userDto).then(function() {
console.log("works");
UserService.getAllUsers().then(function(value) {
$scope.allUsers = value.data;
}, function(reason) {
console.log("error occured");
}, function(value) {
console.log("no callback");
});
$scope.skills = [];
$scope.userDto = {
userId: null,
userName: null,
skillDtos: []
};
}, function(reason) {
console.log("error occured");
}, function(value) {
console.log("no callback");
});
}
}
]);
UserService.js
'use strict'
angular.module('demo.services', []).factory('UserService', ["$http", "CONSTANTS", function($http, CONSTANTS) {
var service = {};
service.getUserById = function(userId) {
var url = CONSTANTS.getUserByIdUrl + userId;
return $http.get(url);
}
service.getAllUsers = function() {
return $http.get(CONSTANTS.getAllUsers);
}
service.saveUser = function(userDto) {
return $http.post(CONSTANTS.saveUser, userDto);
}
return service;
}]);
app.css
body{
background-color: #efefef;
}
span.spacing{
margin-right: 10px;
}
input.custom-width{
width: 200px;
}
input.spacing{
margin-right: 5px;
}
可以使用以下方法构建应用程序
mvn clean install 或者java -jar bootdemo-0.0.1-SNAPSHOT.jar
打开浏览器并点击 http:// localhost:8080 / home
在打开一个简单的页面之后,输入名称和技能,输入的数据将保留在数据库中。