Spring Boot微操
一、创建Spring Boot项目
- 创建maven项目
- 添加springboot的parent
- 添加web的启动依赖
- 设置一个引导类
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
// 引导类不能放在根目录下
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
// 启动Spring boot 需要指定引导类
// 为了能使用虚拟机指令启动,要将args传入run中
SpringApplication.run( SpringBootDemoApplication.class, args );
}
}
- 设置用maven插件启动
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
- 整合mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- 创建实体类、mapper和mapper.xml
- 创建application.properties或application.yml文件(默认会找一个application的文件)
- junit测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
二、切换配置文件
1) yml 配置Bean注入
- 导入配置文件处理器:
<!--导入配置文件处理器,这样在对bean用properties或yml文件进行绑定的时候就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
- 创建一个Bean对象:
@Component // 将Person放入容器中
@ConfigurationProperties(prefix="person") // 必须具备Get Set方法
public class Person {
private String name;
private int age;
private Date date;
private String[] hobby;
private Set<String> gfs;
private Map<String, String> maps;
private Dog dog;
private List<Dog> dogs;
// Getter & Setter
// 重写toString()
}
class Dog {
private String name;
// Getter & Setter
// 重写toString()
}
或者
@Component // 将Person放入容器中
public class Person {
@Value("${person.name}")
private String name;
// Getter
// 重写toString
}
- 创建一个yml文件
person:
name: jack
age: 25
date: 2020/11/21 00:44:02
hobby: [game,music]
gfs: [a,b,c]
maps:
key1: value1
key2: value2
dog:
name: 阿黄
dogs:
- name: 阿花
- name: 阿云
- Test
//@RunWith(SpringRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes= SpringBootDemoApplication.class)
public class BootTest {
@Autowired
private Person person;
@Test
public void test() {
System.out.println(person.toString());
}
}
2) 切换配置文件
- 配置文件的命名:
- 指定要使用的配置文件:
1. 通过application.yml指定要使用application-uat.yml配置文件
spring:
profiles:
active: uat
2. 不创建默认的application.yml,而是在运行过程中通过参数来指定要使用的配置文件
- 项目打包:
mvn clean package -Dmaven.test.skip=true 跳过test进行打包
- 通过Java虚拟机参数:
java -jar -D--spring.profiles.active=dev SpringBootDemo-1.0-SNAPSHOT.jar
- 通过SpringBoot参数指定:
java -jar SpringBootDemo-1.0-SNAPSHOT.jar --spring.profiles.active=dev
三、常见配置项
# ----------------------------------------
# WEB PROPERTIES
# ----------------------------------------
# EMBEDDED SERVER CONFIGURATION (ServerProperties)
server.port=8080 # server.port=8080
server.servlet.context-path= # Context path of the application.
# HTTP encoding (HttpEncodingProperties)
spring.http.encoding.charset=UTF-8 # Charset of HTTP requests and responses.Added to the "Content-Type" header if not set explicitly.
# JACKSON (JacksonProperties)
spring.jackson.date-format= # Date format string or a fully-qualified date format class name. For instance, `yyyy-MM-dd HH:mm:ss`.
# SPRING MVC (WebMvcProperties)
spring.mvc.servlet.load-on-startup=-1 # Load on startup priority of the dispatcher servlet.
spring.mvc.static-path-pattern=/** # Path pattern used for static resources.
spring.mvc.view.prefix= # Spring MVC view prefix.
spring.mvc.view.suffix= # Spring MVC view suffix.
# DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
spring.datasource.password= # Login password of the database.
spring.datasource.url= # JDBC URL of the database.
spring.datasource.username= # Login username of the database.
参考官网:Spring Boot Reference Guide
四、Spring Boot中使用Redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 转换格式 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Autowired
private StringRedisTemplate template;
@Override
public List<User> selectAll() {
// 将用户列表存入redis
String userList = template.opsForValue().get("userList");
List<User> listUser = new ArrayList<>();
if (userList == null || "".equals(userList)) {
listUser = userMapper.selectUserList();
String userJson = JSONObject.toJSONString(listUser);
template.opsForValue().set("userList",userJson);
} else {
listUser = (List<User>) JSONObject.parse(userList);
}
return listUser;
}
}
五、日志
1. 默认日志
Spring Boot默认使用Logback,通过slf4j做进一步封装,之后底层日志换成log4j代码也无需改动。
public class
private Logger logger = LoggerFactory.getLogger(getClass());
---
---
logger.debug("访问了****");
application.yml
logging:
level:
root: info #不让控制台输出太多没用的信息
com.ttc.controller: debug #只让controller输出debug级别的日志
file:
# path:
name: ../../appdata/log/SpringBootDemo.log
#path: 指定path时 名字默认为spring.log
#name: 指定name可以同时指定目录
max-size: 1MB #到达1MB切换文件
2. 切换至log4j
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--找到起步依赖 排除logback-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
log4j2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<Configuration status="INFO" monitorInterval="30">
<Properties>
<Property name="LOG_HOME">../../appdata/log</Property>
<Property name="FILE_NAME">SpringBootDemo</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%t] %-5p %c -%m%n"/>
</Console>
<!--滚动日志配置-->
<RollingRandomAccessFile name="RollingAppender"
fileName="${LOG_HOME}/${FILE_NAME}.log"
filePattern="${LOG_HOME}/$${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log">
<PatternLayout pattern="%d [%t] %-5p [%c] - %m%n" />
<Policies>
<TimeBasedTriggeringPolicy interval="1"/>
<SizeBasedTriggeringPolicy size="1 MB"/>
</Policies>
<!--同一文件夹下最多20个文件-->
<DefaultRolloverStrategy max="20"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<!--info级别的日志打印到控制台-->
<AppenderRef ref="Console"/>
</Root>
<Logger name="com.ttc.controller" level="debug">
<!--debug级别的日志打印到日志文件-->
<AppenderRef ref="RollingAppender"/>
</Logger>
</Loggers>
</Configuration>
六、Hanler(天坑!!!!)
实现功能:判断前台是否第一次登录,有无Cookie,无跳转到登录,有CooKie判断redis缓存中Cookie是否超时,超时跳转到登录。
这里真的是天坑SpringBoot的拦截器注入问题!!!
上代码:
handler
public class LoginInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private StringRedisTemplate template;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("拦截器执行"+ request.getRequestURI() +"。。。");
if (request.getRequestURI().contains("login")){
logger.debug("拦截器放开。。。");
return true;
}
User user = new User();
String userInfo;
// 获取cookie
Cookie[] cookies = request.getCookies();
if (cookies == null) {
logger.debug("用户未登录,跳转到登陆页面。。。");
response.sendRedirect("/login.html");
return false;
}
for (Cookie cookie: cookies) {
if ("SESSION_ID".equals(cookie.getName())) {
logger.debug("获取redis中的Session信息");
if (cookie.getValue()==null || "".equals(cookie.getValue())) {
// 删除cookie
logger.debug("删除cookie。。。");
cookie.setMaxAge(0); // 删除cookie
response.sendRedirect("/login.html");
return false;
}
userInfo = template.opsForValue().get(cookie.getValue());
if (userInfo == null || "".equals(userInfo)) {
logger.debug("cookie中"+cookie.getValue());
// 删除cookie
logger.debug("redis中"+userInfo);
logger.debug("删除cookie。。。");
cookie.setMaxAge(0); // 删除cookie
response.sendRedirect("/login.html");
return false;
}
logger.debug("userInfo from redis " + userInfo);
user = JSON.parseObject(userInfo, User.class);
break;
}
}
if (user == null) {
logger.debug("用户未登录,跳转到登陆页面。。。");
response.sendRedirect("/login.html");
return false;
} else {
return true;
}
}
}
注册类
要使用Handler还要把注册Handler,这里才是天坑!!!!
网上说解决拦截器注入失败的原因,要注册Bean或者配置启动类里面的包扫描器的,真的理解原理了才知道,到底是怎么回事。
- 首先,启动类的问题,启动类不会扫描他的父类包,所以启动类一定放在所有类的最外层。
方法:
如果启动类所在的包为:com.ttc,则只会扫描com.ttc包及其所有子包,如果service或dao所在包不在com.ttc及其子包下,则不会被扫描!
只要在启动类前面加上:
@SpringBootApplication(scanBasePackages = “com.*”)
- 其次,要使拦截器中能够注入,一定要在注册类中把拦截器注入到容器中。
方法两点:
- 手动创建Bean,用loginInterceptor()方法返回的拦截器放在注册器中
- 在拦截器上加@Component,并在注册器中@Autowired
目的:无外乎就是保证整个容器中单例并已经被注入到容器中
七、异常处理
通过AOP实现异常捕捉统一处理:
/**
* 通过AOP切面监听全局的错误信息,进行统一的错误处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
//指定该ExceptionHandler能处理的类型
@ExceptionHandler(value = TtcException.class)
public String handler(TtcException e, Model model) {
logger.error(e.getMessage());
model.addAttribute("errCode", e.getErrCode());
model.addAttribute("errMsg", e.getErrMsg());
return "error";
}
}
八、整合dubbo
- 创建空的maven项目和子项目
- 说明:
SpringBoot_Dubbo:作为整个项目
common:存放Service等公共类
consumer:作为服务消费方
provider:作为服务提供方 - pom:
在consumer和provider中引入common:
- common中创建公共Service
- 定义接口:
public interface UserService {
String getNameById(int id);
}
- provider
- 创建文件:
- 实现接口:
@Service // org.apache.dubbo.config.annotation.Service包下的注解
public class UserServiceImpl implements UserService {
@Override
public String getNameById(int id) {
if (id == 1) {
return "jerry";
}
if (id == 2) {
return "tom";
}
return "error";
}
}
- 服务注册:
ProviderApplication.java
@SpringBootApplication
@EnableDubbo(scanBasePackages = "com.ttc.service.impl")
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
- 配置文件:
application.yml
dubbo:
application:
name: user-service # 服务提供方的名字
qos-enable: false
registry:
protocol: zookeeper
address: 192.168.1.214:2181
protocol:
name: dubbo
port: 28888
- consumer
- 创建文件:
- Controller:
UserController.java
@RestController
public class UserController {
@Reference //org.apache.dubbo.config.annotation.Reference;
private UserService userService;
@RequestMapping("/user/{id}")
public String getNameById(@PathVariable int id) {
String name = userService.getNameById(id);
return name;
}
}
- 启动类:
ConsumerApplication.java
@SpringBootApplication
// 这里面不用注册服务,消费方要访问服务
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
- 配置文件:
dubbo:
application:
name: user-service # 服务提供方的名字
qos-enable: false
registry:
protocol: zookeeper
address: 192.168.1.214:2181
#集群写法 address: zookeeper: //192.168.1.214:2181?backup=192.168.1.215,192.168.1.216
!注意:
如果报错连接失败,可能是zookeeper中的配置有问题,也可能是依赖的jar包默认的超时时间有问题
解决方案:
- 在zoo.cfg中修改tickTime=20000
- 在yml中增加:
dubbo:
registry:
timeout: 20000
config-center:
timeout: 20000