热部署
导入依赖
<!-- 热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
ctrl+shift+alt + /
Registry
![]()
settings
![]()
- 基本上是针对的是jsp和controller层修改之后,不需要重新启动服务器,实现自动刷新的效果
拦截器
登录拦截器的类
@Component public class LoginInterceptor implements HandlerInterceptor { /** * 每个控制层的中的每个方法 - 小handler - 处理器方法 * * 进入handler之前执行 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //false - 不放行.. //true - 放行... HttpSession session = request.getSession(); User user = (User) session.getAttribute("user"); if(null!=user) return true; return false; } }
springboot拓展了springmvc的功能
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //注册登录拦截器 registry.addInterceptor(loginInterceptor) .addPathPatterns("/**") .excludePathPatterns("/users/send","/users/login","/phone/index","/plugins/**","/imgs/**","/css/**","/js/**"); //排除哪些路径是需要拦截,哪些路径是不需要拦截 } }
SpringBoot整合分页插件
导入依赖
<!-- springboot分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency>
PageInfo<实体名> pageInfo = new PageInfo<>(mapper查询出来的集合对象);
此处的实体一定是entity[有表对应的],不能是第三方实体.
jquery-load
异步加载页面.
后台cart/cart -> cart.jsp[单独抽出来的一部分] - 存在核心的分页的数据的那一部分的jsp内容
index_load.jsp - div采用jquery.load方式去异步加载上面的jsp文件/cart/cart
图片上传
图片nginx服务器
限制图片上传的大小
spring: servlet: multipart: # 启用 enabled: true # 单个文件的大小 max-file-size: 50MB # 设置总上传数据总大小 max-request-size: 100MB
服务器端校验
前端校验可以被绕过,所以必须要进行服务器端校验 - 后端校验
导入依赖
<!-- 后端校验--> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
fastjson
将java对象转换成json字符串.
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.78</version> </dependency>
package tech.aistar.fastjson; import com.alibaba.fastjson.JSON; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import tech.aistar.mapper.UsersMapper; import tech.aistar.model.entity.Users; /** * 本类用来演示: * * @author: success * @date: 2021/10/8 2:53 下午 */ @SpringBootTest public class TestFastJSon { @Autowired private UsersMapper usersMapper; @Test public void testJavaToJsonString(){ Users users = usersMapper.findByEmail("849962874@qq.com"); String jsonStr = JSON.toJSONString(users); System.out.println(jsonStr); } }
//设置文档的输出类型
response.setContentType("application/json;charset=utf-8");
//获取文档输出流
PrintWriter out = response.getWriter();
String jsonResult = JSON.toJSONString(new Result("507","非法的token"));
out.println(jsonResult);
密码加密
导入依赖 - 权限认证的框架 - SpringSecurity,还有shiro
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
单侧
package tech.aistar.pwd; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * 本类用来演示: 加密技术 * * @author: success * @date: 2021/10/8 3:18 下午 */ @SpringBootTest public class PasswordTest { //思考 - 为什么RedisTemplate可以直接使用 //springboot底层的自动化配置原理有关系. @Autowired private BCryptPasswordEncoder encoder; @Test public void testEncoder(){ //页面上输入的数据. String pwd = "admin123"; //加密之后保存到db中 //db的password的字段的长度修改长一点 //不可逆 - 底层随机盐 String encoderPwd = encoder.encode(pwd); //密码需要进行密文的处理的 //$2a$10$XHCcsh3SWU/gjOzht17CPOX2W4pGFiBogyUCfUVBGyIfuJRY6UX5m System.out.println(encoderPwd); } @Test public void testGet(){ //直接进行判断 boolean flag = encoder.matches("admin123","$2a$10$XHCcsh3SWU/gjOzht17CPOX2W4pGFiBogyUCfUVBGyIfuJRY6UX5m"); System.out.println(flag); } }
需要对拦截的请求处理一下 - 放行,否则会自动跳转默认的登录界面里面
package tech.aistar.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * 本类用来演示: * * @author: success * @date: 2021/10/8 3:17 下午 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/**").permitAll() .anyRequest().authenticated() .and().csrf().disable(); } }
redis+token如何防止表单重复提交
/user/registerView - server端生成token[令牌] - 存储到redis中,并且将这个token存储到request作用域中.
转发到WEB-INF/register.jsp文件 - form表单
<input type='hidden' value='${token}' name='token'>
点击注册按钮 - 都可以将这个隐藏域的token值一起发送给server - /user/register
/user/register后端是可以拿到redis中的token,然后和form表单提交过来的token值进行比较.
如果一样,则直接顺利执行.执行注册操作之后,将redis中的token删除.
再次发送,隐藏域中的token会再次和redis中的token进行比较[已经被删除了] - 比较失败! - 重复提交了.
RabbitMQ
ActiveMQ:基于JMS,Apache
RocketMQ:(Rocket,火箭)阿里巴巴的产品,基于JMS,目前由Apache基于会维护
Kafka:分布式消息系统,亮点:吞吐量超级高,没秒中数十万的并发。
RabbitMQ:(Rabbit,兔子)由erlang语言开发,基于AMQP协议,在erlang语言特性的加持下,RabbitMQ稳定性要比其他的MQ产品好一些,而且erlang语言本身是面向高并发的编程的语言,所以RabbitMQ速度也非常快。且它基于AMQP协议,对分布式、微服务更友好。
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)
特点
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题实现高性能,高可用,可伸缩和最终一致性[架构]
使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ。其使用场景为:异步处理,应用解耦,流量削锋和消息通讯四个场景。
安装
- Erlang - http://www.erlang.org/downloads
- RabbitMQ - https://github.com/rabbitmq/rabbitmq-server/releases
- 注意一下两者的版本,需要相互兼容.否则安装运行失败!
RestFul
Rest和RestFul
REST(Representational State Transfer) 表现状态转换【架构样式的网络系统】
REST – Representational State Transfer 直接翻译:表现层状态转移
通俗理解就是 - URL定位资源,用HTTP动词(GET,POST,DELETE,PUT,PATCH)描述操作。
put和patch都是进行更新的http动词,区别是
put - 更新全局,比如更新页面,更新用户,username,email.password… - 消耗更多的带宽 .
在页面中仅仅只更新了一个一个属性,比如username.提交请求的时候,也会将这个user中所有的属性全部
发送到后端,封装到user对象中.
@PutMapping @ResponseBody public Result update(Users user){ //.... }
patch - 更新局部,如果仅仅更新了username,只会发送username到后端的user对象中.
通俗来讲就是:资源在网络中以某种表现形式进行状态转移。分解开来:
Resource:资源,即数据
Representational:某种表现形式,比如用JSON,XML,JPEG等;
State Transfer:状态变化。通过HTTP动词实现。RESTFUL是一种网络应用程序的设计风格和开发方式
REST指的是一组架构约束条件和原则(描述的是在网络中client和server的一种交互形式)。满足这些约束条件和原则的应用程序或设计就是Restful
RestFul特点
1).每一个URI代表1种资源; 2).CRUD(POST GET PUT DELETE) 3).通过操作资源的表现形式来操作资源 4).资源的表现形式是XML或者HTML 5).客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息
RestFul使用
URL设计 A).动宾结构(动词+宾语[名词]) GET 获取 安全 POST 创建 不安全 PUT[PATCH] 更新【X-HTTP-Method-Override】 不安全 DELETE 删除【X-HTTP-Method-Override】 不安全 B).使用 GET : /users - 获取用户列表 GET : /users/1 - 获取 Id 为 1 的用户 POST : /users - 创建一个用户 PUT : /users/1 - 替换 Id 为 1 的用户 PATCH : /users/1 - 修改 Id 为 1 的用户 DELETE : /users/1 - 删除 Id 为 1 的用户
@Value和@ConfigurationProperties
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value - 一一绑定
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties - 统一绑定.
Spring Configuration Proccessor 配置文件处理器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
全局异常处理器
什么是全局异常处理器?
就是把错误异常统一处理的方法。
应用场景:
1、当你使用jsr303参数校验器,如果参数校验不通过会抛异常,而且无法使用try-catch语句直接捕获,这时可以使用全局异常处理器来捕捉该异常。
2、当你自定义了一个异常类,可以在全局异常处理器中来捕捉该异常。(当然也可以直接在抛出异常处直接捕获,但是这样需要在每一个抛出的地方都写一次捕获代码,看起来不够美观且复用性不强,其他异常同理)。
package tech.aistar.exception;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import tech.aistar.model.bo.Result;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 本类用来演示: 全局异常处理器
*
* @author: success
* @date: 2021/10/12 9:37 上午
*/
@ControllerAdvice
public class GlobalExceptionHandler {
// @ExceptionHandler(value = Exception.class)
// @ResponseBody
// public Result handler(){
// System.out.println("==============");
// return new Result("500","sorry,server is update");
// }
/**
* 测试阶段...
* @param e
* @return
*/
// @ExceptionHandler(value = Exception.class)
// @ResponseBody
// public Result handler(Exception e){
// System.out.println("==============");
// return new Result("500","sorry,server is update",e.getMessage());
// }
@ExceptionHandler(value = Exception.class)
public void handler(HttpServletResponse response){
//servlet代码中重定向
try {
response.sendRedirect("/boot/error/error.html");
} catch (IOException e) {
e.printStackTrace();
}
}
}
SpringBoot自动化配置原理
SpringBoot中如何注入bean
直接在bean上加上@Component注解
需要新建一个bean的配置类 - 专门用来注入bean
package tech.aistar.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * 本类用来演示: 注入第三的bean,让这些bean受spring管理 * * @author: success * @date: 2021/10/8 3:21 下午 */ @Configuration public class BeanConfig { //<bean id="" class=""> @Bean public BCryptPasswordEncoder encoder(){ return new BCryptPasswordEncoder(); } }
保留了spring的传统,新建了一个spring文件 - 配置了bean标签
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="teacher" class="tech.aistar.model.bo.Teacher"></bean> </beans>
在springboot的主程序上读取spring文件
@ImportResource("classpath:applicationContext.xml")
- 自动化配置 - 自动化注入
场景
part01 - 之前已经在配置类中进行了配置了
@Autowired private BCryptPasswordEncoder encoder;
part02 - 并没有手动配置
@Autowired private RedisTemplate redisTemplate; @Autowired private RabbitMqTemplate rrr;
剖析源码
@SpringBootApplication - @EnableAutoConfiguration - @Import({AutoConfigurationImportSelector.class})
研究AutoConfigurationImportSelector - 重要的方法 - selectImports
手动实现
分别写MyRabbitTemplate和MyRedisTemplate
public class MyRabbitTemplate { public MyRabbitTemplate(){ System.out.println("rabbit..."); } } package tech.aistar.auto; public class MyRedisTemplate { public MyRedisTemplate(){ System.out.println("redis..."); } }
AutoRabbitConfig和AutoRedisConfig
public class AutoRabbitConfig { @Bean public MyRabbitTemplate rabbit(){ return new MyRabbitTemplate(); } } package tech.aistar.auto; import org.springframework.context.annotation.Bean; /** * 本类用来演示: * * @author: success * @date: 2021/10/12 10:36 上午 */ public class AutoRedisConfig { @Bean public MyRedisTemplate redis(){ return new MyRedisTemplate(); } }
读取自动化配置的类
package tech.aistar.auto; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; /** * 本类用来演示: * * @author: success * @date: 2021/10/12 10:38 上午 */ public class MyAutoConfigurationSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //源码去读取了spring.factories文件 - 自动化配置类的全限定名 return new String[]{"tech.aistar.auto.AutoRabbitConfig","tech.aistar.auto.AutoRedisConfig"}; } }
配置类
package tech.aistar.auto; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * 本类用来演示: * * @author: success * @date: 2021/10/12 10:40 上午 */ @Configuration @Import(MyAutoConfigurationSelector.class) public class AllBean { }
流程图
SpringBoot日志框架配置
- 日志门面 - 日志接口 - slfj4-api.jar
- 日志实现
- 如果日志接口和日志实现不匹配,还需要适配包
推荐使用logback日志框架[slf4j和logback - 作者是同一个人,不需要]
<?xml version="1.0" encoding="UTF-8"?> <!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。 debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 --> <configuration scan="false" scanPeriod="60 seconds" debug="false"> <!-- 定义日志的根目录 --> <property name="LOG_HOME" value="app/log" /> <!-- 定义日志文件名称 --> <property name="appName" value="aistar-springboot"></property> <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 --> <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender"> <!-- 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 --> <!--<layout class="ch.qos.logback.classic.PatternLayout">--> <encoder> <springProfile name="dev"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} -----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern> </springProfile> <springProfile name="prod"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ====> [%thread] ==== %-5level %logger{50} - %msg%n</pattern> </springProfile> </encoder> <!--</layout>--> </appender> <!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 --> <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 指定日志文件的名称 --> <file>${LOG_HOME}/${appName}.log</file> <!-- 当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名 TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> <totalSizeCap>3GB</totalSizeCap> </rollingPolicy> <!-- 日志输出格式: --> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern> </encoder> </appender> <!-- logger主要用于存放日志对象,也可以定义日志类型、级别 name:表示匹配的logger类型前缀,也就是包的前半部分 level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出, false:表示只用当前logger的appender-ref,true: 表示当前logger的appender-ref和rootLogger的appender-ref都有效 --> <!-- hibernate logger --> <logger name="tech.aistar" level="info" /> <!-- Spring framework logger --> <logger name="org.springframework" level="debug" additivity="false"></logger> <!-- root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应, 要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。 --> <root level="info"> <appender-ref ref="stdout" /> <appender-ref ref="appLogAppender" /> </root> </configuration>