# springboot
# 1. 基本概念
每次创建新的SSM项目,都需要:创建Maven项目,添加依赖,配置web.xml,添加spring的配置文件,进行springmvc的相关配置,添加数据库的配置文件,配置mybatis等……准备工作相当的多,而且,随着项目的深入,可能还需要在开发过程中添加新的依赖或新的配置。
事实上,每个不同的项目,经历以上的过程是极为相似的!
使用SpringBoot可以快速的创建项目,却不用关心以上各种配置,在SpringBoot中,有一项开发理念就是“约定大于配置”,即:大多数人共同遵守的配置习惯,将在SpringBoot中成为默认配置,开发者就不必再关心这些配置了!
# 2. 基本使用
访问 http://start.spring.io
然后,确定当前需要创建的项目的参数,主要是:
顶部的项目类型、开发语言、SpringBoot版本;
- Group,例如填写为com.springboot;
- Artifact,例如填写为sample;
- Name,表示项目名称,自动为Artifact的值,可以不更改;
- Description:项目描述;
- Package Name:项目的根包,在SpringBoot中,任何自定义组件都必须在根包或其子包中,例如控制器类、业务类等;
- Packaging:WEB应用必须选择war;
- Java Version:默认为8,也可以使用较低版本。
至此,项目的配置就已经完成,点击绿色的 Generate Project 按钮即可下载项目,得到项目的压缩包,将其解压,并剪切到Workspace中(这一步并不是必须的),然后在Eclipse中选择Import > Existing Maven Projects导入该项目,保证当前计算机是可以连接到Maven服务器,然后,项目会自动更新,添加所需要的所有依赖,至此,项目创建完成!
# 3. 输出Hello,SpringBoot
设计目标:当输入某请求时,服务器返回"Hello, SpringBoot!"
。
在项目的根包下创建子包controller
,然后创建控制器类,例如HelloController
,然后,添加@RestController
注解:
@RestController
public class HelloController {
}
@RestController也是一种@Controller,并且,表示该控制器类中所有处理请求的方法都是@ResponseBody的。使用这个注解后,处理请求也就不可以是转发或重定向了,如果一定要转发或重定向,必须使用原有@Controller注解,并且处理请求的方法上不可以添加@ResponseBody。
然后,添加处理请求的方法:
@RestController
public class HelloController {
@GetMapping("/hello.do")
public String sayHello() {
return "Hello, SpringBoot!";
}
}
至此,简单的HelloWorld就已经完成!
项目的根包下有SampleApplication
类(类名中Application左侧的是项目的Artifact名,所以,不同的项目,该类的名称可能不同),该类是SpringBoot项目的启动类,直接以Run as
> Java Application
方式启动即可,启动时,控制台会有启动日志:
其中,有一条:
Tomcat started on port(s): 8080 (http) with context path ''
SpringBoot是内置Tomcat的,所以,项目本身也不需要添加Tomcat运行环境,并且,SpringBoot部署项目时,如以上日志描述的,使用的Context Path是空值,以前访问时的URL可能是:
http://localhost:8080/项目名/hello.do
而在SpringBoot项目中则是:
http://localhost:8080/hello.do
也就是不需要再在URL中添加项目名了,执行效果例如:
# 4. 关于静态资源
项目中的src\main\resources\
下的static
文件夹是专门用于存放静态资源的,例如图片等等:
然后,启动SpringBoot,在浏览器中通过http://localhost:8080/timg.jpg
即可访问:
# 5. 整合MyBatis
如果在http://start.spring.io
创建项目时,并没有添加MyBatis等依赖,则需要手动添加:
<!-- 整合MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
然后,还需要添加数据库的驱动:
<!-- 数据库 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
注意:SpringBoot会自动使用最新版本的jar包,并不推荐自定义版本号,如果指定了版本,反而会提示警告:
然后,打开SpringBoot的配置文件src\main\resources\application.properties
,添加数据库连接的相关配置:
spring.datasource.url=jdbc:mysql://localhost:3306/tedu_ums?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
以上配置,将由SpringBoot自动读取,并注入到数据源对象中,所以,以上配置的各属性名必须是以上使用的这几个名称,不可以自行修改属性名。
开发持久层功能时,需要先创建接口,例如com.springboot.sample.mapper.UserMapper
。此前使用传统的SSM开发时,需要在配置文件中指定接口所在的包,在SpringBoot中,可以为接口添加@Mapper
注解,而无需添加配置。
@Mapper
public interface UserMapper {
}
在添加完抽象方法之后,传统的SSM开发中需要继续配置XML映射,但是,在SpringBoot的应用中,可以直接添加注解:
@Mapper
public interface UserMapper {
@Select("select id, password from t_user where username=#{username}")
User findByUsername(String username);
}
不难发现,当执行的任务是查询时,则添加@Select
注解,与之类似的还有@Insert
、@Update
、@Delete
注解,根据需要执行的任务进行选取即可!
当完成后,应该执行单元测试,可以自定义测试类,所有的测试类都应该添加注解:
@RunWith(SpringRunner.class)
@SpringBootTest
public class 测试类 ...
在编写测试方法时,所需要的对象如果是当前项目的组件,都可以自动装配:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTestCase {
@Autowired
private UserMapper userMapper;
// ...其它
然后编写测试方法:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTestCase {
@Autowired
private UserMapper userMapper;
@Test
public void findByUsername() {
String username = "mvc";
User user
= userMapper.findByUsername(username);
System.out.println(user);
username = "hello";
user = userMapper.findByUsername(username);
System.out.println(user);
}
}
至此,简单的持久层访问就已经完成!
# 6. 业务层(非SpringBoot知识点)
设计业务层方法时,以操作成功(操作正确)为前提来设计返回值的类型,而几乎每个业务方法都有出错的可能,出错时,直接抛出某种异常即可!
以登录为例,方法名可以使用login
,符合大多数人的英语认知;返回值应该是User,因为登录成功后,可能需要将用户的id、username等数据存入Session,则使用1个User对象可以封装这多项数据;参数应该使用String username和String password,简单易懂:
User login(String username, String password);
该方法可能出现的“错误”(指的是开发者认为用户操作失败)均通过抛出异常来表现,则调用方法时的语法可能是:
try {
User user = userService.login("", "");
session.setAttribute(
"id", user.getId());
session.setAttribute(
"username", user.getUsername());
} catch (Username异常 e) {
} catch (Password异常 e) {
}
使用这种做法,既能满足返回正确的数据,又能体现多种不同的错误,并且,在语法上表达得也非常清楚!
抛出的异常应该继承自RuntimeException
,并添加父类中已经存在的多种构造方法,便于快速创建对象!通常,异常类存放在service
的子级ex
包中。
由于自定义的异常都是RuntimeException
的子孙类异常,Java语法并不强制要求处理这些异常,则方法的调用者可能不清楚需要处理哪些异常,所以,在方法的声明中应该明确的抛出:
public User login(
String username, String password)
throws UserNotFoundException,
PasswordNotMatchException {
// ...
}
并且,在方法的声明中明确的添加注释:
/**
* 用户登录
* @param username 用户名
* @param password 密码
* @return 成功登录的用户数据,包括用户的id等……
* @throws UserNotFoundException 用户数据不存在
* @throws PasswordNotMatchException 密码不匹配
*/
public User login(
String username, String password)
throws UserNotFoundException,
PasswordNotMatchException {
// ... ...
}
# 7. 控制器层
先创建com.springboot.sample.controller.UserController
,添加@RestController
注解,然后,添加方法处理请求:
@RestController
public class UserController extends BaseController {
@Autowired
private UserService userService;
@RequestMapping("/login.do")
public ResponseResult login(
String username, String password) {
User user = userService.login(
username, password);
return new ResponseResult(SUCCESS);
}
}
并且,另行创建控制器的基类提供静态常量,并处理异常:
public abstract class BaseController {
protected static final int SUCCESS = 1;
@ExceptionHandler(ServiceException.class)
public ResponseResult handleException(Exception e) {
// 判断异常的种类并进行处理
if (e instanceof UserNotFoundException) {
return new ResponseResult(2, e);
} else if (e instanceof PasswordNotMatchException) {
return new ResponseResult(3, e);
}
return null;
}
}
# 8. 插入数据时获取数据id
插入数据时,持久层的方法之前使用@Insert
注解配置SQL,然后,还需要通过@Options(useGeneratedKeys=true, keyProperty="id")
注解配置获取数据的id,与XML映射效果相同,插入时使用的对象在完成后将包括数据的id值。
# 9. 使用主页
在src/main/resources/static
或webapp
下的index.html
会是默认主页,访问时,仅需通过http://localhost:8080/
即可访问。
# 10. 使用错误页面
在src/main/resources/resouces/error
文件夹是专门用于存放错误页面的位置,默认情况下,该文件夹并不存在,需要手动创建,存放在这个文件夹下的页面文件应该使用响应码作为文件名,例如404.html
,后续无须添加任何配置,只要出现错误的响应码,就会将这些页面响应到客户端。
# 11. 复习思考题
为什么业务层的方法要抛出异常;
关于
ResponseResult
类,为什么需要多种不同的构造方法;例如
new ResponseResult(2, e)
是什么意思;处理请求的方法为什么返回
ResponseResult
;为什么自定义的异常需要是
RuntimeException
的子孙类;业务层的方法为什么需要通过
throws
声明抛出异常;为什么要将处理异常的代码编写在
BaseController
中;处理异常时,
@ExceptionHandler
注解中可以不配置参数吗;