Springboot学习记录
一、原理初探
springboot所有自动配置都是在启动的时候扫描并加载:spring.factories 所有的自动配置类都在这里面,但不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后配置成功!
- @SpringBootApplication:springboot应用注解,核心注解
- @Configuration:自动装配
- @ConditionOnxxxx:条件满足才会触发成功
- @AutoConfigurexxxx:自动装配xx
- @Enablexxxx:开启某个功能
-
springboot在启动的时候,从类路径下/META-INF/spring.factories获取指定的值。 -
将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置。 -
以前我们需要自动配置的东西,现在springboot帮我们做了。 -
整个javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.7.3.jar这个包下。 ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fO28tAky-1666933329202)(img/image-20221024105706114.png)]](https://img-blog.csdnimg.cn/badf6e1654aa4dc8b3b6307ac993de69.png) -
它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器。 -
容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig。 -
有了自动配置类,免去了我们手动编写配置文件的工作。
自动装配原理的精髓:
-
springboot启动会加载大量的自动配置类 -
我们看我们需要的功能有没有在springboot默认写好的自动配置类当中 -
我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在其中,我们就不需要在手动配置了) -
给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可 xxxxAutoConfiguration:自动配置类;给容器中添加组件 xxxxProperties:封装配置文件中相关属性
二、yaml
三、jsr303校验
@Validated :数据校验
四、常用方法
-
在application.yml中添加debug: true 来查看哪些自动配置类生效,哪些没有生效! debug: true
-
@Configuration :扩展springmvc -
EnableWebMvc: 导入了一个类:DelegatingWebMvcConfiguration:从容其中获取所有的webmvcconfig
五、SpringBoot Web开发
要解决的问题:
- 导入静态资源
- 首页
- jsp,模板引擎Thymeleaf
- 装配扩展SpringMVC
- 增删改查
- 拦截器
- 国际化
5.1、静态资源
源码:在WebMvcAutoConfiguration.class下的方法中:
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
registration.addResourceLocations(new Resource[]{resource});
}
});
}
}
同时在上面的getStaticLocations()方法中:
public static class Resources {
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
private String[] staticLocations;
private boolean addMappings;
private boolean customized;
private final WebProperties.Resources.Chain chain;
private final WebProperties.Resources.Cache cache;
总结:
- 在springboot中,我们可以使用以下处理静态资源,文件建在resources目录下
- webjars localhost:8080/wabjars/目录结构
- public, static, /**, resources localhost:8080/访问页面
- resources>static>public
5.2、首页
源码:在WebMvcAutoConfiguration.class下的方法中:
private Resource getWelcomePage() {
String[] var1 = this.resourceProperties.getStaticLocations();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String location = var1[var3];
Resource indexHtml = this.getIndexHtml(location);
if (indexHtml != null) {
return indexHtml;
}
}
ServletContext servletContext = this.getServletContext();
if (servletContext != null) {
return this.getIndexHtml((Resource)(new ServletContextResource(servletContext, "/")));
} else {
return null;
}
}
private Resource getIndexHtml(String location) {
return this.getIndexHtml(this.resourceLoader.getResource(location));
}
private Resource getIndexHtml(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && resource.getURL() != null) {
return resource;
}
} catch (Exception var3) {
}
return null;
}
从中我们可以得出我们的首页,名字命名为index.html,放在资源目录下即可,都可以被访问到
-
首页配置:注意点,所有页面的静态资源都需要使用thymeleaf接管;@{} -
页面国际化:
- 我们需要配置i18n文件
- 我们如果需要在项目中进行按钮自动切换,我们需要自定义一个组件
LocaleResolver - 记得将自己写的组件配置到springrongqi中
@Bean - #{}
页面国际化(页面语言转换)地区解析器源码:
public LocaleResolver localeResolver() {
if (this.webProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.WebProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.webProperties.getLocale());
} else {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.webProperties.getLocale());
return localeResolver;
}
}
public class AcceptHeaderLocaleResolver implements LocaleResolver {
private final List<Locale> supportedLocales = new ArrayList(4);
@Nullable
private Locale defaultLocale;
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = this.getDefaultLocale();
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
} else {
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = this.getSupportedLocales();
if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
} else {
return defaultLocale != null ? defaultLocale : requestLocale;
}
} else {
return requestLocale;
}
}
}
static public final Locale SIMPLIFIED_CHINESE = createConstant("zh", "CN");
故我们可以根据上面的源码进行添加我们的组件: public class MyLocaleResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest request) {
String language = request.getParameter("l");
Locale locale = request.getLocale();
if (!StringUtils.isEmpty(language)){
String[] split = language.split("_");
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
结果如图(实现中英文切换): ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z4RL8IMz-1666933329203)(img/image-20221025164215467.png)]](https://img-blog.csdnimg.cn/ea0a4e765a2641119bb831c303da250a.png) ![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-waELy4bc-1666933329203)(img/image-20221025164229839.png)]](https://img-blog.csdnimg.cn/b272e114fa824851b6c0488994336662.png)
5.3、thymeleaf模板引擎
导入thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
源码:在ThymeleafProperties.class里面:
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
结论:
- 我们将html文件放在templates资源文件下,然后controller就可以执行页面跳转了
在页面传递参数的时候,出现取值爆红的情况:
解决方案:在上面一行添加->
5.4、springmvc配置
springboot有很多自动配置,原理都是一样的,通过这个WebMvc的自动配置原理分析,学会一种学习方式:
- 通过源码探究,得出结论,这个结论一定是属于自己的,一通百通。
springboot的底层,大量的用到了这些设计细节思想,所以,没事需要多阅读源码!得出结论。
springboot在自动配置很多组件的时候,先看容器中有没有用户自己配制的,如果有,就使用用户的,反之,就是默认配置;如果有些组件可以存在多个,比如我们的视图解析器,就将用户和自己默认的组合起来。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
六、Data
配置application.yml,使用druid数据源,导入log4j
spring:
datasource:
username: root
password: '123456'
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRusMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
类似web.xml可以配置servlet和filter,举例
package com.heze.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.HashMap;
@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
HashMap<String, String> initParameters = new HashMap<>();
initParameters.put("loginUsername", "admin");
initParameters.put("loginPassword", "123456");
initParameters.put("allow", "");
bean.setInitParameters(initParameters);
return bean;
}
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
HashMap<String, String> initParameters = new HashMap<>();
initParameters.put("exclusions", "*.js,*.css,/druid/*");
bean.setInitParameters(initParameters);
return bean;
}
}
结果:可以监听我们方法的使用
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJOG1Lgu-1666933329204)(img/image-20221026173035678.png)]](https://img-blog.csdnimg.cn/df8c6935e3484bc89114d8a5b14a3ed3.png)
七、整合Mybatis框架
7.1、添加依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
7.2、在application.properties中添加mybatis
#整合mybatis
mybatis.type-aliases-package=com.heze.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
7.3、在资源文件中新建mybatis.mapper,将xxxMapper.xml文件放在下面
7.4、xxxMapper.java使用@Mapper注解,表示这是一个mybatis的mapper类
八、SpringSecurity(安全)
在web开发中,安全第一位! 过滤器,拦截器~
功能性需求:否
做网站:在涉及隐私等时候需要考虑安全
所以
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications.
翻译:Spring安全性是一个专注于为Java应用程序提供身份验证和授权的框架。
即:权限的释放:
- 功能权限
- 访问权限
- 菜单权限
- …拦截器,过滤器:大量的原生代码~冗余
从MVC-spring-springboot—框架思想 — > 逐步简化代码
记住几个类:
- WebSecurityConfigurerAdpter:自定义Security策略 用来继承
- AuthenticationManagerBuilder:自定义认证策略
- @EnableWebSecurity:开启WebSecurity模式, @Enablexxxx开启某个功能
Spring Security的两个主要目标是“认证”和“授权”(访问控制)
“认证”(Authentication)
“授权”(Authorization)
步骤:
-
导入依赖 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
-
在config下新建SecurityConfig.java 授权: @EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
http.formLogin();
}
http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");
http.csrf().disable();
http.logout().logoutSuccessUrl("/");
http.rememberMe().rememberMeParameter("remember");
}
认证:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("admin2").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
九、Swagger简介
前后端分离:
Vue + SpringBoot
后端时代:前端只用管理静态页面;html==>后端。模板引擎 JSP ==>后端是主力
前后端分离式时代:
- 后端:后端控制层,服务层,数据访问层
- 前端:前端控制层,视图层
- 伪造后端数据,json。已经存在了,不需要后端,前端工程依旧能够跑起来
- 前后端如何交互? ====>API
- 前后端相对独立,松耦合
- 前后端甚至可以部署在不同的服务器上
产生一个问题:
- 前后端集成联调,前端人员和后端人员无法做到“及时协商,尽早解决”,最终导致问题的集中爆发;
解决方案:
- 首先制定scheme【计划的提纲】,实时更新最新API,降低集成的风险;
- 早些年:制定word计划文档;
- 前后端分离:
- 前端测试后端接口:postman
- 后端提供接口,需要实时更新最新的消息及改动!
9.1、Swagger
- 号称世界上最流行的Api框架;
- RestFul Api文档在线生成工具=>Api文档与API定义同步更新
- 直接运行,可以在线测试API接口;
- 支持多种语言:(Java,Php…)
9.2、在项目中使用Swagger需要springbox
9.3、SpringBoot集成Swagger
-
新建一个SpringBoot项目=>web项目 -
导入依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
-
编写一个Hello工程 -
配置Swagger===>Config @Configuration
@EnableSwagger2
public class SwaggerConfig {
}
-
测试运行
提一下:分布式Dubbo + ZooKeeper + SpringBoot
- zookeeper:注册中心
- dubbo-admin:是一个监控管理后台查看我们注册了哪些服务,哪些服务被消费了
- Dubbo:jar包~
十、总结
? 简而言之:springboot省下了我们很多开发的代码量,使用起来比较简单,但是又由于整合了很多东西,我们在表面开发的时候需要提高读源码的能力,然后就是提高自己上手写项目的能力,理解逻辑,明白具体开发过程,然后查漏补缺,提升自己。
|