springBoot

  1. 配置如何编写 yaml
  2. 自动装配原理
  3. 集成web开发:业务的核心
  4. 集成数据库:Druid
  5. 分布式开发:Dubbo+zookeeper
  6. swagger:接口文档
  7. 任务调度
  8. SpringSecunity Shiro

1、微服务

微服务架构打破之前的all in one 的架构方式,把每个功能元素对出来。

好处:

  1. 节省 了调用资源
  2. 每个功能元素的服务都是一个可替代、可独立升级的软件代码

1.1 HelloWorld

可以从Spring Initializr上下载一个项目,耶可以从IDEA中创建springboot项目!

要在Application的同级目录下建包,不然不会生效!

在pom.xml里有一个web依赖:用来启动tomcat

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

springboot所有的依赖都是以spring-boot-starter开头!

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.1</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.liu</groupId><artifactId>helloWorld</artifactId><version>0.0.1-SNAPSHOT</version><name>helloWorld</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
  • 如上所示:主要分为四部分
  • 项目元数据信息:maven项目的基本元素:gav,name,description等
  • parent:继承spring-boot-starter-parent的依赖管理、控制版本和打包等
  • dependencies:项目具体依赖,这里包含了web依赖用于实现http接口(包含了springmvc)
  • build:构建配置部分:默认使用spring-boot-maven-plugin,配合spring-boot-starter-parent就可以把springboot应用打包成jar来运行。

1.2 打包成jar包

在maven的lifecycle里有一个package可以打包成jar包!

在target项目下可以得到生成的jar包!

1.3 IDEA创建springboot项目

第一步:新建项目

第二步:选择spring-web项目

1.4 更改项目的端口号

1.5 更改banner

在resources目录下新建banner.txt文件

放入你要生成的banner图示:

2、原理

2.1、自动配置

pom.xml

  • spring-boot-dependencies:核心依赖在父工程中

  • 依赖不需要配置版本号是因为有版本仓库

启动器

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>
  • 启动器:就是springBoot的启动场景

  • 要使用什么功能就找到对应的启动器就可以了start

主程序

@SpringBootApplication : 标注这个类是一个springboot的应用@SpringBootConfiguration:springboot的配置类,启动类下的所有资源被导入@Configuration:配置@Component:组件
@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 = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");return configurations;
}
META-INF/spring.factories:自动配置的核心文件

2.2 底层原理:

getAutoConfigurationEntry:获取自动配置的实体
getCandidateConfigurations:获取候选的配置protected Class<?> getAnnotationClass() {return EnableAutoConfiguration.class;}
List<String> getCandidateConfigurations 所有的加载的配置
loadSpringFactories:项目资源:"META-INF/spring.factories"系统资源从这些资源中遍历所有的element并封装成properties供我们使用

2.3 结论:

  • springboot所有的自动配置都在启动类中被扫描并加载:spring.factories所有的自动配置类都在这里,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,自动装配就生效,然后配置成功!
  1. springboot在启动的时候,从类路径下META-INF/spring.factories获取指定的值
  2. 将这些自动配置的类导入容器,自动配置就会生效,进行自动配置
  3. 整合javaEE,解决方案和自动配置的东西都在spring-boot-autoconfigure-2.2.0.RELEASE.jar包下
  4. 它会把所有需要导入的组件以类名的方式返回,这些组件就会被添加到容器中
  5. 容器中也会存在非常多的xxxAutoConfiguration的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件并自动配置

2.4 Run

@SpringBootApplication
public class Springboot01HelloworldApplication {//该方法返回一个configurableApplicationContext对象//参数一:应用入口的类   参数类:命令行传参public static void main(String[] args) {SpringApplication.run(Springboot01HelloworldApplication.class, args);}}

2.5 springApplication

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

  1. 推断应用的类型是普通的项目还是web项目
  2. 查找并加载所有可用初始化器,设置到initializers属性中
  3. 找出所有的应用程序监听器,设置到listeners属性中,获取上下文,处理bean
  4. 推断并设置main方法的定义类,找到运行的主类

查看构造器

    this.sources = new LinkedHashSet();this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.addConversionService = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = Collections.emptySet();this.isCustomEnvironment = false;this.lazyInitialization = false;this.applicationContextFactory = ApplicationContextFactory.DEFAULT;this.applicationStartup = ApplicationStartup.DEFAULT;this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = this.deduceMainApplicationClass();

3、springboot配置

3.1 配置文件

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

  • application.properties

    • 语法结构:key = value
  • application.yml
    • 语法结构:key: 空格 value
  • 配置文件的作用:修改springboot自动配置的默认值,因为springboot在底层给我们自动配置好了

3.2 yaml

1.格式

server:port: 8080

2.标记语言

  • 以前的配置文件大多数使用xml来配置,比如一个简单的端口配置
  • xml配置:
<server><port>8081</port>
</server>
# 普通的key-value
name: liuxiang# 对象
student:name: liuxiangage: 3#行内写法
student1: {name: liuxiang,age: 3}#数组
pets:- cat- dog- pigpets1: [cat,dog,pig]

3.3 给实体类赋值

1.原生的赋值:需要给每一个属性赋值,麻烦

  @Value("旺财")private String name;@Value("3")private  Integer age;

2.用yaml赋值

person:name: liuxiang${random.uuid}age: ${random.int}happy: truebirth: 1997/12/12maps: {k1: v1,k2: v2}list:- code- music- boydog:name: ${person.hello:hello}_旺财 #前面值如果不存在就选择后面的值age: 3

springboot测试:

package com.liu;import com.liu.pojo.Dog;
import com.liu.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class Springboot02ConfigApplicationTests {@Autowiredprivate Person person;@Testvoid contextLoads() {System.out.println(person);}
}

结果:注入成功!

Person{name='liuxiang', age=18, happy=true, birth=Fri Dec 12 00:00:00 CST 1997, maps={k1=v1, k2=v2}, list=[code, music, boy], dog=Dog{name='旺财', age=3}}

实体类:

package com.liu.pojo;import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.List;
import java.util.Map;@Component
@ConfigurationProperties(prefix = "person")
/*
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值映射到这个组件中
告诉springboot将本类中的所有属性和配置文件中相关的配置进行绑定
参数prefix = "person":将配置文件中的person下面的所有属性一一对应只有这个组件是容器中的组件才能用@ConfigurationProperties
*/
public class Person {private String name;private Integer age;private Boolean happy;private Date birth;private Map<String,Object> maps;private List<Object> list;private Dog dog;public Person() {}public Person(String name, Integer age, Boolean happy, Date birth, Map<String, Object> maps, List<Object> list, Dog dog) {this.name = name;this.age = age;this.happy = happy;this.birth = birth;this.maps = maps;this.list = list;this.dog = dog;}
  • 松散绑定:比如yaml中写的last-name,和lastName是一样的,-后面跟着的字母默认是大写的
  • JSR303数据校验,可以在字段增加一层过滤器验证,可以保证数据的合法性
  • 复杂类型封装,yml中可以封装对象,使用@value就不支持

3.4 JSR303校验

@Validated //数据校验

参考配置:

@Null 限制只能为null
@NotNull 限制必须不为null
@AssertFalse 限制必须为false
@AssertTrue 限制必须为true
@DecimalMax(value) 限制必须为一个不大于指定值的数字
@DecimalMin(value) 限制必须为一个不小于指定值的数字
@Digits(integer,fraction) 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future 限制必须是一个将来的日期
@Max(value) 限制必须为一个不大于指定值的数字
@Min(value) 限制必须为一个不小于指定值的数字
@Past 限制必须是一个过去的日期
@Pattern(value) 限制必须符合指定的正则表达式
@Size(max,min) 限制字符长度必须在min到max之间
@Past 验证注解的元素值(日期类型)比当前时间早
@NotEmpty 验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank 验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

4、多环境配置及配置文件地址

配置文件存放地址:

  1. file:./config/ : 项目目录下的config下的优先级最高
  2. file:./ :优先级其次
  3. classpath:/config/:优先级第三
  4. classpath:/:优先级最低

application.properties文件:

# springboot的多环境配置:可以选择激活哪一个配置文件
spring.profiles.active=dev

application-dev.properties 线下环境:

server.port=8082

application-test.properties 测试环境:

server.port=8081

yaml配置实现:(推荐)

server:port: 8081
spring:profiles:active: dev
---
server:port: 8082
spring:profiles:dev---
server:port: 8083
spring:profiles:dev

5、自动配置再理解

一定要满足条件才会生效,导入相关依赖,找到对应的start启动器即可!

配置文件yaml和spring.factories的关系:

  • 都是通过ConfigurationProperties(prefix = “xxx”)来实现
  • xxxAutoConfiguration:有默认值,都有一个xxxProperties的文件和配置文件绑定,就可以使用自定义的配置了
  • 这样配置文件就可以动态的修改spring的内容

5.1 自动装配的原理

  1. springboot启动会加载大量的自动配置类
  2. 看我们需要的功能有没有再springboot默认写好的自动配置类当中
  3. 再看这个自动配置类中到底配置了哪些组件(要用的在其中就不需要再配置了)
  4. 给容器中自动配置类添加组件时,会从properties类中获取某些属性,我们只需要在配置文件中指定这些属性的值即可
  5. xxxAutoConfiguration:自动配置类;给容器中添加组件
  6. xxxProperties:封装配置文件中相关属性

可以在yaml文件下通过debug=true来查看,哪些自动配置类生效了,一部分生效,一部分不生效,选择最好的

debug: true

6、springBoot Web开发

要解决的问题:

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

6.1、静态资源导入

这四个文件c夹下的所有的资源都可以被访问到,例如:

优先级:

resources>static>public

  • 在springboot还可以通过webjars访问:localhost:8080/webjars/

6.2 thymeleaf模板引擎

  • jsp就是一个模板引擎

  • 前端页面变量报红添加注释

  • <!--suppress ThymeleafVariablesResolveInspection -->
    

引入thymeleaf:

  1. Thymeleaf

  2. GitHub - thymeleaf/thymeleaf: Thymeleaf is a modern server-side Java template engine for both web and standalone environments.

  3. Spring Boot Reference Guide spring导入start文档

依赖jar包:

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

结论:

需要使用thymeleaf,只需要将html放在templates下,在controller下设置跳转页面路径即可

  • 用thymeleaf需要导入头文件约束 xmlns:th=“http://www.thymeleaf.org”

  • 超连接,文本等等需要放在th下

  • 常用命名空间:

  • <xmlns:th="http://www.thymeleaf.org">
    <xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
    <xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
    
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<!--用thymeleaf需要导入头文件约束 xmlns:th="http://www.thymeleaf.org"-->
<!--所有的html元素都可以被thymeleaf替代,用th:元素名-->
<div th:text="${msg}"></div>
<div th:utext="${msg}"></div> <!--转义--><hr>
<!--遍历数组-->
<h3 th:each="user:${users}" th:text="${user}"></h3></body>
</html>

变量表达式:

  • 变量:${}:与EL表达式一样

  • 消息:#{}

  • URL:@{}

  • Fragment:~{}:片段表达式

  • 文本:‘’

  • 数字:1,2

  • 布尔值:true

  • Null:null

文本操作:

  • string:+

数学运算:

  • +,-,*,/,%

其他操作:

  • 等于操作:==,!=

  • 比较:>,<,<=,>=

  • 三元运算符:(if) ? (then) ? (else)

6.3、装配扩展springMVC

29. Developing Web Applications (spring.io)

If you want to keep Spring Boot MVC features and you want to add additional MVC configuration (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc. If you wish to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, you can declare a WebMvcRegistrationsAdapter instance to provide such components.

视图解析器的源码:

  • 在springboot2.7.1版本中,将此部分改成了判断是否有下一个视图,有就添加
  • 采用了迭代器而不是之前的全部遍历
  • 获取候选的,再选择最好的视图
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered, InitializingBean{}
private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception {List<View> candidateViews = new ArrayList();
if (this.viewResolvers != null) {Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set");Iterator var5 = this.viewResolvers.iterator();while(var5.hasNext()) {ViewResolver viewResolver = (ViewResolver)var5.next();View view = viewResolver.resolveViewName(viewName, locale);if (view != null) {candidateViews.add(view);}Iterator var8 = requestedMediaTypes.iterator();

自定义视图解析器:继承视图解析器的接口并注册到bean中会自动装配

package com.liu.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.Locale;//全面扩展mvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {//ViewResolver 实现了视图解析器接口的类,可以把它看作视图解析器@Beanpublic ViewResolver myViewResolver(){return new MyViewResolver();}//自定义一个自己的视图解析器MyViewResolverpublic static class MyViewResolver implements ViewResolver{@Overridepublic View resolveViewName(String viewName, Locale locale) throws Exception {return null;}}
}

格式Formatter

public String getDateFormat() {return this.format.getDate();}

可以再yml文件中自己配置日期格式!

  • springboot再自动配置很多组件的时候,先看容器中有没有用户自己配置的(如果有用户自己配置的@bean),就用用户的,没有就用自动配置的,比如视图解析器,将用户配置的和自己默认的组合起来作为候选的,再选择最好的。

视图跳转

//扩展springMVC,官方这样操作
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {//视图跳转@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/liu").setViewName("test");}
}

@EnableWebMvc注解:自动配置的时候不能加这个注解

@EnableWebMvc //导入了这个类DelegatingWebMvcConfiguration  作用:从容器中获取所有的webmvc config

原因:在WebMvcAutoConfiguration这个类中有三个条件满足才生效,而在EnableWebMvc这个注解当中导入了这个类DelegatingWebMvcConfiguration,不满足自动装配的条件了

@ConditionalOnWebApplication(type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})

总结:

  • 在springboot中,有许多的xxxConfiguration配置类帮助进行扩展配置,看见这个就是改变了原始的配置,要注意扩展了什么功能!

7、员工管理系统

  • 关闭模板引擎的缓存
#关闭模板引擎的缓存
spring.thymeleaf.cache=false

7.1 首页配置

  • 所有的静态资源都需要用thymeleaf接管,链接用@{}

7.2 国际化(中英文切换)

要确保全部是UTF-8格式!

可视化配置:

页面的每个原始都需要这么配置:

在yml文件中配置真实的配置文件地址:

#真实配置文件
spring.messages.basename=i18n.login

国际化消息用:#{}取值

7.2.1 自定义国际化组件

前端的两个跳转链接:

<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>
package com.liu.config;import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;public class MyLocaleResolver implements LocaleResolver {//解析请求@Overridepublic Locale resolveLocale(HttpServletRequest request) {//获取请求中的语言参数 带l都走这个请求String language = request.getParameter("l");Locale locale = Locale.getDefault();//默认的,如果没有就使用默认的//如果请求的链接携带了地区化的参数if (!StringUtils.isEmpty(language)){//zh_CN 分割键值对String[] split = language.split("_");//国家地区locale = new Locale(split[0], split[1]);}return locale;}@Overridepublic void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {}
}

要将这个写的类注册到spring中:@Bean

package com.liu.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index.html").setViewName("index");}//自定义的国际化组件@Beanpublic LocaleResolver localeResolver(){return new MyLocaleResolver();}
}

步骤:

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

7.3 登录功能

index.html

<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
  • 判断条件格式:

  • ${#strings.isEmpty(name)}
    ${#strings.arrayIsEmpty(nameArr)}
    ${# strings.listIsEmpty(nameList)}
    ${# strings.setIsEmpty(nameSet)}
    

后端controller层:

  • 接收前端name属性传过来的值,用@RequestParam保证路径不出错!
  • main.html在myMvcConfig文件中设置了访问到dashboard.html页面!
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index.html").setViewName("index");registry.addViewController("/main.html").setViewName("dashboard");}//自定义的国际化组件@Beanpublic LocaleResolver localeResolver(){return new MyLocaleResolver();}
}
@Controller
public class LoginController {@RequestMapping("/user/login")public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){if (!StringUtils.isEmpty(username) && "123456".equals(password)){//登陆成功,sessionsession.setAttribute("loginUser",username);return "redirect:/main.html";}else {model.addAttribute("msg","用户名或者密码错误!");return "index";}}}

7.4 拦截器

package com.liu.config;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class LoginHandlerInterceptor implements HandlerInterceptor {//return true 放行 反之不放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//登录成功后,获取sessionObject loginUser = request.getSession().getAttribute("loginUser");if (loginUser==null){//没有登录request.setAttribute("msg","没有权限,请先登录");request.getRequestDispatcher("/index.html").forward(request,response);//重定向回去return false;}else {return true;}}}
  • 到spring中注册Bean,也就是带@Configuration注解,继承了WebMvcConfigurer的类
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");registry.addViewController("/index.html").setViewName("index");registry.addViewController("/main.html").setViewName("dashboard");}//自定义的国际化组件@Beanpublic LocaleResolver localeResolver(){return new MyLocaleResolver();}//拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/","user/login");}
}

7.5 前端侧边栏

实现代码的复用:用thymeleaf模板进行片段的插入

将公共部分的代码提取到common文件下:

<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">

dashboard.html:侧边栏

<div th:replace="~{/common/commons::sidebar}"></div>

list.html:在同样的位置进行插入片段!用~{}方式

<div th:replace="~{/common/commons::sidebar}"></div>

dashboard.html:导航栏

<div th:replace="~{/common/commons::topbar}"></div>

list.html:在同样的位置进行插入片段!用~{}方式

<div th:replace="~{/common/commons::topbar}"></div>
  • fragment inclusion的两种方式

    • th:insert
    • th:replace

7.6 提取公共页面

commons.html

<a th:class="${active=='main.html'?'nav-link active':'nav-link'}" th:href="@{/index.html}">
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">

dashboard.html

<div th:replace="~{/common/commons::sidebar(active='main.html')}"></div>

list.html

<div th:replace="~{/common/commons::sidebar(active='list.html')}"></div>

thymeleaf前端变量报红的解决办法:在html页面的头部加上下面这个注释

<!--suppress ThymeleafVariablesResolveInspection -->

7.7 展示数据库数据

<thead>
<tr><th>id</th><th>lastName</th><th>email</th><th>gender</th><th>department</th><th>birth</th><th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="emp:${emps}"><td th:text="${emp.getId()}"></td><td>[[${emp.getLastName()}]]</td><td th:text="${emp.getEmail()}"></td><td th:text="${emp.getGender()==0?'女':'男'}"></td><td th:text="${emp.department.getDepartmentName()}"></td><td th:text="${#dates.format(epm.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td><td><a class="btn btn-sm btn-primary" th:href="@{'/emp/'+${emp.getId()}}">编辑</a><a class="btn btn-sm btn-danger">删除</a></td>
</tr>
</tbody>

日期转换:去参考thymeleaf官方文档

  • 地址:Tutorial: Using Thymeleaf

  • ${#dates.format(date, 'dd/MMM/yyyy HH:mm')}
    ${#dates.arrayFormat(datesArray, 'dd/MMM/yyyy HH:mm')}
    ${#dates.listFormat(datesList, 'dd/MMM/yyyy HH:mm')}
    ${#dates.setFormat(datesSet, 'dd/MMM/yyyy HH:mm')}
    

7.8 添加员工

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

问题报错:

Failed to convert property value of type ‘java.lang.String’ to required type ‘java.util.Date’ for property ‘birth’;

解决:

在pojo实体类的属性上添加注解:

@DateTimeFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

或者在配置文件中修改:

spring.mvc.date-format=yyyy-MM-dd
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{emp}" method="post"><div class="form-group"><label>LastName</label><input type="text" name="lastName" class="form-control" placeholder="name"></div><div class="form-group"><label>Email</label><input type="email" name="email" class="form-control" placeholder="email"></div><div class="form-group"><label>Gender</label><div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="gender" value="1"><label class="form-check-label">男</label></div><div class="form-check form-check-inline"><input class="form-check-input" type="radio" name="gender" value="0"><label class="form-check-label">女</label></div></div><div class="form-group"><label>department</label><select class="form-control" name="department.id"><option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option></select></div><div class="form-group"><label>Birth</label><input type="text" name="birth" class="form-control" placeholder="2022-07-13"></div><button type="submit" class="btn btn-default btn-success">添加</button>
</form>
</main>
@GetMapping("/emp")public String toAddPage(Model model){//查出所有部门的信息Collection<Department> departments = departmentDao.getDepartments();model.addAttribute("departments",departments);return "emp/add";}@PostMapping("/emp")public String addEmp(Employee employee){//添加的操作System.out.println("add=>"+employee);employeeDao.add(employee);//保存员工信息return "redirect:/emps";}

7.9 修改员工信息

list.html页面

<td><a class="btn btn-sm btn-primary" th:href="@{'/emp/'+${emp.getId()}}">编辑</a>
</td>
  • 因为有自增id,所以要隐藏,不然一修改就自动新增了!
    <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"><form th:action="@{updateEmp}" method="post"><input type="hidden" name="${id}" th:value="${emp.getId()}"><div class="form-group"><label>LastName</label><input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="name"></div><div class="form-group"><label>Email</label><input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="email"></div><div class="form-group"><label>Gender</label><div class="form-check form-check-inline"><input th:checked="${emp.getGender()==1}" class="form-check-input" type="radio" name="gender" value="1"><label class="form-check-label">男</label></div><div class="form-check form-check-inline"><input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio" name="gender" value="0"><label class="form-check-label">女</label></div></div><div class="form-group"><label>department</label><select class="form-control" name="department.id"><option th:selected="${dept.getId()==emp.department.getId()}"th:each="dept:${departments}" th:text="${dept.getDepartmentName()}"th:value="${dept.getId()}"></option></select></div><div class="form-group"><label>Birth</label><input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}" type="text" name="birth" class="form-control" placeholder="2022-07-13"></div><button type="submit" class="btn btn-default btn-success">修改</button></form></main>
//跳转到修改页面@GetMapping("/emp/{id}") //restful风格public String tuUpdateEmp(@PathVariable("id")Integer id,Model model){//查出原来的数据Employee employeeById = employeeDao.getEmployeeById(id);model.addAttribute("emp",employeeById);//查询部门所有信息Collection<Department> departments = departmentDao.getDepartments();model.addAttribute("departments",departments);return "emp/update";}@PostMapping("/updateEmp")public String updateEmp(Employee employee){employeeDao.add(employee);//修改员工信息return "redirect:/emps";}

7.10 删除员工

index.html

<a class="btn btn-sm btn-danger" th:href="@{'/delete/'+${emp.getId()}}">删除</a>
 //删除员工信息@RequestMapping("/delete/{id}")public String deleteById(@PathVariable("id")Integer id){employeeDao.deleteEmployeeById(id);return "redirect:/emps";}

8、Data

对于数据访问层,无论是SQL还是NOsql,springboot底层都是采用springData的方式进行统一处理。

springData官网:https://spring.io/projects/spring-data

数据库相关的启动器:官方文档:Spring Boot Reference Guide

8.1、JDBC

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
  • Could not autowire. No beans of ‘DataSource’ type found问题解决办法:

  • @Autowired(required = false)
    

配置文件:

  • DataSourceProperties
  • 对于的DataSourceAutoConfiguration

8.2 SpringBoot封装JDBC

spring:
datasource:
username: root
password: 123456789
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver #数据源HikariDataSource
package com.liu.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;import java.util.List;
import java.util.Map;@RestController
public class JdbcController {@Autowired(required = false)JdbcTemplate jdbcTemplate;//查询数据库的所有信息//无实体类时用万能的map查询数据@GetMapping("/userList")public List<Map<String,Object>> userList(){String sql = "select * from mybatis.user";List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);return maps;}@GetMapping("/addUser")public String addUser(){String sql = "insert into mybatis.user(id,name,pwd) values (5,'小明','12345678')";int i = jdbcTemplate.update(sql);return "addOK!";}@GetMapping("/updateUser/{id}")public String updateUser(@PathVariable("id")int id){String sql = "update mybatis.user set name=?,pwd=? where id="+id;//封装数据Object[] objects = new Object[2];objects[0] = "小华";    //修改的名字objects[1] = "zxczxc"; //密码jdbcTemplate.update(sql,objects);return "updateOK";}@GetMapping("/deleteUser/{id}")public String deleteUser(@PathVariable("id")int id){String sql = "delete from mybatis.user where id=?";jdbcTemplate.update(sql,id);return "deleteOK!";}}

8.3 数据源Druid

  • 阿里巴巴平台的数据库连接池实现,结合C3P0,DBCP,PROXOOL等DB池的有点,同时加入日志监控。

  • 天生针对监控而生的DB连接池

  • HikariDataSource是速度最快的数据源

依赖包:

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.21</version>
</dependency>
<!--log4j的依赖包-->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>

yaml配置中修改数据源的使用:只用加type即可

spring:datasource:username: rootpassword: 123456789url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=falsedriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceinitial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truefilters: stat,wall,log4jmax-pool-prepared-statement-per-connection-size: 20use-global-data-source-stat: trueconnect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

测试数据源:

@SpringBootTest
class Springboot04DataApplicationTests {@Autowired(required = false)DataSource dataSource;@Testvoid contextLoads() throws SQLException {System.out.println(dataSource.getClass());//获得数据库连接Connection connection = dataSource.getConnection();System.out.println(connection);connection.close();}
}

结果:

强大之处:自动配置

建立一个自动配置的文件

  • 松散绑定:不用注意大小写,下划线什么的
@Configuration
public class DruidConfig {@ConfigurationProperties(prefix = "spring.datasource")@Beanpublic DataSource druidDataSource(){return new DruidDataSource();}
}

与yaml配置文件绑定:只需要再前缀处加入数据源的名称和@Bean注解

@ConfigurationProperties(prefix = "spring.datasource")
@Bean

8.3.1 后台监控

  • 因为springboot内置了servlet容器,所以没有web.xml,替代方法:ServletRegistrationBean
//后台监控
//因为springboot内置了servlet容器,所以没有web.xml,替代方法:ServletRegistrationBean@Beanpublic ServletRegistrationBean StatViewServlet(){ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");//后台需要登录,账号密码配置Map<String,String> initParameters = new HashMap<>();//增加配置initParameters.put("loginUsername","admin");//登录的key是固定的loginUsername  loginPasswordinitParameters.put("loginPassword","123456");//允许谁可以访问initParameters.put("allow","");//静止谁访问initParameters.put("liuxiang","192.168.11.123");bean.setInitParameters(initParameters);//设置初始参数return bean;@Beanpublic FilterRegistrationBean webStatFilter() {FilterRegistrationBean bean = new FilterRegistrationBean();bean.setFilter(new WebStatFilter());Map<String, String> map = new HashMap<>();map.put("exclusions", "*.js*,*.css,/druid/*");bean.setInitParameters(map);return bean;}}

访问druid monitor自动跳转到login,html页面

输入用户名和密码进入后台监控:

出现filter下的数据即可监控统计sql了

当发起后台sql请求时,可以看到监控统计:

9、整合mybatis

导入依赖包:

不是springboot官方的,不是以spring开头

<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version>
</dependency>

以前是写一个mapper和一个对应的mapper.xml,现在统一放在resource目录下

配置文件下:

#整合mybatis
#别名
mybatis.type-aliases-package=com.liu.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

UserMapper

@org.apache.ibatis.annotations.Mapper
@Repository
public interface Mapper {List<User> queryUserList();User queryUserById(int id);int updateUser(User user);int addUser(User user);int deleteUser(int id);}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.mapper.Mapper"><select id="queryUserList" resultType="User">select *from mybatis.user;</select><select id="queryUserById" resultType="User">select *from mybatis.userwhere user.id=#{id};</select><update id="updateUser" parameterType="User">update mybatis.userset name = #{name},pwd=#{pwd}where id=#{id};</update><delete id="deleteUser" parameterType="int">deletefrom mybatis.userwhere id=#{id};</delete><insert id="addUser" parameterType="User">insert into mybatis.user(id, name, pwd)values (#{id},#{name},#{pwd});</insert>
</mapper>

controller层

@RestController
public class UserController {@Autowired(required = false)private UserMapper userMapper;@GetMapping("/queryUserList")public List<User> queryUserList(){List<User> userList = userMapper.queryUserList();return userList;}@GetMapping("/queryUserById/{id}")public User queryUserById(@PathVariable("id") int id){User user = userMapper.queryUserById(id);return user;}@GetMapping("/updateUser")public String updateUser(){userMapper.updateUser(new User(2,"hyt","3423213"));return "OK";}@GetMapping("/addUser")public String addUser(){userMapper.addUser(new User(4,"dsajda","esdfsdf"));return "OK";}@GetMapping("/deleteUser")public String deleteUser(){userMapper.deleteUser(4);return "ok";}
}

10、SpringSecurity(安全)

依赖包

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

认证授权和权限分级

  • 要用数据库连接,在认证部分需要用JDBC连接,而不是在内存中,用Autowired注解自动注入数据源
package com.liu.config;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;//AOP思想
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {//首页所有人可以访问,功能页只有对应有权限的人才能访问//授权http.authorizeHttpRequests().antMatchers("/").permitAll().antMatchers("/level1/**").hasRole("vip1").antMatchers("/level2/**").hasRole("vip2").antMatchers("/level3/**").hasRole("vip3");//没有权限默认会登录页面http.formLogin();//注销http.logout();}//认证//密码编码:passwordEncoder//在spring Security 5.0+ 新增很多加密方法@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("liuxiang").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3").and().withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");}
}

controller层

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

更改前端页面的图标icon

推荐一个网站:https://semantic-ui.com/elements/icon.html

如下所示前端代码即可更改样式:

<a class="item" th:href="@{/toLogin}"><i class="hand point right icon"></i> 登录
</a>
<!--注销-->
<a class="item" th:href="@{/logout}"><i class="share square icon"></i> 注销
</a>

thymeleaf和springsecurity的整合

导依赖包

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity4</artifactId><version>3.0.4.RELEASE</version>
</dependency>

导入命名空间:

<html lang="en" xmlns:th="http://www.thymeleaf.org"xmlns:sec="http://www.thymeleaf.org/extras/spring-security">"
  • springboot版本太高,无法显示!

11、Shiro

  • Apache Shiro是Java的安全(权限)框架
  • 下载地址:http://shiro.apache.org/

功能:

  • Authentication:认证
  • Authorization:授权
  • session management:session管理
  • cryptograhy:加密
  • web support:web支持
  • 缓存

11.1、HelloWorld

1.导入依赖

 <dependencies><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.1</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.21</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.21</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency></dependencies>

2.ini配置

[users]
# user 'root' with password 'secret' and the 'admin' role
root = secret, admin
# user 'guest' with the password 'guest' and the 'guest' role
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# user 'darkhelmet' with password 'ludicrousspeed' and roles 'darklord' and 'schwartz'
darkhelmet = ludicrousspeed, darklord, schwartz
# user 'lonestarr' with password 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

3.Quickstart


import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** Simple Quickstart application showing how to use Shiro's API.** @since 0.9 RC2*/
public class Quickstart {private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);public static void main(String[] args) {Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);// get the currently executing user://获取当前用户对象subjectSubject currentUser = SecurityUtils.getSubject();// 通过当前用户拿到sessionSession session = currentUser.getSession();session.setAttribute("someKey", "aValue");String value = (String) session.getAttribute("someKey");if (value.equals("aValue")) {log.info("Subject =>session! [" + value + "]");}// let's login the current user so we can check against roles and permissions:if (!currentUser.isAuthenticated()) { //令牌UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");token.setRememberMe(true);try {currentUser.login(token);} catch (UnknownAccountException uae) {log.info("There is no user with username of " + token.getPrincipal());} catch (IncorrectCredentialsException ice) {log.info("Password for account " + token.getPrincipal() + " was incorrect!");} catch (LockedAccountException lae) {log.info("The account for username " + token.getPrincipal() + " is locked.  " +"Please contact your administrator to unlock it.");}// ... catch more exceptions here (maybe custom ones specific to your application?catch (AuthenticationException ae) {//unexpected condition?  error?}}//say who they are://print their identifying principal (in this case, a username):log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");//test a role:if (currentUser.hasRole("schwartz")) {log.info("May the Schwartz be with you!");} else {log.info("Hello, mere mortal.");}//test a typed permission (not instance-level)if (currentUser.isPermitted("lightsaber:wield")) {log.info("You may use a lightsaber ring.  Use it wisely.");} else {log.info("Sorry, lightsaber rings are for schwartz masters only.");}//a (very powerful) Instance Level permission:if (currentUser.isPermitted("winnebago:drive:eagle5")) {log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +"Here are the keys - have fun!");} else {log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");}//all done - log out!currentUser.logout();System.exit(0);}
}

4.log4j.properties

#输出日志文件到console和file目的地
log4j.rootLogger=DEBUG,console,file# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.Target=System.out
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n# 文件输出的相关设置(file)
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.File=D:/logs/log.log4j
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p] [%d{yy-MM-dd}][%c]%m%n#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

总结方法:

 //1.获取当前用户对象subject
Subject currentUser = SecurityUtils.getSubject();
// 2.通过当前用户拿到session
Session session = currentUser.getSession();
// 3.判断当前的用户是否被认证
if (!currentUser.isAuthenticated()){UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");}
// 4.获得当前用户的认证
currentUser.getPrincipal()
// 5.拥有什么角色
if (currentUser.hasRole("schwartz")){}
// 6.获取什么权限if (currentUser.isPermitted("lightsaber:wield")) {}
//7.注销
currentUser.logout();

SpringBoot中集成Shiro

三大对象:(面试必问)

  1. subject:用户
  2. securityManager:管理所有用户
  3. realm:连接数据

导入依赖:

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.9.0</version>
</dependency>

index.html

<!DOCTYPE html>
<!--suppress ThymeleafVariablesResolveInspection -->
<html lang="en" xmlns:th="http://www.thymeleaf.org"xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body><h1>首页</h1><div th:if="${session.loginUser==null}"><a th:href="@{toLogin}">登录</a>
</div>
<p th:text="${msg}"></p>
<hr>
<div shiro:hasPermission="user:add"><a th:href="@{/user/add}">add</a>
</div><div shiro:hasPermission="user:update"><a th:href="@{/user/update}">update</a>
</div></body>
</html>

连接数据部分:授权和认证UserRealm

public class UserRealm extends AuthorizingRealm {@AutowiredUserService userService;//授权@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {System.out.println("执行了=>授权doGetAuthorizationInfo");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//        info.addStringPermission("user:add"); 所有人都有权限//拿到当前登录的这个对象Subject subject = SecurityUtils.getSubject();User currentUser = (User) subject.getPrincipal(); //拿到user对象//设置当前用户的权限info.addStringPermission(currentUser.getPerms());return info;}//认证@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行了=>认证doGetAuthenticationInfo");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);//密码认证:加密,MD5return new SimpleAuthenticationInfo(user,user.getPwd(),"");}
}

Controller层

@Controller
public 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);return "index";} catch (UnknownAccountException e){model.addAttribute("msg","用户名错误");return "login";} catch (IncorrectCredentialsException e){model.addAttribute("msg","密码错误");return "login";}}@RequestMapping("/unauth")@ResponseBodypublic String unAuthor(){return "未经授权不予登录!";}}

shiroConfig

@Configuration
public class ShiroConfig {//3.ShiroFilterFactoryBean@Bean(name = "filterShiroFilterRegistrationBean")public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager getDefaultWebSecurityManager){ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();//设置安全管理器bean.setSecurityManager(getDefaultWebSecurityManager);//添加shiro的内置过滤器/*anno:无需认证就可以访问authc:必须认证才能访问user:必须拥有 记住我 功能才能用perms:拥有对某个资源的权限才能访问roles:拥有某个角色权限才能访问*///拦截Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();//        filterChainDefinitionMap.put("/user/add","authc");
//        filterChainDefinitionMap.put("/user/update","authc");//授权filterChainDefinitionMap.put("/user/add","perms[user:add]");filterChainDefinitionMap.put("/user/update","perms[user:update]");//拦截请求filterChainDefinitionMap.put("/user/*","authc");bean.setFilterChainDefinitionMap(filterChainDefinitionMap);//设置登录的请求bean.setLoginUrl("/toLogin");//设置未被认证的请求bean.setUnauthorizedUrl("/unauth");return bean;}//2.DefaultWebSecurityManager@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//关联RealmsecurityManager.setRealm(userRealm);return securityManager;}//1.创建Realm对象,需要自定义@Beanpublic UserRealm userRealm(){return new UserRealm();}//整合shiroDialect:用来整合shiro和thymeleaf@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}}

连接数据库,用druid数据源,整合mybatis,整合shiro

导入依赖

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version></dependency><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency>

编写配置文件:yaml

spring:datasource:username: rootpassword: 123456789url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=falsedriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceinitial-size: 5min-idle: 5max-active: 20max-wait: 60000time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsepool-prepared-statements: truefilters: stat,wall,log4jmax-pool-prepared-statement-per-connection-size: 20use-global-data-source-stat: trueconnect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500mybatis:type-aliases-package: com.liu.pojomapper-locations: classpath:mapper/*.xml

User

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private int id;private String pwd;private String name;private String perms;//权限表
}

UserMapper接口

@Mapper
@Repository
public interface UserMapper {public User queryUserByName(String name);
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.mapper.UserMapper"><select id="queryUserByName" resultType="User">select *from mybatis.userwhere user.name=#{name};</select></mapper>

service接口

public interface UserService {public User queryUserByName(String name);
}

service实现类

@Service
public class UserServiceImpl implements UserService{//调mapper层@AutowiredUserMapper userMapper;@Overridepublic User queryUserByName(String name) {return userMapper.queryUserByName(name);}
}
  • 一个开源项目:GitHub - ChenCurry/my-site: follow WinterChenS:Docker+SpringBoot2.0+Mybatis+thymeleaf等技术实现的个人网站

12、Swagger

官网:API Documentation & Design Tools for Teams | Swagger

前后端分离:

  • 前端测试后端接口:postman
  • 后端提供接口

api框架:

  • RestFul API文档在线自动生成工具—api文档与api定义同步更新
  • 直接运行,可以在线测试api接口

在项目中使用swagger需要springbox:

  • swagger2
  • ui

12.1、springBoot集成Swagger

依赖包

<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version>
</dependency>
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version>
</dependency>

配置swagger:

@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig {}

产生的问题:Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerException

因为版本不兼容

解决:

  • 在springboot的配置文件中配置如下:因为高版本后的路径匹配修改了

  • spring:mvc:pathmatch:matching-strategy: ant_path_matcher
    
  • 降低springboot版本到2.5.6

测试运行:Swagger UI

12.2、配置swagger

  • Swagger的bean实例 Docket
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig {@Beanpublic Docket docket(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());}//配置swagger信息=apiInfoprivate ApiInfo apiInfo(){//作者信息Contact contact = new Contact("liuxiang", "http://localhost:8080/", "971223772@qq.com");return new ApiInfo("刘想的swagger日记","牛牛的Java学习之旅","1.0","http://localhost:8080/",contact,"Apache 2.0","http://www.apache.org/licenses/LICENSE-2.0",new ArrayList());}}

Swagger配置扫描接口

 @Beanpublic Docket docket(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()//basePackage:指定要扫描的包//any():扫描全部//none():不扫描//withClassAnnotation():扫描类上的注解//withMethodAnnotation():扫描方法上的注解.apis(RequestHandlerSelectors.basePackage("com.liu.swagger.controller"))//paths():过滤路径.paths(PathSelectors.ant("/liu/**")).build();}

配置是否启动swagger

@Bean
public Docket docket(){return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(false) //是否启动swagger.select().apis(RequestHandlerSelectors.basePackage("com.liu.swagger.controller")).build();
}

测试题

如果想要在生产环境下开启swagger,在测试环境下不开启

方法:

  • 通过设置两套模式application-dev.properties和application-prod.properties,分别配置不同的端口号,在application.properties中选择开启哪套环境

  • spring.profiles.active=dev
    
  • 在swaggerConfig中配置:

  • @Bean
    public Docket docket(Environment environment){//设置要显示的swagger环境
    Profiles profiles = Profiles.of("dev", "test");
    //通过environment.acceptsProfiles判断是否处在设定的环境下
    boolean flag = environment.acceptsProfiles(profiles);return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).enable(flag).select().apis(RequestHandlerSelectors.basePackage("com.liu.swagger.controller")).build();
    }
    

配置API文档分组

配置多个分组,多个Docket即可!

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

实体类配置

@ApiModel("用户实体类")
public class User {@ApiModelProperty("用户名")public String username;@ApiModelProperty("密码")public String password;}

controller

//只要接口中的返回值存在实体类,就会被扫描到swagger中
@PostMapping(value = "/user")public User user(){return new User();}

其他的注解

@ApiOperation("hello控制类") //用在方法上@GetMapping(value = "/hello")public String hello(@ApiParam("用户名") String username){return "hello"+username;}

结果:有中文了

13、任务

13.1 异步任务

在方法上加注解@Async

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

在main方法上加注解@EnableAsync开启异步功能

@EnableAsync
@SpringBootApplication
public class Springboot10TestApplication {public static void main(String[] args) {SpringApplication.run(Springboot10TestApplication.class, args);}
}

就可以一瞬间响应,无需等待!

13.2 邮件任务

导入依赖

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

一个简单的邮件

1.先配置相关信息,先在qq邮件设置里将POP3/SMTP服务开启

spring.mail.username=971223772@qq.com
spring.mail.password=zyvegqhkctuobeif
spring.mail.host=smtp.qq.com
#开启加密验证
spring.mail.properties.mail.smtp,ssl.enable=true

2.测试

@SpringBootTest
class Springboot10TestApplicationTests {@Autowired(required = false)JavaMailSenderImpl mailSender;@Testvoid contextLoads() {SimpleMailMessage mailMessage = new SimpleMailMessage();mailMessage.setSubject("牛牛的Java学习之旅!");mailMessage.setText("加油继续努力!");mailMessage.setTo("971223772@qq.com");mailMessage.setFrom("971223772@qq.com");mailSender.send(mailMessage);}}

一个复杂的邮件发送

@SpringBootTest
class Springboot10TestApplicationTests {@Autowired(required = false)JavaMailSenderImpl mailSender;@Testvoid contextLoads() throws MessagingException {MimeMessage mimeMessage = mailSender.createMimeMessage();//组件MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true);//设置主题messageHelper.setSubject("牛牛的Java的学习");messageHelper.setText("<p style='color:red'>继续加油学习!冲鸭!</p>",true);//附件 绝对地址messageHelper.addAttachment("1.jpg",new File("C:\\Users\\liuxiang\\Desktop\\1.jpg"));messageHelper.setTo("971223772@qq.com");messageHelper.setFrom("971223772@qq.com");mailSender.send(mimeMessage);}
}

封装成工具类

 //封装成工具类/**** @param html* @param subject* @param text* @param fileName* @param fileUrl* @param ReceiveAddress* @param sendAddress* @throws MessagingException* @Author liuxiang*/public void sendMail(Boolean html,String subject,String text,String fileName,String fileUrl,String ReceiveAddress,String sendAddress) throws MessagingException{MimeMessage mimeMessage = mailSender.createMimeMessage();//组件MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,html);//设置主题messageHelper.setSubject(subject);messageHelper.setText(text,html);//附件 绝对地址messageHelper.addAttachment(fileName,new File(fileUrl));messageHelper.setTo(ReceiveAddress);messageHelper.setFrom(sendAddress);mailSender.send(mimeMessage);}

13.3 定时任务

  1. 在main线程开启定时功能的注解
@EnableAsync
@EnableScheduling //开始定时功能的注解
@SpringBootApplication
public class Springboot10TestApplication {public static void main(String[] args) {SpringApplication.run(Springboot10TestApplication.class, args);}
}
  1. 使用 @Scheduled(cron = “0 * * * * 0-7”)注解和cron表达式
@Service
public class ScheduleService {//在一个特定的事件执行这个方法//cron表达式 任务调度//秒 分 时 日 月 周几@Scheduled(cron = "0 * * * * 0-7")public void hello(){System.out.println("hello");}
}

文件上传和下载

springMVC中没有装配MultipartResolver,所以默认情况下不能处理文件上传工作,如果想要使用文件上传功能,需要在上下文中配置MultipartResolver。

前端表单要求:必须将表单的method设置为POST,并将enctype设置为multipart/form-data,只有在这种情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。

  • application/x-www=form-urlencoded:默认方式,只处理表单域中的value属性值,采用这种编码方式的表单会将表单域中的值处理成URL编码方式。
  • multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码丰富会把文件域指定文件的内容也封装到请求参数中,不会对字符编码
  • text/plain:除了把空格转换为"+"号外,其他字符不作编码处理,适用 直接通过表单发送右键
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post"><input type="file" name="file"><input type="submit"></form>

后端导入文件上传的jar包:commons-fileupload

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>

在resources目录下的springmvc-servlet.xml配置:

<!--文件上传配置--><bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!--请求的编码格式,必须和jsp的pageEncoding属性一致,默认是ISO-8859-1--><property name="defaultEncoding" value="utf-8"/><!--上传文件大小上限,单位为字节(10485760=10M)--><property name="maxUploadSize" value="10485760"/><property name="maxInMemorySize" value="40960"/></bean>

controller层

package com.liu.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.commons.CommonsMultipartFile;import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;@RestController
public class FileController {//@RequestMapping("file") 将name=file控件得到的文件封装成CommonsMultipartFile对象//批量上传CommonsMultipartFile则为数组即可@RequestMapping("/upload")public String fileUpLoad(@RequestParam("file")CommonsMultipartFile file, HttpServletRequest request) throws IOException {//获取文件名:file.getOriginalFilename();String uploadFileName = file.getOriginalFilename();//如果文件名为空,直接回到首页if ("".equals(uploadFileName)){return "redirect:/index.jsp";}System.out.println("上传文件名:"+uploadFileName);//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");//如果路径不存在,创建一个File realPath = new File(path);if (!realPath.exists()){realPath.mkdir();}System.out.println("上传文件保存地址:"+realPath);InputStream is = file.getInputStream();//文件输入流FileOutputStream os = new FileOutputStream(new File(realPath, uploadFileName));//输出流//读取写出int len = 0;byte[] buffer = new byte[1024];while ((len=is.read(buffer))!=-1){os.write(buffer,0,len);os.flush();}os.close();is.close();return "redirect:/index.jsp";}
}

方法二:

  //采用file.TransferTo来保存上传的文件@RequestMapping("/upload2")public String fileUpLoad2(@RequestParam("file")CommonsMultipartFile file, HttpServletRequest request) throws IOException {//上传路径保存设置String path = request.getServletContext().getRealPath("/upload");//如果路径不存在,创建一个File realPath = new File(path);if (!realPath.exists()) {realPath.mkdir();}System.out.println("上传文件保存地址:"+realPath);//通过CommonsMultipartFile的方法直接写文件file.transferTo(new File(realPath +"/" + file.getOriginalFilename()));return "redirect:/index.jsp";}

文件下载

  1. 设置response响应头
  2. 读取文件InputStream
  3. 写出文件OutputStream
  4. 执行操作
  5. 关闭流
//下载图片方法@RequestMapping("/downLoad")public String downLoads(HttpServletResponse response,HttpServletRequest request) throws IOException {//要下载的图片String path = request.getServletContext().getRealPath("/upload");String fileName = "基础语法.jpg";//1.设置response响应头response.reset();//设置页面不缓存,清空bufferresponse.setCharacterEncoding("utf-8");response.setContentType("multipart/form-data");//二进制传输数据//设置响应头response.setHeader("Content-Disposition","attachment;fileName="+ URLEncoder.encode(fileName,"UTF-8"));File file = new File(path, fileName);//2.读取文件--输入流InputStream is = new FileInputStream(file);//3.写出文件-输出流OutputStream fos = response.getOutputStream();byte[] buffer = new byte[1024];int index = 0;while ((index = is.read(buffer))!=-1){fos.write(buffer,0,index);fos.flush();}fos.close();is.close();return null;}
 <a href="${pageContext.request.contextPath}/downLoad">下载图片</a>

14、分布式Dubbo+Zookeeper+SpringBoot

分布式理论:

分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像单个相关系统

分布式系统是由一组通过网络进行通信、为了完成共同的任务而协调工作的计算机节点组成的系统。分布式系统的出现是为了用廉价的、普通的机器完成单个计算机无法完成的计算、存储任务。

dubbo官方文档:文档 | Apache Dubbo

ORM单一应用架构:

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

缺点:

  • 性能扩展难
  • 协同开发问题
  • 不利于维护升级

垂直应用架构:

当访问量逐渐变大,单一应用增加机器带来的加速度越来越小,将应用拆分不相干的几个应用,以提升效率,MVC架构是关键

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

分布式服务架构:

将核心业务提取出来作为独立的服务,组件形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求,RPC分布式服务框架是关键。

流动计算架构:

小服务资源的浪费等问题产生,需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。提高机器利用率的资源调度和治理中心(SOA)是关键

什么是RPC

RPC:Remote Procedure Call 远程过程调用

  • 允许程序调用另外一个地址空间的过程或函数,而不用程序员显示编码这个远程调用的细节。

  • 核心:通讯、序列化

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

Consumer:o调用远程服务的服务消费方,服务消费者在启动时向注册中心订阅自己所需的服务

Registry:注册中心返回服务提供者地址列表给消费者

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

Dubbo和zookeeper的安装

zookeeper下载地址:

http://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz

管理员身份进入cmd命令:

可能遇到的问题:闪退

解决:

  • 将conf文件夹下面的zoo_sample.cfg复制一份改名未zoo.cfg即可
  • zookeeper的端口号:clientPort=2181

dubbo-admin下载

地址:https://github.com/apache/dubbo-admin/tree/master

端口号:7001

在项目目录下打包dubbo-admin

D:\Environment\dubbo-admin-master>mvn clean package -Dmaven.test.skip=true

出现build success即可

cmd命令下执行jar包:

java -jar dubbo-admin-server-0.3.0.jar

账户密码是 root-root

服务注册

  • 不用上述的下载也可以,通过导入jar包即可
<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>3.0.9</version>
</dependency>
<!--zookeeper-->
<dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.2</version>
</dependency>

需要排除日志,不然会起冲突,报错:

<dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.14</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency>

注册中心:

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

service层:

@DubboService
@Component  //使用了dubbo后不要使用service
public class TicketServiceImpl implements TicketService{@Overridepublic String getTicket() {return "llx";}
}

消费者配置:

server.port=8002#消费者应用名字
dubbo.application.name=consumer-server#注册中心的地址
dubbo.registry.address=zookeeper://127.0.0.1.2181

service

@Service //是spring的注解service
public class UserService {//想拿到provider-service的票,去注册中心拿到服务@Reference //定义路径相同的的接口名 引用 从远程注入服务TicketService ticketService;public void buyTicket(){String ticket = ticketService.getTicket();System.out.println(ticket);}
}

复利用,开发性的浪费

分布式服务架构:

将核心业务提取出来作为独立的服务,组件形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求,RPC分布式服务框架是关键。

流动计算架构:

小服务资源的浪费等问题产生,需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。提高机器利用率的资源调度和治理中心(SOA)是关键

什么是RPC

RPC:Remote Procedure Call 远程过程调用

  • 允许程序调用另外一个地址空间的过程或函数,而不用程序员显示编码这个远程调用的细节。

  • 核心:通讯、序列化

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

Consumer:o调用远程服务的服务消费方,服务消费者在启动时向注册中心订阅自己所需的服务

Registry:注册中心返回服务提供者地址列表给消费者

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

Dubbo和zookeeper的安装

zookeeper下载地址:

http://archive.apache.org/dist/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz

管理员身份进入cmd命令:

[外链图片转存中…(img-oR9kCsl1-1658244095119)]

可能遇到的问题:闪退

解决:

  • 将conf文件夹下面的zoo_sample.cfg复制一份改名未zoo.cfg即可
  • zookeeper的端口号:clientPort=2181

dubbo-admin下载

地址:https://github.com/apache/dubbo-admin/tree/master

端口号:7001

在项目目录下打包dubbo-admin

D:\Environment\dubbo-admin-master>mvn clean package -Dmaven.test.skip=true

出现build success即可

[外链图片转存中…(img-Qmxxx9gy-1658244095119)]

cmd命令下执行jar包:

java -jar dubbo-admin-server-0.3.0.jar

账户密码是 root-root

服务注册

  • 不用上述的下载也可以,通过导入jar包即可
<dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>3.0.9</version>
</dependency>
<!--zookeeper-->
<dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.2</version>
</dependency>

需要排除日志,不然会起冲突,报错:

<dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>2.12.0</version></dependency><dependency><groupId>org.apache.zookeeper</groupId><artifactId>zookeeper</artifactId><version>3.4.14</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency>

注册中心:

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

service层:

@DubboService
@Component  //使用了dubbo后不要使用service
public class TicketServiceImpl implements TicketService{@Overridepublic String getTicket() {return "llx";}
}

消费者配置:

server.port=8002#消费者应用名字
dubbo.application.name=consumer-server#注册中心的地址
dubbo.registry.address=zookeeper://127.0.0.1.2181

service

@Service //是spring的注解service
public class UserService {//想拿到provider-service的票,去注册中心拿到服务@Reference //定义路径相同的的接口名 引用 从远程注入服务TicketService ticketService;public void buyTicket(){String ticket = ticketService.getTicket();System.out.println(ticket);}
}

SpringBoot的完整学习相关推荐

  1. 跟狂神学习SpringBoot,完整学习笔记

    1.SpringBoot 回顾什么是Spring Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson . Spring是为了解决企业级应用开发的 ...

  2. Springboot中国古代史在线学习网站 毕业设计-附源码260839

    Springboot中国古代史在线学习网站 摘要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为 ...

  3. 基于Springboot的书籍学习平台

    摘要 首先,论文一开始便是清楚的论述了平台的研究内容.其次,剖析平台需求分析,弄明白"做什么",分析包括业务分析和业务流程的分析以及用例分析,更进一步明确平台的需求.然后在明白了平 ...

  4. 基于Springboot实现在线学习管理平台

    项目编号:BS-XX-064 运行环境: 开发工具:IDEA / ECLIPSE 数据库:MYSQL5.7 应用服务器:TOMCAT8.5.31 JDK:1.8 后台开发技术:Springboot+m ...

  5. Springboot中国古代史在线学习网站毕业设计源码260839

    Springboot中国古代史在线学习网站 摘要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为 ...

  6. Java 开发 (实习生/应届生) 完整学习路线和规划,希望能够帮到屏幕前迷茫的你

    这是来自一个没有团队,没有机构,仅仅是一个热爱Java开发的带学生UP主完成的学习路线,所有的资料.源码可以直接下载. 各位小伙伴大家好,也许这是你们第一次见到我,我和你一样,曾也是一个对于未来充满迷 ...

  7. java springboot VUE 在线学习平台系统开发mysql数据库web结构java编程计算机网页源码maven项目前后端分离

    一.源码特点   springboot VUE 在线学习平台系统是一套完善的完整信息管理类型系统 前后端分离,结合springboot框架和VUE完成本系统,对理解JSP java编程开发语言有帮助系 ...

  8. SpringBoot整合Shiro学习(上)

    SpringBoot整合Shiro(上) 基于[编程不良人]2020最新版Shiro教程,整合SpringBoot项目实战教程 哔哩哔哩链接:https://www.bilibili.com/vide ...

  9. 奉劝那些刚参加工作的学弟学妹们:要想进大厂,这些并发编程知识是你必须要掌握的!完整学习路线!!(建议收藏)

    大家好,我是冰河~~ 今天给大家带来一篇完整的并发编程学习路线,这应该是全网最全的并发编程学习路线了吧,希望能够为各位小伙伴们带来实质性的帮助. 如果这篇文章对大家有点帮助,小伙伴们点赞,收藏,评论, ...

  10. SpringBoot源码学习系列之异常处理自动配置

    SpringBoot源码学习系列之异常处理自动配置 1.源码学习 先给个SpringBoot中的异常例子,假如访问一个错误链接,让其返回404页面 在浏览器访问: 而在其它的客户端软件,比如postm ...

最新文章

  1. 20分钟学会CMake
  2. Java内存泄露和内存溢出、JVM命令行工具、.JDK可视化工具、Java Class文件
  3. Redhat系统下三种主要的软件包安装方法
  4. oracle 之 安装后pl/sql登录报ora-12154
  5. 46. 全排列/47. 全排列 II
  6. bat递归查找指定文件_批处理脚本遍历指定文件夹下的文件
  7. python ssl socket_Python使用Socket(Https)Post登录百度的实现代码
  8. Qt富文本编辑器QTextDocument
  9. 利用GDB调试 MSQL
  10. Vue笔记--高级入门
  11. 现实世界与虚拟世界的差别在哪里
  12. 程序员去美国工作:奋斗在加州
  13. 杭州十二条经典登山路线
  14. Python批量将TXT文件转为Excel格式
  15. 不良 : Cloudera Manager Agent 的日志目录位于可用空间小于 1.0 吉字节 的文件系统上。 /var/log/cloudera-scm-agent(可用:434.8 兆字节 (
  16. linux支持raid5阵列,Linux中raid磁盘阵列,磁盘阵列raid5
  17. centos7安装Memcached
  18. jQuery 效果 ——fadeIn() 方法、fadeOut() 方法
  19. 钉钉生成jspapi token出现IP地址不在白名单的解决办法
  20. Windows10下CUDA与cuDNN的安装

热门文章

  1. 360浏览器的html在哪里,360浏览器收藏夹路径在哪里
  2. ubuntu 安装360浏览器
  3. 北大青鸟汉字注释机内码_众海常用汉字机内码表
  4. uniapp个人中心界面模板
  5. 一个在线挑选编程字体的网站
  6. android 2d 漫画界面,开源漫画项目,部分界面仿有妖气
  7. R语言与抽样技术学习笔记(Randomize)
  8. 三种刷写LEDE等软路由固件的方法
  9. 关于Cisco ASDM中配置STATIC NAT顺序的问题-By 年糕泰迪
  10. 【离散数学】【图论】哈密顿图