springboot基础及使用
概念
springboot对spring mvc、tomcat等都做了集成,只需要依赖spring boot的jar,通过注解指定启动类,然后执行里面的main方法,它就能启动tomcat提供服务。不需要spring mvc的繁琐配置。
使用spring boot时不需要引入spring mvc,tomcat等的依赖,只需要引入spring boot的依赖即可,它会自动寻找spring mvc,tomcat等的合适的版本引入,解决了框架中各种组件版本不一致的问题。
安装配置:
无ide手工方式:
1、新建文件夹SpringBootSample
2、该文件夹下新建文件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>myproject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
</project>
其中parent里定义了很多依赖的版本,继承它之后,以后的依赖都不用写版本号,全从parent继承,这样可以避免版本冲突,将个依赖的版本都交给了spring boot管理。
不过这里的示例没有任何依赖。
3、cd到该目录下执行mvn package
说明已经构建成功了
4、构建web项目
spring boot将各功能分成了不同模块,只需要添加需要的模块即可,版本号由spring boot统一管理,添加web模块后,它会自动引入spring mvc、tomcat、spring core等依赖。
4.1 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>myproject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<!-- Additional lines to be added here... -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
引入spring-boot-maven-plugin插件可以使mvn package打出的jar包能直接用java -jar命令运行,
未使用插件:
会提示:
使用了插件:
多了BOOT-INF,启动类的class和所有依赖包都在里面。
4.2 编写启动类java
由于添加web依赖后它会自动去找启动类的main方法,找不到会报错,因此需要创建启动类:
创建SpringBootSample\src\main\java目录结构
在该目录下创建Example.java
内容:
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
public class Example {
@RequestMapping("/")
String home() {
return "kkk!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Example.class, args);
}
}
其中@EnableAutoConfiguration表示该类是启动类,从该类main方法启动。
@RestController是@Controller和@ResponseBody的组合写法,表示方法返回的内容直接给客户端,而不是去找模板。
4.3构建运行
有3种方式,一种是利用spring boot插件,执行:
mvn spring-boot:run
它会自动启动tomcat,默认端口是8080
或者mvn package打出jar文件,然后执行:
java -jar target/myproject-0.0.1-SNAPSHOT.jar
第三种方式是在ide里面直接执行启动类的main方法。
5、访问应用
http://localhost:8080
使用ide创建:
idea和eclipse中创建maven项目,跟手工方式一样配置即可。
idea向导创建
idea中还提供了直接创建spring boot项目的方式,可以通过向导勾选web等spring boot的模块:
File-New Project-Spring Initializr
配置说明
向导创建出来的工程配置跟手工创建的一致:
自动创建的启动类:
注意这里创建了一个配置文件,默认是空的。可以将很多配置放在里面,如启动端口,数据库连接等自定义配置信息。
编写启动类,访问测试
@SpringBootApplication
@RestController
@RequestMapping("user")
public class KkkApplication {
public static void main(String[] args) {
SpringApplication.run(KkkApplication.class, args);
}
@RequestMapping("list")
public String hello() {
return "users";
}
}
访问测试
@EnableAutoConfiguration 和@ SpringBootApplication说明
@ SpringBootApplication等同于@Configuration,@EnableAutoConfiguration,@ComponentScan三个属性的组合。
当访问非启动类的Controller时,就需要启动类配置@ComponentScan属性才能扫描到,否则扫描不到。因此启动类上通常用@ SpringBootApplication,这样就可以扫描到其他类的bean。
修改端口和上下文路径
application.properties中增加配置:
server.port=90
server.context-path=/kkk
访问:
使用yml配置
yml文件配置可以减少properties的配置量,如:
注意:
1、冒号后面必须带一个空格,否则无法识别
2、要注意缩进,因为它通过代码行缩进确定属性归属级别的。
使用@value读取配置
yml配置:
读取:
测试:
配置中引用配置:
用${}引用即可,如:
注入分组配置:
如果配置的属性较多,则使用@value需要对每个属性都写一个变量来接收,很不方便,可以将属性放到一个分组里,然后定义一个实体类来接收参数,使用时只用引用该实体类即可:


这里的prefix跟配置中的对应。


这里使用@ConfigurationProperties注入了company里面的属性。
也可以直接注入到Controller,需要在controller中定义配置的属性不过最好不要这样用。
如:

。
分dev、online环境配置:
如:




java -jar指定环境
不过这种方式切环境需要改代码,可以在java –jar运行参数中指定环境,它会覆盖掉配置中指定的环境。
如:
java -jar kkk-0.0.1-SNAPSHOT.jar --spring.profiles.active=online


idea中使用spring-boot插件运行可以在这里配置环境参数:

使用thymeleaf模板
thymeleaf是spring提供的模板,类似于freemarker、velocity等。
pom.xml加入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
templates目录下新建index.html

然后使用@Controller即可返回模板。


使用常用的springmvc注解
匹配多个路径
如:
@RequestMapping(value = {"list", "users"})
public String list() {
return "index";
}
@GetMapping、@PostMapping等的简化写法
@GetMapping("list")相当于@RequestMapping(value = "list", method = RequestMethod.GET).
@PostMapping、@PutMapping等同理。
使用spring data jpa
jpa是一套java提出的规范,用于持久化操作,jpa有很多实现,如hibernate、toplink等。
spring data jpa是封装在hibernate、toplink等上面一层的实现,用户通过它来操作hibernate等,可以不关心底层实现。
配置
pom.xml加入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
application.yml配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/users
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
其中ddl-auto:update表示如果没有表会自动创建表,有表则不动。
create表示则每次都会把原表drop掉重新创建。
create-drop表示每次都重新创建表,应用停下来就把表删除
none表示不执行任何ddl。
validate表示不执行ddl,但会检查实体类和表结构是否一致,不一致就报错。

show-sql:true表示打印sql语句。
创建实体类Company.java:
import org.hibernate.annotations.Generated;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
/**
* Created by Administrator on 2017/12/7.
*/
@Entity
public class Company {
@Id
@GeneratedValue
private Long id;
private String name;
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
其中@Id表示主键,@ GeneratedValue表示自动生成
执行程序即可自动创建表

Long映射成了bigint(20)
String映射成了varchar(255)
增删改查操作:
查询全部数据


新增数据


查询单条数据


更新


更新也是调用save,发现有id就自动执行update语句
删除


根据条件查询

接口里增加的这个方面名字是约定,它会自动根据name去查询。


开启事务
需要开启事务的方法上增加@Transactional注解即可。
表单验证
1、实体类上用注解加条件:

2、方法参数上用@Valid指定验证的实体,后面紧接一个BindingResult参数获取验证结果。
@RequestMapping("addCompany")
public Company addCompany(@Valid Company company, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
return companyRepository.save(company);
}
这里如果不返回null,则执行到companyRepository.save(company);时会抛出异常,jpa会验证实体类的属性。
异常处理
可以异常进行统一处理
定义异常处理类:
MyExceptionHandler.java:
package com.example.demo;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Created by Administrator on 2017/12/15.
*/
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value=Exception.class)
@ResponseBody
public String kkk() {
return "error happend";
}
}
它会自动捕捉异常调用方法。


一般会自定义异常类型,调用不同方法,这里使用Exception异常类只是为了做示范。自定义异常类通常会增加code属性,抛异常时传入code,外面可以取得code。
通常使用方法:
ResultEnum.java
public enum ResultEnum {
ERROR(-1, "未知错误"),
SUCCESS(0, "成功");
private Integer code;
private String msg;
ResultEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
MyException.java
public class MyException extends RuntimeException {
private Integer code;
public MyException(ResultEnum resultEnum) {
super(resultEnum.getMsg());
this.code = resultEnum.getCode();
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
抛异常
throw new MyException(ResultEnum.ERROR);
MyExceptionHandler.java
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Created by Administrator on 2017/12/15.
*/
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value=MyException.class)
@ResponseBody
public String kkk(MyException e) {
return e.getMessage();
}
@ExceptionHandler(value=RuntimeException.class)
@ResponseBody
public String kkkkkk(RuntimeException e) {
return "runtime exception";
}
}
注意这里的MyException继承了RuntimeException,它会自动判断抛出异常的类型,找到最精确的匹配,如抛出MyException,则会调用kkk方法。
使用AOP
加入aop依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2、编写切面类:
如:
package com.example.demo;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
* Created by Administrator on 2017/12/14.
*/
@Aspect
@Component
public class CompanyAspect {
@Before("execution(public * com.example.demo.CompanyController.list(..))")
public void beforeList() {
System.out.println("aspect--before list");
}
}
3、测试:

springaop各种注解的详细用法参见"springaop.docx"
启动类的位置和spring扫描包路径
默认的spring扫描包路径是从启动类开始一层层往下扫描,也可以在启动类上用注解设置,如:
@ComponentScan(basePackages={"com.example"})

如果类没有在扫描包路径下,则即使加了@Component等注解也不会被ioc容器发现。
使用单元测试的时候,由于单元测试类必须在启动类的包路径以下才能找到依赖,因此启动类最好放到最外层。
关于包路径的说明

通过这个菜单可以修改:


测试包的根目录下面的所有测试类的测试方法,都会加入maven的test生命周期运行。
使用单元测试
在test/java这种测试包下创建测试的java文件即可,一般测试包层级跟main/java下的包层级一致,,测试类也跟main/java里的类一一对应,便于管理。
可以使用idea快速创建测试文件,在需要测试的方法上右键-go to-Test,然后选方法创建文件就行了。
但创建出来的类上需要手动加上@RunWith(SpringRunner.class)、@SpringBootTest注解。



测试service的方法
测试service的方法很简单,只用注入service即可,如:
import
com.example.demo.Company;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by Administrator
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class CompanyServiceTest {
@Autowired
CompanyService companyService;
@Transactional
@Test
public void testGetOne() {
Company company = companyService.getOne(2L);
System.out.println("company=" + company);
Assert.assertEquals(company.getAge(), new Integer(56));
}
}
跟一般的Junit测试类似,也是在方法上点右键运行即可。这里加@Transactional是因为jpa的懒加载,加上它防止session提前关闭。
使用Mock测试controller的方法
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.junit.Assert.*;
/**
* Created by Administrator
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class CompanyControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void list() throws Exception {
mvc.perform(MockMvcRequestBuilders.get("/company/list"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().string("kkk"));
}
}
注意这里增加了@AutoConfigureMockMvc注解。mock会自动发送http请求,这里是使用get方法请求"/company/list"这个路径,断言返回状态为200(isOk()),返回值是"kkk",否则报错。

跳过单元测试

整合Mybatis
整合mybatis其实就是将数据库连接配置到配置文件,再通过mybatis.mapper-locations=classpath:mybatis/*.xml
属性指定映射文件的扫描路径,它就会到映射文件中找到namespace对应的Mapper接口,如:

,
然后自动创建接口实现类放到spring容器,bean的id就是接口名的首字母小写,如:

加入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.33</version>
</dependency>
</dependencies>
创建实体类User.java
package com.example2.domain;
public class User {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
创建UserMapper.java
import
com.example2.domain.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* Created by Administrator on 2017/12/8.
*/
public interface UserMapper {
List<User> getAll();
User getOne(Long id);
void insert(User user);
void update(User user);
void delete(Long id);
}
如果启动类上的@MapperScan没有扫描到,这里可以加上@Mapper让其扫描到。
创建映射文件User.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE
mapper
PUBLIC
"-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example2.mapper.UserMapper" >
<select id="getAll" resultType="user" >
SELECT id, name, age
FROM users
</select>
<select id="getOne" parameterType="long" resultType="user" >
SELECT id, name, age
FROM users
WHERE id = #{id}
</select>
<insert id="insert" parameterType="user" >
INSERT INTO users (name,age)
VALUES
(#{name}, #{age})
</insert>
<update id="update" parameterType="user" >
UPDATE
users
<set>
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age},</if>
</set>
WHERE
id = #{id}
</update>
<delete id="delete" parameterType="long" >
DELETE FROM users
WHERE
id =#{id}
</delete>
</mapper>
配置文件application.properties
mybatis.type-aliases-package=com.example2.domain
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8
spring.datasource.username
= root
spring.datasource.password = 123456
mybatis.mapper-locations=classpath:mybatis/*.xml
创建UserController.java
package com.example2;
import com.alibaba.fastjson.JSON;
import com.example2.domain.User;
import com.example2.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* Created by Administrator on 2017/12/5.
*/
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
protected UserMapper userMapper;
@RequestMapping("list")
public String list() {
return JSON.toJSONString(userMapper.getAll());
}
@RequestMapping("{id}")
public String findOne(@PathVariable Long id) {
return JSON.toJSONString(userMapper.getOne(id));
}
@RequestMapping("add")
public String add(@RequestParam("name") String name, @RequestParam("age") Integer age) {
User user = new User();
user.setName(name);
user.setAge(age);
userMapper.insert(user);
return "add success";
}
@RequestMapping("update/{id}")
public String updateCompany(@PathVariable("id") Long id, @RequestParam("name") String name, @RequestParam("age") Integer age) {
User user = new User();
user.setId(id);
user.setName(name);
user.setAge(age);
userMapper.update(user);
return "update success";
}
@RequestMapping("delete/{id}")
public String deleteCompany(@PathVariable("id") Long id) {
userMapper.delete(id);
return "delete success";
}
}
创建主运行类KkkApplication.java
package com.example2;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example2.mapper")
public class KkkApplication {
public static void main(String[] args) {
SpringApplication.run(KkkApplication.class, args);
}
}
这里的@MapperScan表示扫描Mapper的包路径,如果没有它,则每个需要映射的Mapper都需要加@Mapper注解。
数据库ddl:
-- Table "users" DDL
CREATE TABLE `users` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`name` varchar(30) COLLATE utf8_bin DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
测试:
查询列表
http://localhost:8080/user/list
查询单个
http://localhost:8080/user/1
新增
http://localhost:8080/user/add?name=zhangsan&age=18
更新
http://localhost:8080/user/update/1?name=kkkkkk&age=26
删除
http://localhost:8080/user/delete/16
常见问题:
1、通过Mapper没有找到匹配的xml报错。(启动不报错,使用到才报错)
Mybatis大致流程是启动时会把User.xml内容加载到内存,其中的
<mapper namespace="com.example.demo.mappers.UserMapper" >
很关键,它需要对应到对应的全路径接口名上。
然后程序运行期创建Mapper对象时,会找对应的内存中的xml的接口名,如果找不到,就会报
这种异常。
2、Mapper没有被扫描到(启动就报错,注入Mapper失败)
满足如下任何1个条件Mapper就能被扫描到:
Mapper接口在启动类的@MapperScan配置的包里面。如:Mapper接口加@Mapper注解,并且能够被springboot扫描到(默认是启动类所在包及其子路径下)
静态资源文件
使用默认静态资源目录
直接访问的静态资源文件如html、js、css等可以放到静态资源文件目录,用浏览器直接访问,而不需要写controller来转。
默认的静态资源目录是classpath下的META-INF/resources、resources、static、public.
使用如:
这里在static目录下放了kkk3.html静态资源文件。
静态文件目录的优先级是META-INF/resources > resources > static > public。
自定义配置静态资源目录和访问路径
application.properties中配置:
# 默认为classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
spring.resources.static-locations=classpath:/myres2/
# 默认为/**,不能用逗号分隔
spring.mvc.static-path-pattern=/m2/**
访问:
使用自定义类代码方式指定静态资源目录和访问路径
也可以自定义静态资源的目录,只需要继承WebMvcConfigurerAdapter类即可,如:
MyWebAppConfigurer.java:
import
org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/abc/**").addResourceLocations("classpath:/myres/");
super.addResourceHandlers(registry);
}
}
这里自定义了静态资源路径classpath:/myres/,访问的时候用/abc前缀。
映射磁盘绝对路径静态资源
如:
MyWebAppConfigurer.java:
import
org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class MyWebAppConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/ddd/**").addResourceLocations("file:/d:/");
super.addResourceHandlers(registry);
}
}
这里将/ddd/前缀的路径映射到了d盘根目录。
这种方式可以用在CMS发布系统后台发布的静态资源供前台访问的情况等。
自定义错误页面
404、500等跳到指定错误路径上
在启动类中增加方法,如:
public class MvbApplication {
public static void main(String[] args) {
SpringApplication.run(MvbApplication.class, args);
}
/**
* 自定义错误页面
*
* @return
*/
@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/error/error404");
ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/error500");
container.addErrorPages(error404Page, error500Page);
}
};
}
}
404时跳/error/error404,500时跳/error/error500
使用拦截器
拦截器类继承HandlerInterceptor,
MyInteceptor:
import
org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInteceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("preHandle:" + httpServletRequest.getRequestURI());
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("afterCompletion");
}
}
配置类:
CustomWebMvcConfigurerAdapter:
import
com.mvb.interceptors.MyInteceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class CustomWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInteceptor()).addPathPatterns("/**").excludePathPatterns("/doLogin", "/error", "/user/add");
}
}
配置了拦截器的匹配路径和排除路径。
请求访问时,会先去找对应的controller路径,如果没有,就把请求路径改成/error,然后进拦截器。而不是先进拦截器再找controller路径,而是有controller路径,才进拦截器。
因此排除路径中通常都有/error,这个路径是controller找不到时默认的访问路径。
开启热部署(目前bug较多,不建议使用)
注意:idea里热部署有个很SB的bug,重启服务可能会报端口占用,需要到任务管理器里杀java进程才能完全停止服务。
1、开启项目自动编译
2、按ctrl+Alt+Shift+/,开启服务运行时自动make选项
3、插件增加springloaded依赖配置:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.8.RELEASE</version>
</dependency>
</dependencies>
</plugin>
打包成war包
spring boot默认打包成jar包,其中包含了tomcat,执行时用"java -参数"执行,在某些情况下需要打成war包,然后放到tomcat的webapps下再由tomcat加载运行。
配置:
- pom.xml加入plugin
<!-- 打war包配置 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceExcludes>src/main/resources/**</warSourceExcludes>
<!-- war包名称 -->
<warName>kkk</warName>
</configuration>
</plugin>
其中的kkk是包名,最后会生成kkk.war
2、启动类继承SpringBootServletInitializer:
public class TestbootwarApplication extends SpringBootServletInitializer {
// 这个方法注释掉也不影响打包,后期如有其他报错,考虑是否是因为这里注释掉引起的
// @Override
// protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
// return application.sources(TestbootwarApplication.class);
// }
public static void main(String[] args) {
SpringApplication.run(TestbootwarApplication.class, args);
}
}
注意:打出的war包里可能没有web.xml文件,因此需要在tomcat7以上的版本运行才能正常访问。
整合dubbo
代码:
这里创建一个empty project,然后创建provider和consumer2个spring boot
模块。
1、服务提供者provider配置:
服务端需要配置的有:引入依赖、配置dubbo端口和zk地址、@Service注解服务实现类、启动类加@EnableDubbo注解
1、pom加入依赖(dubbo和zookeeper的依赖):
<!-- dubbo -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
2、编写client接口和实现,@Service注解服务接口实现类:
UserClient.java:
package com.dubbo.provider.client;
public interface UserClient {
public String sayHello(String name);
}
UserClientImpl.java:
package com.dubbo.provider.service.api;
import com.alibaba.dubbo.config.annotation.Service;
import com.dubbo.provider.client.UserClient;
@Service
public class UserClientImpl implements UserClient {
public String sayHello(String name) {
return "hello:" + name;
}
}
注意这里的@service是引入dubbo的,而不是spring的。
3、application.yml配置dubbo的zk地址、name等。
server:
port: 8081
dubbo:
application:
name: dubbo-provider
registry:
address: 10.248.224.18:2181
protocol: zookeeper
protocol:
name: dubbo
port: 20880
注意这里的port如果不配置,默认是20880
4、启动类加上@EnableDubbo注解:
ProviderApplication.java:
package com.dubbo.provider;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableDubbo
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
注意只有provider提供者需要加这个注解,consumer消费端不需要加。
2、服务消费者consumer配置:
消费端主要配置:引入maven依赖、配置dubbo和zk地址、@Reference注入服务接口类。
1、pom加入依赖(dubbo和zookeeper的依赖),同provider的一样:
<!-- dubbo -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
2、引入提供者的client的jar包,一般是pom.xml加入client的依赖,这里为了方便,就直接把provider的client的java拷贝过来,注意包路径要一致。
3、application.yml配置dubbo的zk地址、name等,同provider的一样,只是不用配置端口。
server:
port: 9898
dubbo:
application:
name: dubbo-consumer
registry:
address: 10.248.224.18:2181
protocol: zookeeper
protocol:
name: dubbo
4、使用@Reference注入接口依赖调用:
package com.dubbo.consumer.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.dubbo.provider.client.UserClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Reference
UserClient userClient;
@RequestMapping("test")
public String test() {
return userClient.sayHello("ggg");
}
}
注意:这里要使用dubbo提供的@Reference注入,而不能用@Autowired或者@Component等spring的注入。
5、访问测试:
3、流程分析:
1、provider发布流程
1、找@Service注解的bean
provider项目启动时发现有@EnableDubbo注解,就会去找有dubbo的@Service注解的bean。
如:
@Service
public class UserClientImpl implements UserClient
2、取出接口全路径名。
找到以后取出该类实现的第一个接口的全路径.
这里是:
com.dubbo.provider.client.UserClient
a:必须至少实现一个接口,否则报错:
b:如果该类实现了多个接口,也只认写在最前面的一个,如:
public class UserClientImpl implements UserClient,Serializable
也是取出com.dubbo.provider.client.UserClient
如写成:
public class UserClientImpl implements Serializable,UserClient
则服务名变成java.io.Serializable了
3、将接口全名作为服务名发布,注册到注册中心。
2、consumer调用流程
通过@Reference找到接口全名对应的服务。如果注入失败则该属性为null,而不会报错,待调用时会提示空指针错误。
所以空指针表示该服务没有注入成功,有多种可能导致,如consumer接口全路径名与provider接口全路径名不一致、provider没有服务提供者(如provider如果忘了写@EnableDubbo注解则不会发布服务,也就没有提供者了)等情况。
直连方式:
1、pom.xml去掉zk的依赖
不需要zk的依赖,只需要dubbo的即可:
<!-- dubbo -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<!--<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>-->
2、provider端的application.yml的registry配置成N/A
dubbo:
application:
name: mvb-erp-api
registry: N/A
# registry:
# address: 10.248.224.18:2181
# protocol: zookeeper
# timeout: 60000
protocol:
name: dubbo
port: 20880
3、consumer端的application.yml去掉registry的配置:
dubbo:
application:
name: mvb-erp-man
# registry:
# address: 10.248.224.18:2181
# protocol: zookeeper
# timeout: 60000
# protocol:
# name: dubbo
4、consumer的@reference引用标签上加url属性:
@Reference(url = "dubbo://127.0.0.1:20880/com.mvb.erp.client.dubbo.ErpDubboService")
ErpDubboService erpDubboService;
浙公网安备 33010602011771号