SpringBoot学习笔记
发布日期:2021-05-09 09:24:35 浏览次数:17 分类:博客文章

本文共 38293 字,大约阅读时间需要 127 分钟。

目录

SpringBoot

什么是Spring:Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

如何简化Java的开发的

  • 基于POJO的轻量级和最小侵入性编程
  • 通过IOC,依赖注入和面向接口实现松耦合
  • 基于切面和惯例进行声明式编程
  • 通过切面和模板减少样式代码

SppringBoot主要优点:

  • 开箱即用,提供各种默认配置来简化项目配置
  • 内嵌式容器简化Web项目
  • 没有冗余代码和生成XML配置的要求
  • 让所有的Spring开发者更快的入门

什么是微服务:微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合,可以通过http的方式进行互通,要说微服务架构,可以先看看我们以前的单体应用架构。

单体应用架构:我们将一个应用中的所有应用服务都封装在一个应用中,数据访问,web访问等放在一个war包中。

  • 这样做的好处是,易于开发和测试,也十分方便部署,当要拓展时,只要将war复制多份,然后放到多个服务器上,在做个负载均衡就可以了。
  • 单体架构应用的缺点是,哪怕要修改一个非常小的地方,都要停掉整个服务,重新打包,再部署这个应用的war包,特别是对于一个大型应用,我们不可能把所有内容放在一个应用里面,如何维护,如何分工合作都是问题。

微服务架构就是打破 all in one 的架构方式,把每个功能元素独立出来,把独立出来的功能元素动态组合,节省调用资源,每个功能元素的服务器都是一个可替换的,可独立升级的代码。

第一个SpringBoot

  1. 可以从官网新建项目后下载,解压后使用 IDEA 打开
  2. IDEA中新建 spring initiali 项目,选中 spring web 直接创建

导入 jar 包即可

自动装配

pom.xml

  • Spring Boot Dependencies 核心依赖在父工程中
  • 我们在写或者引入一些 SpringBoot 依赖的时候,不需要指定版本,就是因为父工程中已经写了。

启动器

org.springframework.boot
spring-boot-starter

比如:spring-boot-starter-web,它就会自动帮我们导入web项目所有的依赖。

springboot会将所有的的功能场景,都变成一个个的启动器。

需要什么功能,找到对应的启动器就可以了(官网找) 。

主程序

//标注这是一个springboot的应用@SpringBootApplicationpublic class Springboot01HelloworldApplication {    public static void main(String[] args) {        //启动springboot应用        SpringApplication.run(Springboot01HelloworldApplication.class, args);    }}

注解

@SpringBootConfiguration   //springboot的配置	@Configuration   //spring配置类			@Component    //说明这也是一个spring的组件@EnableAutoConfiguration   //自动配置	@AutoConfigurationPackage   //自动配置包		@Import({Registrar.class})   //导入注册器	@Import({AutoConfigurationImportSelector.class})   //导入选择器			//获取所有的配置            List
configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

获取候选的配置

protected List
getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List
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:自动配置的核心文件

SpringBoot所有装配是在启动时扫描并加载,所有的自动配置类都在spring.factories,但只有导入了对应的start,有了启动器,对应的配置才会生效,然后自动装配。

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

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

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

  4. 整合JavaEE,解决方案和自动配置的东西都在springframework.boot.autoconfigure:2.4.0包下。

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

  6. 容器中也会存在非常多的 ***AutoConfigure 的文件,就是这个类给容器导入了这个场景所需要的所有组件。

  7. 有了自动装配,免去了我们手动编写配置注入功能组件的工作。

SpringApplication这个类主要是:

  • 推断应用的类型是普通的项目还是web项目。
  • 查找并加载所有可用初始化器,设置的initializers属性中。
  • 找出所有的应用程序监听器,设置到listeners中。
  • 推断并设置main方法的定义类,找到运行的主类。

自动装配的原理:

  1. SpringBoot启动会加载大量的自动配置类。

  2. 我们看我们需要的配置类有没有在SpringBoot写好的自动配置类中。

  3. 再看这个配置类中到底装配了哪些组件(如果我们需要的组件在里面,就不用手动装配了)。

  4. 给容器中自动配置类添加组件时,会从properties类中获取某些属性,我们只需要在配置文件中指定这些属性即可。

    ***AutoConfiguration:自动配置类,给容器中添加组件

    ***Properties:封装配置文件的相关属性

yaml中配置debug:true,启动可以查看哪些配置类生效了。

在springboot中,有非常多的***.Configuretion会帮助我们进行拓展配置,只要看见了这个,需要注意。

@EnableWebMvc,这配置是导入了一个类,DelegatingWebMvcConfiguration,从容器中获取所有的WebMvcConfigurer。

yaml讲解

SpringBoot使用一个全局的配置文件,配置文件名称是固定的

  • application.properties
    • 语法结构:key=value
  • application.yaml
    • 语法结构:key:空格 value

配置文件可以帮我们修改SpringBoot自动配置的默认值。

YAML是"YAML Ain't a Markup Language"(YAML不是一种)的。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

标记语言

以前的配置文件,大多数使用xml来配置:比如一个简单的端口配置,对比一下yaml和xml

yaml配置:

server:  port: 8088

xml配置

8088

使用注解@ConfigurationProperties将配置文件中的值映射到这个组件中,将类中的属性与配置文件一一对应。

yaml也可以这样使用

person:  name: 周周${random.uuid}  age:  ${random.int}  happy: true  birth: 2020/10/10  maps: {k1: v1,k2: v2}  list:    - code    - music    - girl  dog:    name: ${person.zr:hello}_旺财    age: 5

也可以使用@PropertySource加载指定的properties文件【不建议使用,建议使用yaml】

@ConfigurationProperties只用绑定一次,@Value需要每个字段添加。

yaml可以松散绑定,即yaml中写first-name,实体类写firstName,-后的字母默认是大写的,这就是松散绑定。

JSR303数据验证,就是我们可以在字段增加一层过滤器验证,可以保证数据的合法性。

复杂类型对象,yaml可以封装对象,@Value就不可以。

结论:

  • 配置yaml和properties都可以获取到值,强烈推荐yaml。
  • 如果我们在某个业务中,只获取配置文件中的某个值,可以使用一下@Value。
  • 如果在javaBean和配置文件进行映射,就直接使用@ConfigurationProperties。

JSR303

验证是否为邮箱格式

@Component@ConfigurationProperties(prefix = "person")@Validated//数据校验public class Person {    @Email    private String name;}

其它注解

空检查@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=) Validates that the annotated string is between min and max included.日期检查@Past           验证 Date 和 Calendar 对象是否在当前时间之前@Future     验证 Date 和 Calendar 对象是否在当前时间之后@Pattern    验证 String 对象是否符合正则表达式的规则数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,因为表单值为“”时无法转换为int,但可以转换为Stirng为"",Integer为null@Min            验证 Number 和 String 对象是否大等于指定的值@Max            验证 Number 和 String 对象是否小等于指定的值@DecimalMax 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个通过BigDecimal定义的最大值的字符串表示.小数存在精度@DecimalMin 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个通过BigDecimal定义的最小值的字符串表示.小数存在精度@Digits     验证 Number 和 String 的构成是否合法@Digits(integer=,fraction=) 验证字符串是否是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。@Range(min=, max=) 检查数字是否介于min和max之间.@Range(min=10000,max=50000,message="range.bean.wage")private BigDecimal wage;@Valid 递归的对关联对象进行校验, 如果关联对象是个集合或者数组,那么对其中的元素进行递归校验,如果是一个map,则对其中的值部分进行校验.(是否进行递归验证)@CreditCardNumber信用卡验证@Email  验证是否是邮件地址,如果为null,不进行验证,算通过验证。@ScriptAssert(lang= ,script=, alias=)@URL(protocol=,host=, port=,regexp=, flags=)    @Pattern 正则表达式

Web开发

静态资源

在springboot中,我们可以使用以下路径处理静态资源

  • webjars 【WebMvcAutoConfiguration.addResourceHandlers】 webjars 需导入
  • public,static,/**,resources 【WebMvcAutoConfiguration-->ResourceProperties-->Resources】

优先级:resorces>static(默认)>public

首页

WebMvcAutoConfiguration下部分源码

private Optional
getWelcomePage() { String[] locations = WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations()); return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();}private Resource getIndexHtml(String location) { return this.resourceLoader.getResource(location + "index.html");}

即在资源配置目录下放入index.html即可自动识别。

Thymeleaf

pom中引入Thymeleaf,就可以将.html放在templates下访问。

org.springframework.boot
spring-boot-starter-thymeleaf

部分源码

@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";  ......}

可以看出来前缀是classpath:/templates/,后缀.html,即html文件放在classpath:/templates/下

测试

package com.zr.controller;//在templates下的所有页面,只能通过controller来跳转//需要模板引擎的支持@Controllerpublic class IndexController {    @RequestMapping("/test")    public String index(Model model){        model.addAttribute("msg","hello test");        return "test";    }}

test.html

拓展SpringMvc

package com.zr.config;//扩展springmvc@Configuration//@EnableWebMvc //这配置是导入了一个类,DelegatingWebMvcConfiguration,从容器中获取所有的WebMvcConfigurer。public class MyMvcConfig implements WebMvcConfigurer {    //视图跳转    @Override    public void addViewControllers(ViewControllerRegistry registry) {        registry.addViewController("/zr").setViewName("test");        registry.addViewController("/").setViewName("index");        registry.addViewController("/index.html").setViewName("index");    }    @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;         }     }}

员工管理系统

首页配置,使用了thymeleaf接管,所有的herf引用改为th:herf="@{}"(th标签支持xmlns:th="")

页面国际化

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

整合JDBC使用

对于数据访问层,无论使SQL(关系型数据库)还是NOSQL(非关系型数据库),SpringBoot底层都是采用 Spring Data 的方式进行统一处理。

application.yml

spring:  datasource:    username: root    password: 123456    #时区配置报错 增加时区配置serverTimezone=UTC    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&chacterEncoding=utf-8&serverTimezone=UTC    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis数据库 user表

测试

@SpringBootTestclass Springboot04DataApplicationTests {    @Autowired    DataSource dataSource;    @Test    void contextLoads() throws SQLException {        //查看默认的数据源        System.out.println(dataSource.getClass());        //获得数据库连接        Connection connection = dataSource.getConnection();        System.out.println(connection);        //***templete: SpringBoot已经配置好的bean 拿来即用        connection.close();    }}

controller

@RestControllerpublic class JdbcController {    @Autowired    JdbcTemplate jdbcTemplate;    //查询数据库的所有信息    //没有实体类,数据库的东西怎么取  Map    @GetMapping("/userlist")    public List
> userList(){ String sql = "select * from user"; List
> maps = jdbcTemplate.queryForList(sql); return maps; } @GetMapping("/addUser") public String addUser(){ String sql = "insert into user(id,name,pwd) values(7,'韩信','6666')"; jdbcTemplate.update(sql); return "add OK!"; } @GetMapping("/updateUser/{id}") public String updateUser(@PathVariable("id") int id){ String sql = "update user set name=?,pwd=? where id="+id; //封装 Object[] objects = new Object[2]; objects[0] = "小周啊"; objects[1] = "8888"; jdbcTemplate.update(sql,objects); return "update OK!"; } @GetMapping("/deleteUser/{id}") public String deleteUser(@PathVariable("id") int id){ String sql = "delete from user where id=?"; jdbcTemplate.update(sql,id); return "delele OK!"; }}

整合Druid数据源

Druid是阿里巴巴开源平台上的一个数据库连接池实现,结合了C3P0,DBCP,PEOXOOL等DB池的优点,同时加入了日志监控。

SpringBoot默认的是Hikari数据源。

application.yml

spring:  datasource:    username: root    password: 123456    #时区配置报错 增加时区配置serverTimezone=UTC    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&chacterEncoding=utf-8&serverTimezone=UTC    driver-class-name: com.mysql.cj.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSource    #SpringBoot默认不注入这些属性的,需要自己绑定    #durid配置    initialSize: 5    minIdel: 5    maxActive: 20    maxWait: 60000    timeBetweenEvictionRunsMillis: 60000    minEvictableIdleTimeMillis: 300000    validationQuery: SELECT 1 FROM DUAL    testWhileIdle: true    testOnBorrow: false    testOnReturn: false    poolPreparedStatements: true    #配置监控拦截,stat 监控统计, wall 防御sql注入,log4j 日志    #如果允许时报错,导入log4j依赖    filters: stat,wall,log4j    maxPoolPreparedStatementPerConnectionSize: 20    useGlobalDataSourceStat: true    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

监控

DruidConfig

@Configurationpublic class DruidConfig {    @ConfigurationProperties(prefix = "spring.datasource")    @Bean    public DataSource druidDataSource(){        return new DruidDataSource();    }    //后台监控    //springboot内置了Servlet,所有没有web.xml,替代方法: ServletRegistrationBean    @Bean    public ServletRegistrationBean statViewServlet(){        // ServletRegistrationBean
bean = new ServletRegistrationBean<>(new StatusManagerServlet(), "/druid/*"); ServletRegistrationBean
bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); //后台需要有人登录,账号密码配置 HashMap
initParameters = new HashMap<>(); //增加配置 initParameters.put("loginUsername","admin"); //登录的key是固定的 initParameters.put("loginPassword","123456"); //允许谁可以访问 initParameters.put("allow",""); //禁止谁能访问 initParameters.put("zr","39.405.48.101"); bean.setInitParameters(initParameters); //设置初始化参数 return bean; } @Bean //filter public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean
bean = new FilterRegistrationBean<>(); bean.setFilter(new WebStatFilter()); //可以过滤哪些请求 HashMap
initParameters = new HashMap<>(); //这些不进行统计 initParameters.put("exclusions","*.js,*.css,/druid/*"); bean.setInitParameters(initParameters); return bean; }}

启动后访问localhost:8080/druid ,账号密码为配置中设置的值,登陆后进入监控界面。

整合MyBatis

依赖mybatis-spring-boot-starter

application.properties

spring.datasource.username=rootspring.datasource.password=123456spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&chacterEncoding=utf-8spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#整合mybatismybatis.type-aliases-package=com.zr.pojomybatis.mapper-locations=classpath:mybatis/mapper/*.xml

User

package com.zr.pojo;@Data@AllArgsConstructor@NoArgsConstructorpublic class User {    private Integer id;    private String name;    private String pwd;}

UserMapper

package com.zr.mapper;//这个注解表示了这是一个mybatis的mapper类@Mapper@Repositorypublic interface UserMapper {    List
queryUserList(); User queryById(int id); int addUser(User user); int updateUser(User user); int deleteUser(int id);}

resource/mapper/UserMapper.xml

insert into user(id,name,pwd) values (#{id},#{name},#{pwd});
update user set name = #{name},pwd=#{pwd} where id=#{id};
delete from user where id=#{id};

UserController

package com.zr.controller;@RestControllerpublic class UserController {    @Autowired    private UserMapper userMapper;    @GetMapping("/queryUserList")    public List
queryUserList(){ List
userList = userMapper.queryUserList(); return userList; } @GetMapping("/queryById/{id}") public User queryById(@PathVariable("id") int id){ User user = userMapper.queryById(id); System.out.println(user); return user; } @GetMapping("/addUser") public String addUser(){ userMapper.addUser(new User(7,"韩信","33333")); return "addUser OK"; } @GetMapping("/updateUser") public String updateUser(){ userMapper.updateUser(new User(7,"韩信","54321")); return "updateUser OK"; } @GetMapping("/deleteUser") public String deleteUser(){ userMapper.deleteUser(7); return "deleteUser OK"; }}

以上仅为测试mybatis,没有编写业务层。

SpringSecurity

SpringSecurity是针对spring项目的安全框架,也是 SpringBoot 底层安全模块默认的技术选型,它可以实现强大的web安全控制,对于安全控制,我们只需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理。

记住以下几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

SpringSecurity的两个主要目标是“认证” 和 “授权”(访问控制)

“认证” Authentication

“授权” Authorization

这两个概念是统用的,而不只是SpringSecurity中存在。

项目结构

application.properties

spring.thymeleaf.cache=false

login.html

    
登录

登录

记住我
注册
blog.kuangstudy.com

Spring Security Study by 秦疆

index.html

    
首页

RouterController

package com.zr.controller;@Controllerpublic 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;    }}

SecurityConfig

package com.zr.config;@EnableWebSecuritypublic 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");        //没有权限默认会到登录页 开启登录页面        //usernameParameter("username").passwordParameter("password")        // 括号内为前端传入的用户名为username,底层源码默认为username,当前端为name,pwd时就需要更改括号内问name,pwd        //loginProcessingUrl("/login");//点登录跳转的路径        http.formLogin().loginPage("/toLogin").usernameParameter("username").passwordParameter("password").loginProcessingUrl("/login");        http.csrf().disable();//防止跨站脚本请求攻击关闭  登出失败可能的原因        //开启了注销功能        http.logout().logoutSuccessUrl("/index");        //开启记住我功能,cookie 默认保存两周,自定义接收前端的参数        //开启记住我功能 默认保存两周        http.rememberMe().rememberMeParameter("remember");    }    //认证    //密码编码:PasswordEncoder    //在Spring Security 5.0+中,新增了很多的加密方式    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        //正常应该从数据库中读  这里是从内存中读取        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())                .withUser("zr").password(new BCryptPasswordEncoder().encode("12345")).roles("vip2","vip3")                .and()                .withUser("zz").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1","vip2")                .and()                .withUser("root").password(new BCryptPasswordEncoder().encode("12345")).roles("vip1","vip2","vip3");    }}

Shiro

Apache Shiro 是一个Java的安全(权限)框架。

Shiro 可以非常容易地开发出足够好的应用,其不仅可以用在JavaSE环境,还可以用在JavaEE环境。

Shiro可以完成认证,授权,加密,会话管理,Web集成,缓存等。

重要对象:

Subject:应用代码直接交互的对象是subject,也就是说shiro的对外核心API就是subject,subject代表了当前的用户,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是subject,如网络爬虫,机器人等,与subject的所有交互都会委托给SecurityManager,subject是一个门面,SubjectManager才是具体的执行者。

SecurityManager:安全管理器,即所有与安全有关的操作都会与SecurityManager交互,并且它管理着所有的subject,可以看出它是shiro的核心,它负责与shiro的其它组件交互,相当于SprinfMvc的DispatcherServlet的角色。

Realm:shiro从realm获得安全数据(如用户,角色,权限),就是说SecurityManager要验证用户身份,那么它需要从realm获得相应的用户进行比较,来确定用户的身份是否合法,也需要从realm得到用户相应的角色,权限来验证用户的操作是否能够进行,可以把realm当成DataSource。

整合shiro练习

目录结构

数据库为整合JDBC中的数据库增加一个perms字段

依赖pom.xml

org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-thymeleaf
org.apache.shiro
shiro-spring
1.4.1
mysql
mysql-connector-java
log4j
log4j
1.2.17
com.alibaba
druid
1.1.21
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.1
org.projectlombok
lombok
1.18.16
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0

application.yml

spring:  datasource:    username: root    password: 123456    #时区配置报错 增加时区配置serverTimezone=UTC    url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&chacterEncoding=utf-8&serverTimezone=UTC    driver-class-name: com.mysql.cj.jdbc.Driver    type: com.alibaba.druid.pool.DruidDataSource    #SpringBoot默认不注入这些属性的,需要自己绑定    #durid配置    initialSize: 5    minIdel: 5    maxActive: 20    maxWait: 60000    timeBetweenEvictionRunsMillis: 60000    minEvictableIdleTimeMillis: 300000    validationQuery: SELECT 1 FROM DUAL    testWhileIdle: true    testOnBorrow: false    testOnReturn: false    poolPreparedStatements: true    #配置监控拦截,stat 监控统计, wall 防御sql注入,log4j 日志    #如果允许时报错,导入log4j依赖    filters: stat,wall,log4j    maxPoolPreparedStatementPerConnectionSize: 20    useGlobalDataSourceStat: true    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

application.properties

mybatis.type-aliases-package=com.zr.pojomybatis.mapper-locations=classpath:mapper/*.xml

index.html

    
Title

首页


add

login.html

登录

用户名:

密码:

add.html

add

update.html

update

User实体类

package com.zr.pojo;@Data@AllArgsConstructor@NoArgsConstructorpublic class User {    private int id;    private String name;    private String pwd;    private String perms;}

UserMapper

package com.zr.mapper;@Repository@Mapperpublic interface UserMapper {    public User queryUserByName(String name);}

UserMapper.xml

UserService

package com.zr.service;import com.zr.pojo.User;public interface UserService {    public User queryUserByName(String name);}

UserServiceImpl

package com.zr.service;@Servicepublic class UserServiceImpl implements UserService{    @Autowired    UserMapper userMapper;    @Override    public User queryUserByName(String name) {        return userMapper.queryUserByName(name);    }}

MyController

package com.zr.controller;@Controllerpublic class MyController {    @RequestMapping({"/","/index"})    public String toIndex(Model model){        model.addAttribute("msg","hello shiro");        return "index";    }    @RequestMapping("/user/add")    public String add(){        return "user/add";    }    @RequestMapping("/user/update")    public String update(){        return "user/update";    }    @RequestMapping("/toLogin")    public String toLogin(){        return "login";    }    @RequestMapping("/login")    public String login(String username,String password,Model model){        //获取当前的用户        Subject subject = SecurityUtils.getSubject();        //封装用户的登录数据        UsernamePasswordToken token = new UsernamePasswordToken(username, password);        try{            subject.login(token); //执行登录的方法,如果没有一次就ok            return "index";        }catch (UnknownAccountException a){ //用户名不存在            model.addAttribute("msg","用户名错误!");            return "login";        }catch (IncorrectCredentialsException a){ //密码不存在            model.addAttribute("msg","密码错误!");            return "login";        }    }    @RequestMapping("/noauth")    @ResponseBody    public String unAuthorized(){        return "未授权不能访问此页面!";    }}

UserRealm

package com.zr.config;//自定义的UserRealmpublic class UserRealm extends AuthorizingRealm {    @Autowired    UserService userService;    //授权    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {        System.out.println("执行了==>>授权:AuthorizationInfo");        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();        //info.addStringPermission("user:add");        //拿到当前登录的对象        Subject subject = SecurityUtils.getSubject();        User currentUser = (User)subject.getPrincipal(); //拿到user对象        //设置当前用户的权限        info.addStringPermission(currentUser.getPerms());        return info;    }    //认证    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        System.out.println("执行了==>>认证:AuthenticationInfo");        //用户名密码 数据库中取        UsernamePasswordToken userToken = (UsernamePasswordToken) token;        User user = userService.queryUserByName(userToken.getUsername());        if (user==null){            return null;        }        Subject currentSubject = SecurityUtils.getSubject();        Session session = currentSubject.getSession();        session.setAttribute("loginUser",user);        //密码认证 shiro默认做        return new SimpleAuthenticationInfo(user,user.getPwd(),"");    }}

ShiroConfig

package com.zr.config;@Configurationpublic class ShiroConfig {    //ShiroFilterFactoryBean    @Bean    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();        //设置安全管理器        bean.setSecurityManager(defaultWebSecurityManager);        //添加shiro的内置过滤器        /*            anon:无需认证就可以访问            authc:必须认证            user:必须拥有记住我功能才能用            perms:拥有某个资源的权限才能访问            role:拥有某个角色权限才能访问         */        Map
filterMap = new LinkedHashMap<>(); //授权 没有授权会跳转到未授权界面(401) filterMap.put("/user/add","perms[user:add]"); filterMap.put("/user/update","perms[user:update]"); filterMap.put("/user/*","authc"); bean.setFilterChainDefinitionMap(filterMap); //返回登录页 bean.setLoginUrl("/toLogin"); //未授权页面 bean.setUnauthorizedUrl("/noauth"); return bean; } //DefaultWebSecurityManager @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; } //创建realm对象,自定义 @Bean public UserRealm userRealm(){ return new UserRealm(); } //整合shiroDialect 用来整合shiro和thymeleaf @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }}

Swagger

学习目标:

  • 了解Swagger的概念和作用
  • 巩固前后端分离
  • 在SpringBoot中巩固Swagger

Swagger简介

  • 号称最流行的Api框架
  • RestFul Api 文档在线自动生成工具=>Api文档与Api定义同步更新
  • 直接运行,可以在线测试Api接口
  • 支持多种语言

在项目中使用swagger需要导入jar包

  • swagger2
  • ui

SpringBoot集成Swagger

  1. 新建一个SpringBoot-web项目

  2. 导入相关依赖

    io.springfox
    springfox-swagger2
    2.9.2
    io.springfox
    springfox-swagger-ui
    2.9.2
  3. 编写helloword工程

  4. 配置swagger===>config

    package com.zr.config;@Configuration@EnableSwagger2  //开启swagger2public class SwaggerConfig {}
  5. 访问http://localhost:8080/swagger-ui.html

配置Swagger

Swagger的Bean实例 Docket

package com.zr.config;import java.util.ArrayList;@Configuration@EnableSwagger2  //开启swagger2public class SwaggerConfig {    //配置swagger的Docket的 Bean实例    @Bean    public Docket docket(){        return new Docket(DocumentationType.SWAGGER_2)                .apiInfo(apiInfo());    }    //配置swagger文档的信息    private ApiInfo apiInfo(){        //作者信息        Contact contact = new Contact("周周", "http://www.cnblogs.com/zhou-zr", "813794474@qq.com");        return new ApiInfo("周周的SwaggerApi文档",                "coding",                "1.0",                "http://www.cnblogs.com/zhou-zr",                contact,                "Apache 2.0",                "http://www.apache.org/licenses/LICENSE-2.0",                new ArrayList()        );    }}

Swagger配置扫描接口

Docket select()

在配置Swagger中修改

//配置swagger的Docket的 Bean实例//enable 是否启动swagger 如果为false 不能再浏览器中访问swagger@Beanpublic Docket docket(){    return new Docket(DocumentationType.SWAGGER_2)            .apiInfo(apiInfo())	        .enable(false)            .select()            //配置要扫描接口的方式            //basePackage 指定扫描的包            //any() 扫描全部            //none() 都不扫描            //withClassAnnotation() 扫描类上的注解 参数是一个注解的反射对象            //withMethodAnnotation() 扫描方法上的注解            .apis(RequestHandlerSelectors.basePackage("com.zr.controller"))            //过滤什么路径            .paths(PathSelectors.ant("/zr/**"))            .build();}

根据是否是生产环境来开启swagger

//配置swagger的Docket的 Bean实例     //enable 是否启动swagger 如果为false 不能再浏览器中访问swagger     @Bean     public Docket docket(Environment environment){         //设置要显示的swagger环境         Profiles profiles = Profiles.of("dev");         //通过environment.acceptsProfiles判断是否处在自己设定的环境当中         boolean flag = environment.acceptsProfiles(profiles);         return new Docket(DocumentationType.SWAGGER_2)                 .apiInfo(apiInfo())                 .enable(flag)                 .select()                 .apis(RequestHandlerSelectors.basePackage("com.zr.controller"))                 //过滤什么路径                 //.paths(PathSelectors.ant("/zr/**"))             .build(); }

application.properties

spring.profiles.active=dev

application-dev.properties

server.port=8081

application-pro.properties

server.port=8082

配置Api文档分组

.groupName("zzr")

如何配置多个分组:配置多个Docket实例即可。

@Beanpublic Docket docket2(){    return new Docket(DocumentationType.SWAGGER_2).groupName("A");}@Beanpublic Docket docket3(){    return new Docket(DocumentationType.SWAGGER_2).groupName("B");}@Beanpublic Docket docket4(){    return new Docket(DocumentationType.SWAGGER_2).groupName("C");}

接口测试

实体类配置

package com.zr.pojo;// @Api(注释)@ApiModel("用户实体类")public class User {    @ApiModelProperty("用户名")    public String username;    @ApiModelProperty("密码")    public String password;}

HelloController

package com.zr.controller;@RestControllerpublic class HelloController {    @GetMapping("/hello")    public String hello(){        return "hello";    }    //只有我们的接口中,返回值中存在实体类,就会被扫描到swagger中    @PostMapping("/user")    public User user(){        return new User();    }    @ApiOperation("Hello控制类")    @GetMapping("/hello2")    public String hello(@ApiParam("用户名") String username){        return "hello"+username;    }    @ApiOperation("post测试")    @PostMapping("/postt")    public User postt(@ApiParam("用户名")User user){        return user;    }}

总结:

  1. 我们以通过Swagger给一些比较难理解的属性或者接口,增加注释信息
  2. 接口文档实时更新
  3. 可以在线测试

注意点:在正式上线的时候,关闭Swagger。

任务

异步任务

AsyncService

package com.zr.service;@Servicepublic class AsyncService {    //告诉spring这是一个异步的方法    @Async    public void hello(){        try {            Thread.sleep(3000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("数据正在处理");    }}

AsyncController

package com.zr.controller;@RestControllerpublic class AsyncController {    @Autowired    AsyncService asyncService;    @RequestMapping("/hello")    public String hello(){        asyncService.hello();  //停止3秒        return "ok";    }}

主程序开启异步注解的功能

package com.zr;//开启异步注解的功能@EnableAsync@SpringBootApplicationpublic class Springboot09TestApplication {    public static void main(String[] args) {        SpringApplication.run(Springboot09TestApplication.class, args);    }}

定时任务

TaskScheduler  任务调度者TaskExecutor  任务执行者@EnableScheduling //开启定时功能的注解@Scheduled  //什么时候执行Cron 表达式

主程序开启定时任务的支持

package com.zr;@EnableScheduling //开启定时功能的注解@SpringBootApplicationpublic class Springboot09TestApplication {    public static void main(String[] args) {        SpringApplication.run(Springboot09TestApplication.class, args);    }}

定时任务设置

package com.zr.service;@Servicepublic class ScheduledService {    //在一个特定的时间执行    /*        0 30 10 * * ?   每天的10.30 执行一次        0 0/5 10,16 * * ?   每天的10点到16点 每隔五分钟执行一次        0 20 10 ? * 1-6    每个月的周一到周六 10.20执行一次     */    //cron表达式  秒 分 时 日 月 星期几(0-7  ?每一天)    @Scheduled(cron = "0 * * * * ?")    public void hello(){        System.out.println("hello,你被执行了");    }}

邮件任务

package com.zr;@SpringBootTestclass Springboot09TestApplicationTests {    @Autowired    JavaMailSenderImpl mailSender;    @Test    void contextLoads() {        //一个简单的邮件        SimpleMailMessage mailMessage = new SimpleMailMessage();        mailMessage.setSubject("小周,你好!");        mailMessage.setText("好好学习!");        mailMessage.setTo("813794474@qq.com");        mailMessage.setFrom("813794474@qq.com");        mailSender.send(mailMessage);    }    @Test    void contextLoads2() throws MessagingException {        //一个复杂的邮件        // MimeMessage 也可以直接new创建        MimeMessage mimeMessage = mailSender.createMimeMessage();        //组装        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);        helper.setSubject("周周,你好呀!");        helper.setText("

好好学习!

",true); //附件 helper.addAttachment("winC.PNG",new File("C:\\Users\\zr\\Pictures\\截图\\winC.PNG")); helper.addAttachment("2.jpg",new File("C:\\Users\\zr\\Pictures\\截图\\winC.PNG")); helper.setTo("813794474@qq.com"); helper.setFrom("813794474@qq.com"); mailSender.send(mimeMessage); }}

分布式

分布式系统理论

什么是分布式系统:

在《分布式系统原理与范型》 一书中有如下定义:分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统。

分布式系统是有一组通过网络进行通信,为了完成共同的任务而协调工作的计算机节点组成的系统,分布式系统的出现是为了用廉价的,普通的机器完成单个计算机无法完成的计算,存储任务。其目的是利用更多的机器,处理更多的数据。

只有当单个节点无法满足日益增长的计算,存储任务需求时,且硬件的提升高昂到得不偿失时,且程序也不能进一步优化的时候,我们才需要考虑分布式系统。

单一应用架构:当网站流量很小时,只需一个应用,将所有的功能部署在一起,以减少部署节点和成本,此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

适用于小型网站,小型管理系统,将所有的应用部署到一个应用中,简单易用。

缺点:性能扩展比较难,协同开发问题,不利于升级维护。

垂直应用架构:当数据量逐渐增大,单一应用增加机器带来的加速度越来越小,将一个应用拆分成互不相干的几个应用,以提升效率,此时,用于加速前端页面开发的Web框架(MVC)是关键。

通过切分业务来实现各个模块独立部署,降低了维护和部署的难度,团队各司其职,性能更易扩展。

缺点:公用模块无法重复利用,开发性的浪费。

分布式服务架构:当垂直应用越来越多,应用之间交互不可避免。将核心应用抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

流动计算架构:当服务越来越多,容量的评估,小服务资源的浪费问题等问题逐渐显现,此时需要增加一个调度中心基于访问的压力实时管理集群的容量,提高集群利用率。此时。用于提高资源利用率的资源调度和治理中心(SOA)是关键。

RPC

什么是RPC:

RPC【Remote Procedure Call】是指远程过程调用,是一种进程间的通信方式,它是一种技术的思想,而不是规范,它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不是程序员显示编码这个远程调用的细节,即程序员无论是调用本地的还是远程的函数,本质上编写的调用代码基本相同。

RPC两个核心:通信和序列化。

序列化:数据传输需要转换。

分布式 Dubbo + Zookeeper + SpringBoot

Apache Dubbo是一款高性能,轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。

服务提供者(Provider):暴漏服务的服务提供方,服务提供者在启动时,向注册中心注册自己提供的 。

服务消费者(Consumer):调用远程服务的服务消费方,服务消费者在启动时,向注册中心订阅自己所需的服务,服务消费者从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

注册中心(Registry):注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

监控中心(Monitor):服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

容器(Container):服务运行容器。

调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。

  2. 服务提供者在启动时,向注册中心注册自己提供的服务。

  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。

  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。

  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。

  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

window下安装zookeeper

官网下载zookeeper,解压至指定目录,管理员模式下进入到zookeeper的bin目录下,启动zkServer.cmd。

【注意报错】在zkServer.cmd源文件中加pause,可查看报错信息。

解决:进入conf目录,将zoo_sample.cfg复制一份将名字改为zoo.cfg。

启动zkServer.cmd , 启动zkCli.cmd

window下安装Dubbo

dubbo本身并不是一个服务软件,它其实就是一个jar包,把Java程序连接到zookeeper,并利用zookeeper消费,提供服务。

为了让用户更好的监控众多的dubbo服务,官方提供了一个可视化的监控程序dubbo-admin,不安装也不影响使用。

下载dubbo-admin

cmd进入下载的目录下,执行mvn clean package -Dmaven.test.skip=true

打包成功

执行dubbo-admin\target下的dubbo-admin-0.0.1-SNAPSHOT.jar

命令:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar 【注意 zookeeper的服务一定要打开】

执行完毕后访问 默认账号密码是root,root。

登录成功界面

zookeeper:注册中心

dubbo-admin是一个监控管理后台,可以查看我们注册了哪些服务,哪些服务被消费了。

Dubbo:jar包

步骤:前提开启zookeeper

  1. 提供者提供服务
    1. 导入依赖
    2. 配置注册中心的地址,以及服务发现名,和要扫描的包
    3. 在想要被注册的服务上增加一个注解@Service (Dubbo的)
  2. 消费者如何消费
    1. 导入依赖
    2. 配置注册中心的地址,配置自己的服务名
    3. 从远程注入服务@Reference

项目结构:

provider-server

TickerService

package com.zr.service;public interface TickerService {    public String getTicker();}

TickerServiceImpl

package com.zr.service;//服务注册与发现@Service  //可以被扫描到  项目一启动就自动注册到注册中心@Component  //使用dubbo后尽量不要使用servicepublic class TickerServiceImpl implements TickerService{    @Override    public String getTicker() {        return "zzr";    }}

application.properties

server.port=8001#服务应用的名字dubbo.application.name=provider-server#注册中心地址dubbo.registry.address=zookeeper://127.0.0.1:2181#哪些服务要被注册dubbo.scan.base-packages=com.zr.service

pom.xml(consumer-server的pom相同)

org.apache.dubbo
dubbo-spring-boot-starter
2.7.8
com.github.sgroschupf
zkclient
0.1
org.apache.curator
curator-framework
2.12.0
org.apache.curator
curator-recipes
2.12.0
org.apache.zookeeper
zookeeper
3.4.14
org.slf4j
slf4j-log4j12

consumer-server

TickerService

package com.zr.service;public interface TickerService {    public String getTicker();}

UserService

package com.zr.service;@Service  //放到容器中public class UserService {    //想拿到票provider-server提供的票  去注册中心拿    @Reference //引用 pom坐标 定义路径相同的接口名    TickerService tickerService;    public void byTicket(){        String ticker = tickerService.getTicker();        System.out.println("在注册中心拿到了一张票"+ticker);    }}

application.properties

server.port=8002#消费者从那里去拿需要暴漏自己的名字dubbo.application.name=consumer-server#注册中心的地址dubbo.registry.address=zookeeper://127.0.0.1:2181

测试

package com.zr;@SpringBootTestclass ConsumerServerApplicationTests {    @Autowired    UserService userService;    @Test    void contextLoads() {        userService.byTicket();    }}

结果

dubbo-admin中查看服务提供者

上一篇:SpringCloud学习笔记
下一篇:SpringMVC学习笔记

发表评论

最新留言

留言是一种美德,欢迎回访!
[***.207.175.100]2025年04月18日 08时48分27秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

2024年度“金智奖”揭晓:绿盟科技获双项大奖,创新驱动网络安全新高度。从零基础到精通,收藏这篇就够了! 2023-01-24
2024年最流行的十大开源渗透测试工具 2023-01-24
005从零开始学Python—字符串处理 2023-01-24
2024年网络安全八大前沿趋势,零基础入门到精通,收藏这篇就够了 2023-01-24
2024年薪酬最高的五个网络安全职位,零基础入门到精通,收藏这一篇就够 2023-01-24
2024年非科班的人合适转行做程序员吗? 2023-01-24
2024数字安全创新性案例报告,从零基础到精通,收藏这篇就够了! 2023-01-24
2024最新最全CTF入门指南(非常详细)零基础入门到精通,收藏这一篇就够了 2023-01-24
2024最新科普什么是大模型?零基础入门到精通,收藏这篇就够了 2023-01-24
2024最新程序员接活儿搞钱平台盘点 2023-01-24
2024最火专业解读:信息安全(非常详细)零基础入门到精通,收藏这一篇就够了 2023-01-24
(插播)unity的 异常捕捉和 ios Android 崩溃信息的捕捉。 2023-01-24
2024版最新SRC漏洞挖掘思路手法(非常详细),零基础入门到精通,收藏这一篇就够了 2023-01-24
2024版最新渗透测试零基础入门教程,带你入门到精通(超详细),收藏这篇就够了 2023-01-24
2024版最新网络安全入门必备读书清单(非常详细)零基础入门到精通,收藏这一篇就够了 2023-01-24
2024版最新网络安全教程从入门到精通,看完这一篇就够了 2023-01-24
0/1背包问题——从LeetCode题海中总结常见套路 2023-01-24
(原创)面向对象的系统对接接口编写。第5篇(完结) 2023-01-24
2024网络安全岗就业前景如何?零基础入门到精通,收藏这篇就够了 2023-01-24
2024零基础如何入门网络安全? 2023-01-24