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就能被扫描到:

  1. Mapper接口在启动类的@MapperScan配置的包里面。如:
    
  2. 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加载运行。

配置:

  1. 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;

posted @ 2020-12-16 10:48  吴克兢  阅读(86)  评论(0)    收藏  举报