一.Springboot常见错误总结
- 控制台乱码问题(插件启动,控制台乱码问题)
在pom文件,springboot插件启动中,增加如下(一些虚拟机方法)
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- spring-boot:run 中文乱码解决 -->
<configuration>
<fork>true</fork>
<!--增加jvm参数-->
<jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
</configuration>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>
</dependencies>
</plugin>
- 无法自动注入问题
①.包扫描错误,@MapperScan指定错误 或 @ComponentScan入口类位置错误
②.Mapper文件错误,dao的实现类无法创建,导致无法注入 - yml文件格式错误
- springboot的pom文件中引入多余jar包
- springboot的版本问题(springboot 2.0 使用 spring 5.x)
二.Springboot自带日志
- 默认使用 logback,相比log4j更轻量级,无需引入依赖或jar
- 导入logback.xml 定制显示信息
- 日志 (面试关键点)
- 级别
OFF > FATAL> ERROR > WARN > INFO > DEBUG
- OFF
- FATAL
指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
- ERROR
指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
- WARN
- INFO
消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
- DEBUG
- TRACE
- ALL
ps:log4j建议只使用四个级别
- 两个种类
①.根日志:项目级别日志,项目中所有指定的信息都会被日志展示出来。
②.子日志:包级别/类级别的日志,获取指定包或类的日志信息。
- logback日志框架使用
logback.xml (resources根目录下)
<?xml version="1.0" encoding="UTF-8" ?>
<!--配置项-->
<configuration>
<!-- appender:日志如何做展示的配置 name:展示配置的别名 class:日志如何做展示-->
<appender name="out" class="ch.qos.logback.core.ConsoleAppender">
<!-- 展示布局 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern> [%p] %d{yyyy-MM-dd HH:mm:ss} %m %n</pattern>
</layout>
</appender>
<!-- 根日志 -->
<root level="ERROR">
<appender-ref ref="out"/>
</root>
<!-- 子日志 使用根日志的展示配置-->
<logger name="com.abc.dao" level="DEBUG"/>
</configuration>
三.Springboot的热部署
- 热部署是什么?
大家都知道在项目开发过程中,常常会改动页面数据或者修改数据结构,为了显示改动效果,往往需要重启应用查看改变效果,其实就是重新编译生成了新的 Class 文件,这个文件里记录着和代码等对应的各种信息,然后 Class 文件将被虚拟机的 ClassLoader 加载。
而热部署正是利用了这个特点,它监听到如果有 Class 文件改动了,就会创建一个新的 ClaassLoader 进行加载该文件,经过一系列的过程,最终将结果呈现在我们眼前。
- 使用 Spring Loaded
- 使用 spring-boot-devtools
- jrebel插件
要求:springboot项目,电脑配置好
原理:
java编译,使用类加载器,编译成class文件
jrebel使用两个类加载器,一个在文件运行时生效,一个在代码变动时类加载器生效
四.Springboot中面向切面编程(AOP)
- 回顾spring基于注解的aop开发(spring 4 提出概念,纯注解开发)
- 引入aop注解依赖
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.1</version>
</dependency>
- 通知类上加上@Aspect,指定类为通知类
@Around(execution(* com.baizhi.service..(…))) 指定方法为环绕通知方法
- springboot基于注解的aop开发
- 引入依赖
<!-- Springboot的AOP支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 开发通知类
常用注解:
@Configuration
@Component
@Aspect //作用是把当前类标识为一个切面供容器读取
@Before //标识一个前置增强方法,相当于BeforeAdvice的功能
@After //final增强,不管是抛出异常或者正常退出都会执行
@Around //环绕增强,相当于MethodInterceptor
@AfterReturning //后置增强,相当于AfterReturningAdvice,方法退出时执行
@AfterThrowing //异常抛出增强,相当于ThrowsAdvice
示例代码:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//使用在哪里:方法生效
@Target({ElementType.METHOD})
//什么时候生效
@Retention(RetentionPolicy.RUNTIME)
public @interface AspectAnnotion { //自定义注解
public String value() default "no search method~~~";
}
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
//@Component
@Configuration //开启Spring相关自动配置
@Aspect //指定类为通知类
public class AspectDemo {
@Pointcut("execution(public * com.abc.service.*.*(..))")
public void pointCut(){
}
/**
* @Before 前置通知
* @param joinPoint
*/
@Before("execution(* com.abc.service.*.*(..))")
public void before(JoinPoint joinPoint){
System.out.println(joinPoint.getArgs()); //获取参数
System.out.println(joinPoint.getTarget()); //获取目标对象
System.out.println(joinPoint.getSignature().getName()); //获取方法名
System.out.println("------------this is before-------------");
}
/**
* @Around 环绕通知
* @param proceedingJoinPoint
* @return
*/
@Around(value = "@annotation(com.baizhi.aspect.AspectAnnotion)")
public Object around(ProceedingJoinPoint proceedingJoinPoint){
//获取方法上注解
//拿到方法method
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
//拿到类对象 反射
AspectAnnotion aspectAnnotion = method.getAnnotation(AspectAnnotion.class);
//通过反射拿到实例 调用方法 拿到自定义的方法名
String name = aspectAnnotion.value();
System.out.println(proceedingJoinPoint.getArgs()); //同joinPoint
try {
//前置
Object proceed = proceedingJoinPoint.proceed(); //放行目标方法
//后置
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
/**
* @After 后置通知
* @param joinPoint
*/
@After("pointCut()")
public void after(JoinPoint joinPoint) {
}
}
Proceedingjoinpoint 和 JoinPoint 的关系?
环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的。
环绕通知 ProceedingJoinPoint 执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别。
Proceedingjoinpoint 继承了 JoinPoint 。是在JoinPoint的基础上暴露出 proceed 这个方法。proceed很重要,这个是aop代理链执行的方法。
暴露出这个方法,就能支持 aop:around 这种切面(而其他的几种切面只需要用到JoinPoint,这跟切面类型有关), 能决定是否走代理链还是走自己拦截的其他逻辑。建议看一下JdkDynamicAopProxy的invoke方法,了解一下代理链的执行原理。
五.Springboot中拦截器的实现
- 回顾springMVC中如何实现拦截器
- 实现HandlerInterceptor接口,覆盖preHandle(进入前)、postHandle(跳转前)、afterCompletion(跳转后)
- springmvc.xml中配置
<!--配置拦截器示例 student-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/user/*"/>
<mvc:exclude-mapping path="/vcode/*"/>
<bean class="com.abc.interceptor.InterceptorController"/>
</mvc:interceptor>
</mvc:interceptors>
- springboot中实现拦截器
- 实现一个自定义拦截器
- 书写配置类 extends WebMvcConfigurerAdapter
@EnableWebMvc
@Configuration //或者 @SpringBootConfiguration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
//类似DI注入
@Bean
InterceptorController localInterceptor() {
return new InterceptorController();
}
//覆盖springboot中的默认拦截器方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localInterceptor())
.addPathPatterns("/menu/*") //拦截哪些
.excludePathPatterns("/user/*","/vcode/*"); //不拦截哪些
super.addInterceptors(registry);
}
}
注意:存在静态资源丢失问题。。。
六.Springboot中上传下载
- 在yml文件中,修改上传文件大小限制
spring:
http:
multipart:
enabled: true
max-file-size: 30MB #上传文件限制大小
max-request-size: 30MB #限制文件大小
- 上传几乎没有什么变化
- 下载,使用springboot内置FileCopyUtils.copy
@RequestMapping("/download")
@ResponseBody
public void download(String filename, HttpServletRequest request, HttpServletResponse response) throws IOException {
//获取文件路径
String realPath = request.getSession().getServletContext().getRealPath("/upload");
//找到文件,获取输入流
File file = new File(realPath, filename);
FileInputStream fileInputStream = new FileInputStream(file);
//获取响应类型
String substring = filename.substring(filename.lastIndexOf("."));
//设置响应类型
response.setContentType(substring);
//设置响应头
response.setHeader("Content-Disposition", "attachment;fileName=" + filename);
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
//文件Copy
FileCopyUtils.copy(fileInputStream,outputStream);
}
补充.我的后期项目.yml
server:
port: 9090
context-path: /cmfz
jsp-servlet:
init-parameters:
development: true #开启jsp的热部署配置
tomcat:
max-http-post-size: -1
spring:
mvc:
view:
prefix: /
suffix: .jsp
profiles:
active: dev #激活哪个文件生效
datasource:
type: com.alibaba.druid.pool.DruidDataSource #指定数据源
driver-class-name: com.mysql.jdbc.Driver #指定驱动
url: jdbc:mysql://localhost:3306/abc?characterEncoding=UTF-8 #指定url
username: root
password: root
jackson:
date-format: "yyyy-MM-dd" #适用responseBody时,设置全局日期格式
time-zone: "GMT+08" #设置时区
http:
multipart:
enabled: true
max-file-size: 30MB #上传文件限制大小
max-request-size: 30MB #限制文件大小
mybatis:
mapper-locations: classpath:com/baizhi/mapper/*Mapper.xml #指定mapper配置文件位置
type-aliases-package: com.abc.pojo #指定起别名的包
数据源测试
@Autowired
DataSourceProperties dataSourceProperties;
@Autowired
ApplicationContext applicationContext;
@Test
public void testttt() throws SQLException {
DataSource dataSource = applicationContext.getBean(DataSource.class);
System.out.println(dataSource.getConnection());
}