SpringBoot入门

4 篇文章 0 订阅
2 篇文章 0 订阅

第1节 SpringBoot是什么

1.SpringBoot是一个可以快速创建可运行的、独立的、生产级的基于Spring的应用程序
2.SpringBoot采用一种约定优于配置的设计理念,可以快速让用户创建出一个可运行的基于Spring的应用

第2节 SpringBoot的优势

1.快速构建项目
2.对主流的开发框架无需配置集成,会自动的集成到一起(约定优于配置)
3.项目可独立运行,不需要外部servlet容器(不需要额外配置tomcat或者jetty容器)
4.提供运行时的应用监控(健康检查机制)
5.极大的提高了开发部署效率

第3节 SpringBoot的系统需求

由于每隔一段时间官网就会提升一次版本,当前授课采用的SpringBoot2.1.18版本(2020年11月)

当前版本的系统需求:
1、JDK8到JDK12(包含)版本
2、Spring框架版本5.1.19.RELEASE或者更高
3、项目构建工具
    - 3.1 Maven版本 3.3+
    - 3.2 Gradle版本 4.x (4.4 and later) and 5.x
4、Servlet Containers
    - 4.1 Tomcat 9.0
    - 4.2 Jetty 9.4

第二章 快速入门

第1节 SpringBoot的脚手架

  • 脚手架的概念

在我们软件开发中的脚手架的概念,类似于我们的maven工具一样,以一种预先定义好的方式生成特定环境,特定的项目目录结构,并且预先定义好了每一个目录文件的具体功能

  • 官网脚手架地址

https://start.spring.io/

  • 阿里云脚手架地址

https://start.aliyun.com

第2节 创建SpringBoot项目的方式

  • 使用官网提供的工具创建(Spring官网下载sts)

  • 使用IDEA创建

    IDEA的
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

![在这里插入图片描述

配置热加载:
在这里插入图片描述

  • 使用脚手架在线创建

第3节 SpringBoot项目结构以及依赖

  • SpringBoot的目录结构

在这里插入图片描述

  • SpringBoot的依赖样式

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <!--带有starter样式-->
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

第4节 快速入门代码编写

  • 使用IDEA创建项目(2.1.14版本)

在这里插入图片描述

  • 勾选web依赖(首先创建一个web项目[相当于我们的SpringMVC项目])

在这里插入图片描述

第三章 启动方式

第1节 热部署

  • 添加maven依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional><!--设置子项是否依赖,如果不设置为true热加载也不会成功-->
    </dependency>
    
  • 配置maven插件(yml配置了,不用加)

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <fork>true</fork><!-- 如果不配置fork热加载不会成功 -->
        </configuration>
    </plugin>
    
  • application.yml文件配置

    spring:
      #配置热部署
      devtools:
      restart:
          enabled: true
        #监听类目录
          additional-paths: 类目录
          #排除监听目录文件
          exclude: 目录
      #页面 禁止thymeleaf缓存(建议:开发环境设置为false,生成环境设置为true)
      thymeleaf:
        cache: false
    
  • IDEA设置

    1、打开IDEA 在当前路径下: File->Settings->Build, Execution, Deployment->Compiler 勾选Build project automatically 然后确定
    2、继续在使用快捷键(Ctrl+Shift+Alt+/)打开面板,选择Registry勾选compiler.automake.allow.when.app.running
    

    启动方式

    1. 使用自带的main方法启动
    2. 在当前项目的根目录下(pom文件所在目录)执行 mvn spring-boot:run 命令
    3. 执行 java -jar xxx.jar 命令
    4. 修改端口号 java -jar xxx.jar --server.port=9999
    

第2节 Web

1、简介

使用SpringBoot;

1)、创建SpringBoot应用,选中我们需要的模块;

2)、SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来

3)、自己编写业务代码;

自动配置原理?

这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx

xxxxAutoConfiguration:帮我们给容器中自动配置组件;
xxxxProperties:配置类来封装配置文件的内容;

2、SpringBoot对静态资源的映射规则;
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {
  //可以设置和静态资源有关的参数,缓存时间等
	WebMvcAuotConfiguration:
		@Override
		public void addResourceHandlers(ResourceHandlerRegistry registry) {
			if (!this.resourceProperties.isAddMappings()) {
				logger.debug("Default resource handling disabled");
				return;
			}
			Integer cachePeriod = this.resourceProperties.getCachePeriod();
			if (!registry.hasMappingForPattern("/webjars/**")) {
				customizeResourceHandlerRegistration(
						registry.addResourceHandler("/webjars/**")
								.addResourceLocations(
										"classpath:/META-INF/resources/webjars/")
						.setCachePeriod(cachePeriod));
			}
			String staticPathPattern = this.mvcProperties.getStaticPathPattern();
          	//静态资源文件夹映射
			if (!registry.hasMappingForPattern(staticPathPattern)) {
				customizeResourceHandlerRegistration(
						registry.addResourceHandler(staticPathPattern)
								.addResourceLocations(
										this.resourceProperties.getStaticLocations())
						.setCachePeriod(cachePeriod));
			}
		}

        //配置欢迎页映射
		@Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(
				ResourceProperties resourceProperties) {
			return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(),
					this.mvcProperties.getStaticPathPattern());
		}

       //配置喜欢的图标
		@Configuration
		@ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true)
		public static class FaviconConfiguration {

			private final ResourceProperties resourceProperties;

			public FaviconConfiguration(ResourceProperties resourceProperties) {
				this.resourceProperties = resourceProperties;
			}

			@Bean
			public SimpleUrlHandlerMapping faviconHandlerMapping() {
				SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
				mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1);
              	//所有  **/favicon.ico 
				mapping.setUrlMap(Collections.singletonMap("**/favicon.ico",
						faviconRequestHandler()));
				return mapping;
			}

			@Bean
			public ResourceHttpRequestHandler faviconRequestHandler() {
				ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler();
				requestHandler
						.setLocations(this.resourceProperties.getFaviconLocations());
				return requestHandler;
			}

		}

1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;

​ webjars:以jar包的方式引入静态资源;

http://www.webjars.org/

在这里插入图片描述

localhost:8080/webjars/jquery/3.3.1/jquery.js

<!--引入jquery-webjar-->在访问的时候只需要写webjars下面资源的名称即可
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>jquery</artifactId>
			<version>3.3.1</version>
		</dependency>

2)、"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射

"classpath:/META-INF/resources/", 
"classpath:/resources/",
"classpath:/static/", 
"classpath:/public/" 
"/":当前项目的根路径

localhost:8080/abc === 去静态资源文件夹里面找abc

3)、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;

​ localhost:8080/ 找index页面

4)、所有的 **/favicon.ico 都是在静态资源文件下找;

3、RestfulCRUD
1)、默认访问首页
package com.springboot.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class MyMvcConfig extends WebMvcConfigurationSupport {
    //设置默认首页
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/index.html").setViewName("login");
    }
}
2)、国际化

1)、编写国际化配置文件;

2)、使用ResourceBundleMessageSource管理国际化资源文件

3)、在页面使用fmt:message取出国际化内容

步骤:

1)、编写国际化配置文件,抽取页面需要显示的国际化消息

在这里插入图片描述

2)、SpringBoot自动配置好了管理国际化资源文件的组件;

@ConfigurationProperties(prefix = "spring.messages")
public class MessageSourceAutoConfiguration {
    
    /**
	 * Comma-separated list of basenames (essentially a fully-qualified classpath
	 * location), each following the ResourceBundle convention with relaxed support for
	 * slash based locations. If it doesn't contain a package qualifier (such as
	 * "org.mypackage"), it will be resolved from the classpath root.
	 */
	private String basename = "messages";  
    //我们的配置文件可以直接放在类路径下叫messages.properties;
    
    @Bean
	public MessageSource messageSource() {
		ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
		if (StringUtils.hasText(this.basename)) {
            //设置国际化资源文件的基础名(去掉语言国家代码的)
			messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
					StringUtils.trimAllWhitespace(this.basename)));
		}
		if (this.encoding != null) {
			messageSource.setDefaultEncoding(this.encoding.name());
		}
		messageSource.setFallbackToSystemLocale(this.fallbackToSystemLocale);
		messageSource.setCacheSeconds(this.cacheSeconds);
		messageSource.setAlwaysUseMessageFormat(this.alwaysUseMessageFormat);
		return messageSource;
	}

3)、去页面获取国际化的值;

在这里插入图片描述

<!DOCTYPE html>
<html lang="en"  xmlns:th="http://www.thymeleaf.org">
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<meta name="description" content="">
		<meta name="author" content="">
		<title>Signin Template for Bootstrap</title>
		<!-- Bootstrap core CSS -->
		<link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}" rel="stylesheet">
		<!-- Custom styles for this template -->
		<link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
	</head>

	<body class="text-center">
		<form class="form-signin" action="dashboard.html">
			<img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt="" width="72" height="72">
			<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
			<label class="sr-only" th:text="#{login.username}">Username</label>
			<input type="text" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus="">
			<label class="sr-only" th:text="#{login.password}">Password</label>
			<input type="password" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="">
			<div class="checkbox mb-3">
				<label>
          		<input type="checkbox" value="remember-me"/> [[#{login.remember}]]
        </label>
			</div>
			<button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
			<p class="mt-5 mb-3 text-muted">© 2017-2018</p>
			<a class="btn btn-sm">中文</a>
			<a class="btn btn-sm">English</a>
		</form>

	</body>

</html>

效果:根据浏览器语言设置的信息切换了国际化;

原理:

​ 国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象);

		@Bean
		@ConditionalOnMissingBean
		@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
		public LocaleResolver localeResolver() {
			if (this.mvcProperties
					.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
				return new FixedLocaleResolver(this.mvcProperties.getLocale());
			}
			AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
			localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
			return localeResolver;
		}
默认的就是根据请求头带来的区域信息获取Locale进行国际化

4)、点击链接切换国际化

/**
 * 可以在连接上携带区域信息
 */
public class MyLocaleResolver implements LocaleResolver {
    
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String l = request.getParameter("l");
        Locale locale = Locale.getDefault();
        if(!StringUtils.isEmpty(l)){
            String[] split = l.split("_");
            locale = new Locale(split[0],split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}


 @Bean
    public LocaleResolver localeResolver(){
        return new MyLocaleResolver();
    }
}


3)、登陆

开发期间模板引擎页面修改以后,要实时生效

1)、禁用模板引擎的缓存

# 禁用缓存
spring.thymeleaf.cache=false 

2)、页面修改完成以后ctrl+f9:重新编译;

登陆错误消息的显示

<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
4)、拦截器进行登陆检查

拦截器

/**
 * 登陆检查,
 */
public class LoginHandlerInterceptor implements HandlerInterceptor {
    //目标方法执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("loginUser");
        if(user == null){
            //未登陆,返回登陆页面
            request.setAttribute("msg","没有权限请先登陆");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else{
            //已登陆,放行请求
            return true;
        }

    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

注册拦截器

  //所有的WebMvcConfigurerAdapter组件都会一起起作用
    @Bean //将组件注册在容器
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
        WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
            @Override
            public void addViewControllers(ViewControllerRegistry registry) {
                registry.addViewController("/").setViewName("login");
                registry.addViewController("/index.html").setViewName("login");
                registry.addViewController("/main.html").setViewName("dashboard");
            }

            //注册拦截器
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                //super.addInterceptors(registry);
                //静态资源;  *.css , *.js
                //SpringBoot已经做好了静态资源映射
                registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**")
                        .excludePathPatterns("/index.html","/","/user/login");
            }
        };
        return adapter;
    }
4、错误处理机制
1)、SpringBoot默认的错误处理机制

默认效果:

​ 1)、浏览器,返回一个默认的错误页面

在这里插入图片描述

浏览器发送请求的请求头:

在这里插入图片描述

​ 2)、如果是其他客户端,默认响应一个json数据

在这里插入图片描述

在这里插入图片描述

原理:

​ 可以参照ErrorMvcAutoConfiguration;错误处理的自动配置;

给容器中添加了以下组件

​ 1、DefaultErrorAttributes:

帮我们在页面共享信息;
@Override
	public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
			boolean includeStackTrace) {
		Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
		errorAttributes.put("timestamp", new Date());
		addStatus(errorAttributes, requestAttributes);
		addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
		addPath(errorAttributes, requestAttributes);
		return errorAttributes;
	}

​ 2、BasicErrorController:处理默认/error请求

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
    
    @RequestMapping(produces = "text/html")//产生html类型的数据;浏览器发送的请求来到这个方法处理
	public ModelAndView errorHtml(HttpServletRequest request,
			HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
				request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
        
        //去哪个页面作为错误页面;包含页面地址和页面内容
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}

	@RequestMapping
	@ResponseBody    //产生json数据,其他客户端来到这个方法处理;
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		Map<String, Object> body = getErrorAttributes(request,
				isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		return new ResponseEntity<Map<String, Object>>(body, status);
	}

​ 3、ErrorPageCustomizer:

	@Value("${error.path:/error}")
	private String path = "/error";  系统出现错误以后来到error请求进行处理;(web.xml注册的错误页面规则)

​ 4、DefaultErrorViewResolver:

@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
			Map<String, Object> model) {
		ModelAndView modelAndView = resolve(String.valueOf(status), model);
		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
		}
		return modelAndView;
	}

	private ModelAndView resolve(String viewName, Map<String, Object> model) {
        //默认SpringBoot可以去找到一个页面?  error/404
		String errorViewName = "error/" + viewName;
        
        //模板引擎可以解析这个页面地址就用模板引擎解析
		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
				.getProvider(errorViewName, this.applicationContext);
		if (provider != null) {
            //模板引擎可用的情况下返回到errorViewName指定的视图地址
			return new ModelAndView(errorViewName, model);
		}
        //模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面   error/404.html
		return resolveResource(errorViewName, model);
	}

​ 步骤:

​ 一但系统出现4xx或者5xx之类的错误;ErrorPageCustomizer就会生效(定制错误的响应规则);就会来到/error请求;就会被BasicErrorController处理;

​ 1)响应页面;去哪个页面是由DefaultErrorViewResolver解析得到的;

protected ModelAndView resolveErrorView(HttpServletRequest request,
      HttpServletResponse response, HttpStatus status, Map<String, Object> model) {
    //所有的ErrorViewResolver得到ModelAndView
   for (ErrorViewResolver resolver : this.errorViewResolvers) {
      ModelAndView modelAndView = resolver.resolveErrorView(request, status, model);
      if (modelAndView != null) {
         return modelAndView;
      }
   }
   return null;
}
2)、如果定制错误响应:
1)、如何定制错误的页面;

1)、有模板引擎的情况下;error/状态码; 【将错误页面命名为 错误状态码.html 放在模板引擎文件夹里面的 error文件夹下】,发生此状态码的错误就会来到 对应的页面;

​ 我们可以使用4xx和5xx作为错误页面的文件名来匹配这种类型的所有错误,精确优先(优先寻找精确的状态码.html);

​ 页面能获取的信息;

​ timestamp:时间戳

​ status:状态码

​ error:错误提示

​ exception:异常对象

​ message:异常消息

​ errors:JSR303数据校验的错误都在这里

​ 2)、没有模板引擎(模板引擎找不到这个错误页面),静态资源文件夹下找;

​ 3)、以上都没有错误页面,就是默认来到SpringBoot默认的错误提示页面;

2)、如何定制错误的json数据;

​ 1)、自定义异常处理&返回定制json数据;

@ControllerAdvice
public class MyExceptionHandler {

    @ResponseBody
    @ExceptionHandler(UserNotExistException.class)
    public Map<String,Object> handleException(Exception e){
        Map<String,Object> map = new HashMap<>();
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        return map;
    }
}
//没有自适应效果...

​ 2)、转发到/error进行自适应响应效果处理

 @ExceptionHandler(UserNotExistException.class)
    public String handleException(Exception e, HttpServletRequest request){
        Map<String,Object> map = new HashMap<>();
        //传入我们自己的错误状态码  4xx 5xx,否则就不会进入定制错误页面的解析流程
        /**
         * Integer statusCode = (Integer) request
         .getAttribute("javax.servlet.error.status_code");
         */
        request.setAttribute("javax.servlet.error.status_code",500);
        map.put("code","user.notexist");
        map.put("message",e.getMessage());
        //转发到/error
        return "forward:/error";
    }
3)、将我们的定制数据携带出去;

出现错误以后,会来到/error请求,会被BasicErrorController处理,响应出去可以获取的数据是由getErrorAttributes得到的(是AbstractErrorController(ErrorController)规定的方法);

​ 1、完全来编写一个ErrorController的实现类【或者是编写AbstractErrorController的子类】,放在容器中;

​ 2、页面上能用的数据,或者是json返回能用的数据都是通过errorAttributes.getErrorAttributes得到;

​ 容器中DefaultErrorAttributes.getErrorAttributes();默认进行数据处理的;

自定义ErrorAttributes

//给容器中加入我们自己定义的ErrorAttributes
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {

    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
        Map<String, Object> map = super.getErrorAttributes(requestAttributes, includeStackTrace);
        map.put("company","atguigu");
        return map;
    }
}

最终的效果:响应是自适应的,可以通过定制ErrorAttributes改变需要返回的内容,

在这里插入图片描述

第四章 配置文件

第1节 SpringBoot配置文件的两种格式

1、application.properties
2、application.yml

上面这是SpringBoot支持的两种配置文件的格式,在开发中使用哪种都可以,最常见的是yml方式.

第2节 配置文件中自定义配置属性的获取

  • @Value(“${名称}”) 获取单个属性

    1. 在application.properties自定义配置文件
        # 自定义配置
        com.qianfeng.springboot=hello
        name=Tom
        age=18
    2. 在我们的Java类中获取
    
    @Value("${com.qianfeng.springboot}")
    private String qianfeng;
    @Value("${name}")
    private String name;
    @Value("${age}")
    private Integer age;
    /**
     * 测试配置文件获取
     */
    @GetMapping(value = "/getProperties")
    public String getProperties(){
        System.out.println(qianfeng);
        System.out.println(name);
        System.out.println(age);
        return "ok";
    }
    
  • @ConfigurationProperties 获取实体对象

    • 自定义配置(application.yml)

      user:
        userId: 1001
        userName: Tom
        hireDate: 2020-12-12 22:22:22
      
    • 使用实体对象压入配置信息

      @Component  //加入IOC容器进行实例化
      @ConfigurationProperties(prefix = "user") //指定配置前缀
      public class User {
          private Integer userId;
          private String userName;
          @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") //时间格式
          private Date hireDate;
      }
      
      注意:当使用ConfigurationProperties注解时会报一个警告,官网解释需添加一个依赖即可
      
      <dependency>
      	<groupId>org.springframework.boot</groupId>
      	<artifactId>spring-boot-configuration-processor</artifactId>
      	<optional>true</optional>
      </dependency>
      
    • 测试

      @Autowired
      private User user;
      /**
       * 测试配置文件获取
       */
      @GetMapping(value = "/getProperties")
      public String getProperties(){
          System.out.println(user);
          return "ok";
      }
      

      第3节 SpringBoot多环境配置

  • 多环境配置(方式一)

    SpringBoot多环境管理必须要遵循SpringBoot官方配置文件命名规则 application-{profile}.properties或者application-{profile}.yml
    
    
    eg: 
    application-dev.yml  开发环境 
    application-test.yml 测试环境 
    application-pro.yml  生产环境
    

    按照这个格式编写好的配置默认不会被SpringBoot执行,在运行时springboot还会默认执行application.yml文件,可以在appliaction.yml配置文件中设置Spring.profiles.active=dev 这样在项目运行时就会走application-dev.yml配置,但是这样还是需要频繁的修改配置,所以SpringBoot还提供了一个方式在不修改配置的情况下动态的进行配置文件修改

  • 多环境配置(方式二)

    在同一个文件中进行多环境配置的时候使用 --- 三个横线方式进行分割,在yml文件中配置 
    
    spring:
      profiles:
        active: dev
    ---
    spring:
      profiles: dev
    server:
      port: 8080
    ---
    spring:
      profiles: test
    server:
      port: 8081
    ---
    spring:
      profiles: pro
    server:
      port: 8082
    
  • 使用命令动态的选择环境

    1、使用命令将项目打成jar包,然后使用java -jar xxx.jar 运行
    java -jar thymeleaf01-0.0.1-SNAPSHOT.jar --spring.profiles.active=pro
    
    2、使用maven命令运行(2.x版本)
    mvn spring-boot:run -Dspring-boot.run.profiles=test
    
  • maven命令地址

    https://docs.spring.io/spring-boot/docs/current/maven-plugin/reference/html/#run-example-active-profiles
    

第五章 异常处理

第1节 单个异常处理

//设置指定捕获当前类中的哪些异常,当前设置为Exception异常以及其子类都会被捕获到
@ExceptionHandler(value= {Exception.class})
public Object testException(Exception e){
	Map<String,Object> result = new HashMap();
	result.put("code",200);
	result.put("msg":e.getMessage());
	return result;
}

第2节 统一异常处理

@RestController
@ControllerAdvice
public class CommonException {
    @ExceptionHandler(value = {Exception.class})
    public Object testException(Exception e){
        Map<String,Object> result = new HashMap();
    	result.put("code",200);
    	result.put("msg":e.getMessage());
    	return result;
    }
}
  • 异常的优先级

    先查找当前类里面的异常处理,如果不存在在去统一异常处理类中匹配
    

第六章 日志设置

第1节 SpringBoot的日志

SpringBoot默认使用的是Slf4J+logback的日志

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

//日志的依赖一般都不需要加,因为很多其他依赖都带着此依赖,例如 web依赖
1.1 日志的配置
# 修改日志级别
logging.level.root=INFO
# 日志输出路径(会在当前文件夹下生成一个名字为spring.log的日志文件)
logging.path=C:\\log
# 修改生成的文件名称(会在项目的当前路径下生成一个 xxx.log 日志文件)
# logging.path和logging.file不能同时使用,如果同时使用,那么只有logging.file生效
logging.file=abc.log

logback.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!--
    scan(非必须)属性:默认为false,true表示扫描配置文件的变化并自动重新配置,默认每隔1分钟扫描一次
    scanPeriod(非必须)属性:搭配scan属性使用,设置扫描的间隔时间
-->
<configuration scan="true" scanPeriod="1 seconds">
    <!--用于区分不同应用程序的记录-->
    <contextName>edu-cloud</contextName>
    <!--
        设置自定义参数,类似于pom.xml里面的properties标签,下面可以使用${名称}引用此变量
    -->
    <property name="FILE_PATH" value="logs"></property>

    <!--
        ConsoleAppender: 控制台输出
        STDOUT: 给appender起一个名字
    -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <!--
                pattern: 日志输出的格式化样式
            -->
            <pattern>%d{HH:mm:ss.SSS} --> [%thread] --> %-5level %logger{36} - %msg%n</pattern>
            <!--解决乱码问题-->
            <charset>UTF-8</charset>
        </encoder>
    </appender>


    <!--时间滚动日志生成-->
    <appender name="TIME_ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- ThresholdFilter:临界值过滤器,过滤掉 TRACE 和 DEBUG 级别的日志 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <encoder>
            <charset>UTF-8</charset>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{0} -%msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--
                设置生成的文件地址和文件名
             -->
            <!--<fileNamePattern>${FILE_PATH}/log%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>-->
            <fileNamePattern>D:/SpringBootLog/log%d{yyyy-MM-dd_HH-mm}.log</fileNamePattern>
            <!--最多生成十个,查过十个开始删除-->
            <maxHistory>10</maxHistory><!--保存最近30天的日志-->
        </rollingPolicy>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <!-- 文件大小触发重写新文件 -->
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

    <!--
        debug: 指定日志的输出级别,大于debug的级别都会被输出
    -->
    <root level="debug">
        <!--
            appender-ref: 指定哪一个appender使用此日志输出级别
        -->
        <appender-ref ref="STDOUT" />
        <appender-ref ref="TIME_ROLLING_FILE" />
    </root>
</configuration>

第2节 SpringBoot的统一日志处理

2.1 使用AOP进行日志处理
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.2 使用
@Aspect
@Component
public class LogAspect {
    private final static Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
    @Pointcut(value = "execution(* cn.ukoko.springbootaop.controller..*.*(..))")
    public void log(){}
    @Around(value = "log()")
    public Object methodAroud(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取主机IP
        LOGGER.info("IP :{}",request.getRemoteAddr());
        //获取请求地址
        LOGGER.info("URL:{}",request.getRequestURL().toString());
        //获取请求方式
        LOGGER.info("HTTP Method:{}",request.getMethod());
        //获取类名和方法名
        LOGGER.info("Class Method:{}.{}",joinPoint.getSignature().getDeclaringTypeName(),joinPoint.getSignature().getName());
        Object proceed = joinPoint.proceed();
        LOGGER.info("Result:{}",proceed);
        return proceed;
    }
}

第七章 Spring5.x与Spring4.x版本的差异化

Spring5.x版本相比较于4.x版本发生了很大的变化

第1节 注解的变化

1. @RestController          : 组合注解(@ResponseBody + @Controller)
2. @SpringBootApplication   : 组合注解(多用于SpringBoot的启动注解)
3. @ImportResource          : 如果一定要使用xml配置文件可以使用此注解加载
4. @ConfigurationProperties : 获取以xxx作为前缀的数据
5. @value                   : 获取以什么命名的数据

第2节 配置文件的变化

传统的配置比如ssm框架采用的xml配置,而SpringBoot官方不建议使用xml配置,而是采用传统的API和注解的方式进行配置

举一个例子(整合一个不遵循SpringBoot约定优于配置的依赖[非start依赖])

  • 添加依赖

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.20</version>
    </dependency>
    
  • 创建配置文件

    applicaton.yml
    
    username1: root
    password1: root
    jdbcurl1: jdbc:mysql:///ssm
    
    
    /**
     * 如果maven的依赖满足start这样的类型,多半是不需要配置的,这样的类型的依赖可以和SpringBoot直接整合到一起
     * 但是有一些框架或者工具没有提供start这样类型的依赖,在加入依赖之后不会自动整合到SpringBoot上面,这时候就需要我们配置
     */
     //@ImportResource(locations = {"classpath:bean.xml"})
    @Configuration
    public class DruidConfiguration {
        
        @Value("${username1}") //在进行配置时候不要取有歧义的名字
        private String username;
        @Value("${password1}")
        private String password;
        @Value("${jdbcurl1}")
        private String jdbcurl;
        /**
         * 配置Druid
         */
        //@Primary //自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
        @Bean //将方法的返回值加入到IOC容器中,和我们的xml配置中的bean标签功能相同,内部有name属性,设置为IOC容器中的对象取别名
        public DataSource createDruid(){
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            dataSource.setUrl(jdbcurl);
            System.out.println("------------"+dataSource);
            return dataSource;
        }
    }
    
  • 测试

    @Autowired //从IOC容器中获取对象
    private DataSource dataSource;
    
    
    System.out.println("从ioc容器中获取对象:"+dataSource);
    //获取数据库连接
    Connection connection = dataSource.getConnection();
    Statement statement = connection.createStatement();
    String sql="SELECT * FROM user";
    ResultSet rs = statement.executeQuery(sql);
    while(rs.next()){
        int userId = rs.getInt("user_id");
        System.out.println("userId="+userId);
        System.out.println();
    }
    

第八章 CRUD练习(API版本)

  • 添加依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.17</version>
    </dependency>
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
  • 基础配置

    @Mapper: SpringBoot的Mapper层需要添加此注解
    
    # 数据库配置
    spring.datasource.url=jdbc:mysql://localhost:3306/ssm?characterEncoding=utf-8&useUnicode=true&serverTimezone=GMT%2B8
    spring.datasource.username=root
    spring.datasource.password=root
    
    # mybatis配置
    mybatis.mapper-locations=classpath:mapper/*Mapper.xml
    mybatis.type-aliases-package=com.ssm.testdemo.entity
    mybatis.config-location=classpath:mybatis-config.xml
    
    # 中文编码(处理请求)
    spring.http.encoding.charset=UTF-8
    spring.http.encoding.force=true
    spring.http.encoding.enabled=true
    server.tomcat.uri-encoding=UTF-8
    
  • 单元测试(2.1.x版本)

    @RunWith(SpringRunner.class)
    @SpringBootTest
    

第九章 Thymeleaf模板

Thymeleaf是一个web端的并且独立的Java模板引擎,他能够处理HTML、XML、JavaScript、CSS以及纯文本,Thymeleaf的理念是创建一种优雅和易维护的模板,为了实现这一点,它建立在自然模板之上,将逻辑注入到模板文件中,还不会影响到模板被用作设计原型。Thymeleaf一开始就设计了Web标准,SpringBoot官方推荐使用Thymeleaf 而不是JSP

第1节 快速入门

  • 添加依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    
  • 配置文件

    spring: 
        thymeleaf: 
            mode: HTML 
            cache: false 
            encoding: utf-8
    
  • 创建页面

    在templates目录下创建index.html和result.html
    
    1、index.html
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>首页</title>
    </head>
    <body>
        <!--第一个页面请求-->
        <a href="/helloworld">Hello World</a>
    </body>
    </html>
    -------------------------------------------------------------------
    2、result.html
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>结果页</title>
    </head>
    <body>
        <!--结果页面-->
        <h1>结果页面</h1>
    </body>
    </html>
    
  • 创建控制器(@Controller)

    @Controller
    public class UserController {
    
        /**
         * 快速入门
         */
        @GetMapping(value = "/helloworld")
        public String helloworl(){
            System.out.println("第一个thymeleaf请求...");
            return "result";
        }
    
    }
    
  • 刷新前端页面快捷键

    ctrl+F9
    

    第2节 常见语法

  • index.html

    <a href="/HelloThymeleaf">HelloThymeleaf</a>
    
  • result.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>结果页</title>
    </head>
    <body>
    <!--结果页面-->
    <h1>结果页面</h1>
    <!--显示普通文本,从域对象中获取-->
    <p th:text="${username}"></p>
    <hr>
    <!--显示带有样式的普通文本-->
    <p th:utext="${desc}"></p>
    <hr>
    <!--显示对象,数据处理(thymeleaf提供了内置对象API可以操作数据,比如对前端显示时间的格式化)-->
    <div>
        <p th:text="${user.userId}"></p>
        <p th:text="${user.userName}"></p>
        <p th:text="${user.createTime}"></p>
        <p th:text="${#dates.format(user.createTime,'yyyy-MM-dd HH:mm:ss')}"></p>
    </div>
    <hr>
    <!--内置域对象-->
    <p th:text="${#httpServletRequest.getAttribute('password')}"></p>
    <hr>
    <!--数据遍历  list集合-->
    <table>
        <tr>
            <td>No.</td>
            <td>UID</td>
            <td>姓名</td>
            <td>创建时间</td>
            <td>偶数</td>
            <td>奇数</td>
        </tr>
        <tr th:each="x,y:${users}">
            <td th:text="${y.index+1}"/>
            <td th:text="${x.userId}"/>
            <td th:text="${x.userName}"/>
            <td th:text="${x.createTime}"/>
            <td th:text="${y.even}"/><!--偶数-->
            <td th:text="${y.odd}"/><!--奇数-->
        </tr>
    </table>
    <hr>
    <!--数据遍历  map集合-->
    <table>
        <tr>
            <td>No.</td>
            <td>UID</td>
            <td>姓名</td>
            <td>创建时间</td>
            <td>偶数</td>
            <td>奇数</td>
        </tr>
        <tr th:each="x,y:${map}">
            <td th:text="${y.index+1}"/>
            <td th:text="${x.value.userId}"/>
            <td th:text="${x.value.userName}"/>
            <td th:text="${x.value.createTime}"/>
            <td th:text="${y.even}"/><!--偶数-->
            <td th:text="${y.odd}"/><!--奇数-->
        </tr>
    </table>
    </body>
    </html>
    
  • 控制器

    @GetMapping(value = "/HelloThymeleaf")
    public String HelloThymeleaf(Model model, HttpServletRequest request){
        //显示普通文本,从域对象中获取
        model.addAttribute("username","Tom");
        //显示带有样式的普通文本
        model.addAttribute("desc","<span style='color:red'>你好,中国</span>");
        //显示对象,数据处理(thymeleaf提供了内置对象API可以操作数据,比如对前端显示时间的格式化)
        User user = new User(1001,"user01",new Date());
        model.addAttribute("user",user);
        //内置域对象
        request.setAttribute("password","123456");
        //数据遍历 list
        User user1 = new User(1002,"user02",new Date());
        User user2 = new User(1003,"user03",new Date());
        User user3 = new User(1004,"user04",new Date());
        List<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);
        users.add(user3);
        model.addAttribute("users",users);
        //数据遍历 map
        Map<String,User> map = new HashMap<>();
        map.put("user01",user1);
        map.put("user02",user2);
        map.put("user03",user3);
        model.addAttribute("map",map);
        return "result";
    }
    

    第3节 路径处理

    <script type="text/javascript" th:src="@{/js/main.js}"></script> 
    <a th:href="@{/show}">访问controller方法</a> 
    <a th:href="@{/static_index.html}">访问静态页面</a>
    

    第4节 条件语句

    th:if="boolean" th:if的表达式需为boolean值。如果为true,则标签显示,如果为false,则标签不显示
    
    th:unless="boolean" th:unless和th:if相反,表达式也需为boolean值。如果为true,则标签不显示,如果为false,则标签显示
    
    <span th:if="${stat}">偶</span>
    <span th:unless="${stat}">奇</span>
    

    第5节 页面引入

  • 页面引入介绍

    我们常常需要在一个页面当中引入另一个页面,例如,公用的导航栏以及页脚页面。thymeleaf中提供了两种方式进行页面引入
    
  • 引入方式一[th:replace(替换全部)]

  • 引入方式二[th:include(替换内容)]

    1、新建需要被引入的页面文件,路径为"/templates/footer.html"
    
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
    <footer th:fragment="companyInfo"> 
        <p>设为首页 ©2018 SpringBoot 使用<span th:text="${name}"/>前必读意见反馈京ICP证030173号 </p>
    </footer>
    
    2、在目标页面引入footer.html有两种方式
        - 2.1 <div th:include="footer :: companyInfo" th:with="name=${username}"/> 可以设置参数 
        - 2.2 <div th:replace="footer :: companyInfo"/> 设置参数无效,所以直接引入即可
    

    第6节 使用thymeleaf实现CRUD

    请求路径的参数设置
    
    <a th:href="@{/delteUser(userId=${x.userId})}">删除</a>
    删除路径会构建成一个类似于 deleteUser?userId=1000
    
    <a th:href="@{/jumpUser/{userId}(userId=${x.userId})}">更新</a>
    更新路径会构建成一个类似于 jumpUser/1000
    

第十章 SpringBoor + MyBatis

导入相关依赖:

		<!--web依赖-->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
		<!--SpringBoot启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--导入配置文件处理器,配置文件进行绑定就会有提示-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--引用ThymeLeaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--热加载-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional><!--设置子项是否依赖,如果不设置为true热加载也不会成功-->
        </dependency>
        
		<!--Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
		<!--Mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>5.1.47</scope>
        </dependency>
		<!--MyBatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        
        
        
    	<!--<plugin>
        	<groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
            	<fork>true</fork><-- 如果不配置fork热加载不会成功 
            </configuration>
        </plugin>-->  

config文件

application.yml

spring:
  profiles:
    active: dev
  datasource:
    username: root
    password: 3306
    url: jdbc:mysql://localhost:3306/yikao?serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #Druid连接池配置
    druid:
      initial-size: 5
      max-active: 100
      min-idle: 5
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 30000
      validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false
  #配置热部署
  devtools:
    restart:
      enabled: true
      #监听类目录
      additional-paths: 类目录
      #排除监听目录文件
      exclude: 目录
  #页面 禁止thymeleaf缓存(建议:开发环境设置为false,生成环境设置为true)
  thymeleaf:
    cache: false

#配置mybatis
mybatis:
  mapperLocations: classpath*:mapper/*.xml
  typeAliasesPackage: com.zhibang.springbootdemo2.dao

主程序类

@EnableTransactionManagement		//开启事务管理
@MapperScan("com.xr.demo1.dao")	//与dao层的@Mapper二选一写上即可(主要作用是扫包)
package com.zhibang.springbootdemo2;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@MapperScan("com.zhibang.springbootdemo2.dao")//要加上这个注解
@SpringBootApplication
public class Springbootdemo2Application {

    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo2Application.class, args);
    }

}

第十一章 Shiro整合(注解与传统xml对比)

Spring5.x版本在SpringBoot中官方推荐使用Java配置的方式来进行与第三方框架的集成
我们这里以springBoot和Shiro的整合方式来讲解Java的配置.

  • 版本介绍

    SpringBoot版本   : 2.1.18.RELEASE
    shiro-spring版本 : 1.5.0
    
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.5.0</version>
    </dependency>
    
    //如果使用Shiro的注解功能需要添加springboot的aop依赖
    //同时在application.properties中配置spring.aop.proxy-target-class=true,有的版本不需要配置(当前的2.1.17.RELEASE不需要)
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- thymeleaf模板引擎和shiro框架的整合 -->
    <dependency>
        <groupId>com.github.theborakompanioni</groupId>
        <artifactId>thymeleaf-extras-shiro</artifactId>
        <version>2.0.0</version>
    </dependency>
    
  • 整合用到的核心注解

    @Bean: 可以修饰方法和注解,功能和<bean>标签相同,将对象加入到IOC容器中
    @Configuration : 修饰类,被次注解修饰的类,会被Spring扫描到,被认定为是一个配置类
    
  • 集成Shiro配置代码

    @Configuration
    public class ShiroConfig {
    
        /**
         * 将Realm注入到IOC中
         * @return
         */
        @Bean
        public UserRealm createUserRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
            //创建Realm实例
            UserRealm userRealm = new UserRealm();
            //设置加密规则,HashedCredentialsMatcher对象通过给方法入参注入到此方法中
            userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
            return userRealm;
        }
    
        @Bean
        public HashedCredentialsMatcher createHashedCredentialsMatcher(){
            //创建加密对象
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            //加密方式
            credentialsMatcher.setHashAlgorithmName("md5");
            //迭代次数
            credentialsMatcher.setHashIterations(1024);
            return credentialsMatcher;
        }
    
        /**
         * 安全管理器
         */
        @Bean
        public DefaultWebSecurityManager createSecurityManager(UserRealm userRealm){
            //创建web的安全管理器
            DefaultWebSecurityManager  securityManager = new DefaultWebSecurityManager();
            //配置realm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    
    
        /**
         * 将ShiroFilter加入到IOC
         */
        @Bean
        public ShiroFilterFactoryBean createShiroFilter(DefaultWebSecurityManager securityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            //添加Shiro内置过滤器
            Map<String,String> filterMap = new LinkedHashMap<>();
            //释放静态资源
            filterMap.put("/static/**", "anon");
            filterMap.put("/common/**", "anon");
            filterMap.put("/css/**", "anon");
            filterMap.put("/extends/**", "anon");
            filterMap.put("/fonts/**", "anon");
            filterMap.put("/images/**", "anon");
            filterMap.put("/jquery/**", "anon");
            filterMap.put("/js/**", "anon");
            filterMap.put("/layer/**", "anon");
            filterMap.put("/lib/**", "anon");
            //释放login.html页面
            filterMap.put("/dologin", "anon");
            filterMap.put("/tAccount/domain", "anon");
            filterMap.put("/main", "anon");
    //        filterMap.put("/tAccount/add", "authc");
    //        filterMap.put("/tAccount/update", "authc");
    //        filterMap.put("/tAccount/delete", "authc");
    //        map.put("/home1","authc,roles[admin]");
    //        map.put("/home2","authc,roles[admin],perms[user:update]");
            //授权过滤器
    //        filterMap.put("/tAccount/**", "perms[user:*]");
    
            //认证通过才能访问
            filterMap.put("/**","authc");
    
            //设置未认证跳转到登录页面
            shiroFilterFactoryBean.setLoginUrl("/404");
            //设置未授权跳转到登录页面
            shiroFilterFactoryBean.setUnauthorizedUrl("/401");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
            return shiroFilterFactoryBean;
        }
    
        /**
         * 注解生效
         * 如果使用Shiro注解,需要配置AuthorizationAttributeSourceAdvisor,否则注解不生效
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
        /**
         * 配置ShiroDialect,用于thymeleaf和Shiro标签配合使用
         * @return
         */
        @Bean
        public ShiroDialect getShiroDialect(){
            return new ShiroDialect();
        }
    }
    
  • 控制器测试方法

    //@RestController
    @Controller
    public class UserController {
    
        /**
         * 登陆请求
         */
        @PostMapping(value = "/login")
        @ResponseBody
        public String login(SysUsers sysUsers){
            UsernamePasswordToken token = new UsernamePasswordToken(sysUsers.getUsername(),sysUsers.getPassword());
            Subject subject = SecurityUtils.getSubject();
            try {
                subject.login(token);
                return "3";
            }catch (UnknownAccountException uae){
                System.out.println("用户名不存在:"+uae.getMessage());
                return "0";
            }catch (IncorrectCredentialsException ice){
                System.out.println("密码错误:"+ice.getMessage());
                return "1";
            }catch (LockedAccountException lae){
                System.out.println("用户被锁定:"+lae.getMessage());
                return "0";
            }catch (AuthenticationException ae){
    //          由自定义Realm抛出,在此捕获
                System.out.println("其他异常:"+ae.getMessage());
                return "0";
            }
            //return "success";
        }
    
        /**
         * 测试请求
         */
        @RequiresRoles({"admin"})
        @GetMapping(value = "/home1")
        public String index(){
            System.out.println("我是home1,需要认证用户的角色为admin才能访问");
            return "我是home1...";
        }
    
        /**
         * 测试请求
         */
        @RequiresRoles({"admin"})
        @RequiresPermissions({"user:update"})
        @GetMapping(value = "/home2")
        public String index1(){
            System.out.println("我是home2,需要认证用户的角色是admin并且权限为user:update才可以访问");
            return "我是home2...";
        }
    
        /**
         * 处理未认证的请求
         */
        @GetMapping("/unauthentication")
        public void unAuthorization(){
            System.out.println("当前请求未认证...");
            throw new RuntimeException("当前请求未认证...");
        }
    
        /**
         * 处理未授权的请求
         */
        @GetMapping("/unauthorized")
        public void unauthorized(){
            System.out.println("当前请求未授权...");
            throw new RuntimeException("当前请求未授权...");
        }
        /**
         * 退出登录
         */
        @RequestMapping("/logout")
        public String logout(Model model) {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            model.addAttribute("msg","安全退出!");
            return "login";
        }
    }
    
  • 统一异常处理

    @Component
    @ControllerAdvice
    public class ShiroExceptionHandler {
        
        @ResponseBody
        @ExceptionHandler(value = {AuthorizationException.class})
        public Object processShiroAuthorizationException(){
            return "授权失败...";
        }
    
        @ResponseBody
        @ExceptionHandler(value = {AuthenticationException.class})
        public Object processShiroAuthenticationException(){
            return "认证失败...";
        }
    }
    
  • 自定义的Realm

    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        private SysUsersService sysUsersService;
        @Autowired
        private SysUsersRolesService sysUsersRolesService;
        @Autowired
        private SysRolesService sysRolesService;
        @Autowired
        private SysRolesPermissionsService sysRolesPermissionsService;
        @Autowired
        private SysPermissionsService sysPermissionsService;
    
    
        /**
         * 获取授权数据
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    
            //获取用户名
            String username = principalCollection.getPrimaryPrincipal().toString();
            System.out.println("username:"+username);
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //通过用户名查询当前用户的所有角色
            SysUsers user = sysUsersService.getUserByUsername(username);
            //根据用户ID查询角色信息
            List<SysUsersRoles> usersRoles = sysUsersRolesService.getSysUsersRolesByUserId(user.getUserId());
            //根据角色ID查询角色名称
            Set<String> roleNames = new HashSet<>();
            for (SysUsersRoles usersRole : usersRoles) {
                SysRoles roles = sysRolesService.getSysRolesByRoleId(usersRole.getRoleId());
                roleNames.add(roles.getRoleName());
                //通过角色ID查询权限列表
                List<SysRolesPermissions> sysRolesPermissionss = sysRolesPermissionsService.getSysRolesPermissionsByRoleId(roles.getRoleId());
                //根据权限ID查询权限名称
                Set<String > permissionsNames = new HashSet<>();
                for (SysRolesPermissions rolesPermissionss : sysRolesPermissionss) {
                    SysPermissions permissions = sysPermissionsService.getSysPermissionsByPermissionsId(rolesPermissionss.getPermissionId());
                    if (null!=permissions){
                        permissionsNames.add(permissions.getPermissionName());
                    }
                }
                //将权限信息封装进info
                info.addStringPermissions(permissionsNames);
            }
            info.setRoles(roleNames);
            //        info.addRoles(hashSet);
            //Session session = SecurityUtils.getSubject().getSession();
            //session.setAttribute("role", list);
            return info;
        }
    
        /**
         * 获取认证数据
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    
            //获取用户名
            String username = authenticationToken.getPrincipal().toString();
            System.out.println("realm: username:"+username);
            if(username==null || username.length()<=0){
                throw new UnknownAccountException("当前用户不存在");
            }
            //通过用户名查询数据库
            SysUsers user = sysUsersService.getUserByUsername(username);
    
            if(user==null){
                throw new UnknownAccountException("当前用户不存在");
            }
            System.out.println("====="+user);
            //从数据库中获取数据,并且将查询出来的认证数据封装进alt
            ByteSource salt = ByteSource.Util.bytes(user.getSalt());
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),salt,getName());
            return info;
        }
    }
    
  • thymeleaf和shiro标签使用

    使用标签时先添加:

    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
    

    常用标签:

    guest标签
      <shiro:guest>
      </shiro:guest>
      用户没有身份验证时显示相应信息,即游客访问信息。
    
    user标签
      <shiro:user>  
      </shiro:user>
      用户已经身份验证/记住我登录后显示相应的信息。
    
    authenticated标签
      <shiro:authenticated>  
      </shiro:authenticated>
      用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的。
    
    notAuthenticated标签
      <shiro:notAuthenticated>
      
      </shiro:notAuthenticated>
      用户已经身份验证通过,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证。
    
    principal标签
      <shiro: principal/>
      
      <shiro:principal property="username"/>
      相当于((User)Subject.getPrincipals()).getUsername()。
    
    lacksPermission标签
      <shiro:lacksPermission name="org:create">
     
      </shiro:lacksPermission>
      如果当前Subject没有权限将显示body体内容。
    
    hasRole标签
      <shiro:hasRole name="admin">  
      </shiro:hasRole>
      如果当前Subject有角色将显示body体内容。
    
    hasAnyRoles标签
      <shiro:hasAnyRoles name="admin,user">
       
      </shiro:hasAnyRoles>
      如果当前Subject有任意一个角色(或的关系)将显示body体内容。
    
    lacksRole标签
      <shiro:lacksRole name="abc">  
      </shiro:lacksRole>
      如果当前Subject没有角色将显示body体内容。
    
    hasPermission标签
      <shiro:hasPermission name="user:create">  
      </shiro:hasPermission>
      如果当前Subject有权限将显示body体内容
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值