随时可能更新的springboot学习笔记

学习自狂神说秦疆

第一个SpringBoot程序

官方:提供了一个快速生成的网站,IDEA集成了这个网站。

  • 可以在官网直接下载后,导入IDEA开发
  • 直接使用IDEA创建一个springboot项目(一般使用该方式,只有企业版才有该功能)

几点注意事项:

  • 用idea创建一般不需要添加什么额外依赖
  • 创建时经常会出现连接错误,建议使用https://start.aliyun.com
  • 所有的代码需要放在项目目录下,否则无效
  • 彩蛋:在resources文件夹下新建一个bannner.txt,在里面可以放一些其他东西来代替spring自带的ascii画。推荐网站:Spring Boot banner在线生成工具
  • application.properties:springboot核心配置文件
    • 通过server.port修改默认端口
# 应用服务 WEB 访问端口
server.port=8080

原理初探

自动配置:

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中
  • 我们在写或者引入一些springboot依赖的时候,不需要指定版本,因为有这些版本仓库

启动器:

<!--启动器-->        
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
</dependency>
  • 其实就是springboot的启动场景。
  • 比如spring-boot-starter-web,他就会帮我们自动导入web环境所有的依赖
  • springboot会将所有的功能场景,都变成一个个的启动器
  • 我们要使用什么功能,就只需要找到对应的启动器就可以了starter

主程序

//程序的主入口,标注这个类是一个springboot的应用,spring类下的所有资源被导入
@SpringBootApplication
public class HelloworldApplication {
    //SpringApplication
    public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
    }

}
  • 注解

    • @SpringBootConfiguration:springboot的配置
      • @Configuration:spring配置类
      • @Component:说明这也是一个spring的组件
    • @EnableAutoConfiguration:自动配置
      • @AutoConfigurationPackage:自动配置包
        • @Import({Registrar.class}):导入选择器包注册
      • @Import({AutoConfigurationImportSelector.class}):自动配置导入选择
    //获取所有的配置
    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    
    
    //获取候选的配置
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }
    

    META-INF/spring.factories:自动配置的核心文件

    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    //所有资源加载到配置类中
    

img

结论:springboot所有自动配置都是在启动的时候扫描并加载:spring.factories所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,我们自动装配就会生效,然后就配置成功。

1.srpingboot在启动的时候,从类路径下META-INF/spring.factories获取指定的值;

2.将这些自动配置的类导入容器,自动配置类就会生效,帮我们进行自动配置

3.以前我们需要自动配置的东西,现在springboot帮我们做了

4.整合javaEE,解决方案和自动配置的东西都在spring-boot-test-autoconfigure-2.3.7.RELEASE.jar这个包下

5.它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器;

6.容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig;

7.有了自动配置类,免去了我们手动编写配置文件的工作了

JavaConfig @Configuration @Bean

Docker:进程

SpringApplication

这个类主要做了以下四件事情:

1、推断应用的类型是普通的项目还是Web项目

2、查找并加载所有可用初始化器 , 设置到initializers属性中

3、找出所有的应用程序监听器,设置到listeners属性中

4、推断并设置main方法的定义类,找到运行的主类

yaml配置

# springboot这个配置文件中到底可以配置哪些东西呢?官方的配置太多了,了解原理,一通百通
# 对空格的要求十分高

#可以注入到我们的配置类中

# 普通的key-value
name: ysy

# 应用服务 WEB 访问端口
server:
  port: 8081

# 对象
student:
  name: ysy
  age: 22

# 行内写法
student1: {name: ysy, age: 22}

# 数组
pets:
  - cat
  - dog
  - pig

# 数组行内写法
pets1: [cat, dog, pig]


yaml可以直接给实体类赋值

/*
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birth;
    private Map<String, Object> maps;
    private List<Object> lists;
    private Dog dog;
}
person:
  name: ysy
  age: ${random.int}
  happy: false
  birth: 2021/08/06
  maps: {k1: v1, k2: v2}
  lists: [code, music, girl]
  dog:
    name: wangcai
    age: 3

测试类:

@SpringBootTestclass Springboot02ConfigApplicationTests {    @Autowired    private Person person;    @Test    void contextLoads() {        System.out.println(person);    }}

松散绑定:

last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。

结论:

配置yaml和配置properties都可以获取到值 , 强烈推荐 yaml;

如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;

如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!

数据校验:

@Validated //数据校验,报红记得添加依赖

@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;

/*空检查
@Null       验证对象是否为null
@NotNull    验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank   检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty   检查约束元素是否为NULL或者是EMPTY.
    
Booelan检查
@AssertTrue     验证 Boolean 对象是否为 true  
@AssertFalse    验证 Boolean 对象是否为 false  
    
长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内  
@Length(min=, max=) string is between min and max included.

日期检查
@Past       验证 Date 和 Calendar 对象是否在当前时间之前  
@Future     验证 Date 和 Calendar 对象是否在当前时间之后  
@Pattern    验证 String 对象是否符合正则表达式的规则

.......等等
除此以外,我们还可以自定义一些数据校验规则
*/

run方法流程分析

img

配置文件优先级

springboot 启动会扫描以下位置的application.properties或者application.yaml文件作为Spring boot的默认配置文件:

优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件

优先级由高到底,高优先级的配置会覆盖低优先级的配置;

SpringBoot会从这四个位置全部加载主配置文件;互补配置;

# springboot的多环境配置,可以选择激活哪一个配置
# 要使用application-dev.properties中的配置,只需要在默认配置下添加如下语句:
spring.profiles.active=dev

也可以在一个配置文件里写入多个配置:

server:
  port: 8081

# 选择激活哪一个配置
spring:
  profiles:
    active: dev
  
--- # 相当于一个文件分割符
server:
  port: 8082
spring:
  profiles: dev # 给文件命名
  
--- 
server:
  port: 8083
spring:
  profiles: test
  
# 可以通过debug=true来查看,哪些自动配置类生效,哪些没有生效

springboot web开发

要解决的问题:

  • 导入静态资源
  • 首页
  • jsp,模板引擎Thymeleaf
  • 装配扩展SpringMVC
  • 增删改查
  • 拦截器
  • 国际化

静态资源

public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
        Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
        CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
        if (!registry.hasMappingForPattern("/webjars/**")) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

        String staticPathPattern = this.mvcProperties.getStaticPathPattern();
        if (!registry.hasMappingForPattern(staticPathPattern)) {
            this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
        }

    }
}

总结:

  1. 在springboot,我们可以使用以下方式处理静态资源
    • webjars localhost:8080/webjars/
    • public, static, /**, resources localhost:8080/
  2. 优先级:resources > static(默认) > public

模板引擎

结论:只要需要使用thymeleaf,只需要导入对应的依赖就可以了。我们将html页面放在templates目录下

依赖:

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

index.html:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--所有的html元素都可以被thymeleaf替换接管   th:元素名-->
<div th:text = "${msg}"></div>
</body>
</html>

controller:

//在templates目录下的所有页面,只能通过controller来跳转。这个需要模板引擎的支持:thymeleaf
@Controller
public class IndexController {

    @RequestMapping("/index")
    public String index(Model model){
        model.addAttribute("msg", "hello,springboot");
        return "index";
    }
}

springMVC自动配置

//如果你想diy一些定制化的功能,只要写这个组件,然后将它交给springboot,springboot就会帮我们自动装配
//扩展springMVC
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

    //public interface ViewResolver: 实现了视图解析器接口的类,我们就可以把它看做视图解析器

    @Bean
    public ViewResolver myViewResolver(){
        return new MyViewResolver();
    }

//自定义了一个自己的视图解析器
    public static class MyViewResolver implements ViewResolver {
        @Override
        public View resolveViewName(String s, Locale locale) throws Exception {
            return null;
        }
    }

}
//如果我们要扩展MVC,官方建议我们这样去做
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {

//    视图跳转
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/ysy").setViewName("index");
    }
}

全面接管即:SpringBoot对SpringMVC的自动配置不需要了,所有都是我们自己去配置!

只需在我们的配置类中要加一个@EnableWebMvc。不推荐使用。

@EnableWebMvc //这就是导入了一个类:DelegatingWebMvcConfiguration: 从容器中获取所有的webmvcconfig

接下来是写一个模拟数据库操作,代码就不放上来了。

#关闭模板引擎的缓存
spring.thymeleaf.cache=false
  1. 首页配置:

    1. 注意点:所有页面的静态资源都要使用thymeleaf接管。

    2. url: @{}

  2. 页面国际化:

    1. 我们需要配置i18n文件
    2. 我们如果需要在项目中进行按钮自动切换,需要自定义一个组件LocaleResolver
    3. 记得将自己写的组件配置到Spring容器中Bean
    4. {}

  3. 登录 + 拦截器

  4. 员工列表展示

    1. 提取公共页面

      1. th:fragment="sidebar"
      2. <div th:replace="~{commons/commons :: topbar}"></div>
    2. 如果要传递参数,可以直接用()传参,接收判断

    3. 列表循环展示

  5. 添加员工

    1. 按钮提交
    2. 跳转到添加页面
    3. 添加员工成功
    4. 返回首页
  6. CRUD搞定

  7. 404

前端:

  • 模板:别人写好的,我们拿来改成自己需要的

  • 框架:组件:自己手动组合拼接。Bootstrap, Layui, Semantic-ui

    • 栅格系统

    • 导航栏

      <nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
      
      <div th:insert="~{commons/commons :: topbar}"></div>
      
    • 侧边栏

      <nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
          
      <div th:insert="~{commons/commons :: sidebar}"></div>
      
    • 表单

整合JDBC,DRUID

首先导入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

<!--        Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>

<!--        Mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

application.yaml:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/photovoltaic?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 1472583690
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

JDBCController:

@RestController
public class JDBCController {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @GetMapping("/userList")
    public List<Map<String, Object>> userList(){
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }

}

DruidConfig:

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource(){
        return new DruidDataSource();
    }

//    后台监控 : 相当于web.xml
//    因为springboot内置了servlet容器,所以没有web.xml,替代方法:ServletRegistrationBean
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean<StatViewServlet> bean
                = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
//        后台需要有人登陆,账号密码配置
        HashMap<String, String> initParameters = new HashMap<>();
//        增加配置
        initParameters.put("loginUsername", "admin");//登陆key是固定的 loginUsername loginPassword
        initParameters.put("loginPassword", "123456");

//        允许谁可以访问
        initParameters.put("allow", "");

//        禁止谁能访问
//        initParameters.put("468", "127.123.132.12");

        bean.setInitParameters(initParameters);//设置初始化参数
        return bean;
    }


    @Bean
    public FilterRegistrationBean webStatFilter(){
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new WebStatFilter());
//可以过滤哪些请求呢
        Map<String, String> initParameters = new HashMap<>();
        initParameters.put("exclusions", "*.js, *.css, /druid/*");//这些东西不进行统计


        bean.setInitParameters(initParameters);
        return bean;
    }
}

Spring Security

在web开发中,安全第一位。

做网站,安全应该在设计之初考虑

shiro,springsecurity

AOP:横切,配置类

securityconfig:

//AOP:拦截器
@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();
    }

//    认证,springboot 2.1.x可以直接使用
//    密码编码:PasswordEncoder
//    在Spring Security5中新增了很多了加密方式
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        这些数据正常应从数据库中读取
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("1").password(new BCryptPasswordEncoder().encode("1")).roles("vip2", "vip1")
                .and()
                .withUser("2").password(new BCryptPasswordEncoder().encode("2")).roles("vip1", "vip2", "vip3")
                .and()
                .withUser("3").password(new BCryptPasswordEncoder().encode("3")).roles("vip1");
    }
}

RouterController:

@Controller
public class RouterController {

    @RequestMapping({"/index", "/"})
    public String index(){
        return "index";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }

    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id") int id){
        return "views/level1/" + id;
    }

    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id") int id){
        return "views/level2/" + id;
    }

    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id") int id){
        return "views/level3/" + id;
    }

}
posted @ 2021-08-05 15:49  shanaprprpr  阅读(77)  评论(0)    收藏  举报