springboot结课总结

1.springboot入门

简介

•Spring Boot是基于Spring框架开发的全新框架,其设计目的是简化新Spring应用的初始化搭建和开发过程。

•Spring Boot整合了许多框架和第三方库配置,几乎可以达到“开箱即用”。

入门程序

  • @SpringBootApplication 标记该类为主程序启动类

  • **SpringApplication.run()**方法启动主程序类

  • @RestController :该注解为组合注解,等同于Spring中**@Controller**+@ResponseBody注解

    注意:使用此注解只能返回数据 使用@Controller 可返回数据也可返回模板页面

  • @GetMapping :等同于Spring框架中**@RequestMapping**(RequestMethod.GET)注解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8fzQJ10-1593454373934)(D:\掌握月\大三(下)\j2ee\笔记\images\1587801398348.png)]

注意: 在Spring Boot处选择稳定版本(SNAPSHOT表示开发中的不稳定版本)

单元测试与热部署

单元测试

1.pom文件中添加spring-boot-starter-test测试启动器

2.编写单元测试类

3.编写单元测试方法

4.运行结果

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

  • @RunWith(SpringRunner.class) :加载Spring Boot测试注解
  • @SpringBootTest :加载项目的ApplicationContext上下文环境
  1. 除了@Test还有哪些注解

    注解功能说明
    @Before表示在任意使用@Test注解标注的public void方法执行之前执行
    @After表示在任意使用@Test注解标注的public void方法执行之后执行
    @Order定义Spring IOC容器中Bean的执行顺序的优先级

热部署

1.pom文件中添加****spring-boot-devtools热部署依赖

2.IDEA热部署设置

3.热部署****测试

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SuRVHYqY-1593454373944)(D:\掌握月\大三(下)\j2ee\笔记\images\1587801581010.png)]

<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>

Spring Boot 原理分析

<!-- Spring Boot父项目依赖管理 -->
<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.1.3.RELEASE</version>
	<relativePath/>
</parent>

spring-boot-starter-parent是通过标签对一些常用技术框架的依赖文件进行了统一版本号管理。

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

spring-boot-starter-web依赖启动器的主要作用是提供Web开发场景所需的底层所有依赖文件,它对Web开发场景所需的依赖文件进行了统一管理。

•Spring Boot应用的启动入口是**@SpringBootApplication**注解标注类中的main()方法;

•@SpringBootApplication能够扫描Spring组件并自动配置Spring Boot。

•@SpringBootApplication注解是一个组合注解,包含**@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan**三个核心注解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wD8wLrJ9-1593454373954)(D:\掌握月\大三(下)\j2ee\笔记\images\1587801934474.png)]

2.springboot核心配置

@Component

@ConfigurationProperties注入属性

@Value

@PropertySource

@ImportResource

@Configuration

@Profile

Application.yaml

•YAML文件格式是Spring Boot支持的一种JSON超集文件格式。

•相较于传统的Properties配置文件,YAML文件以数据为核心,是一种更为直观且容易被电脑识别的数据序列化格式。

application.yaml文件的和一样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8cGh32t0-1593454373960)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802179098.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KiRM69ew-1593454373966)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802197066.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GAjKq6Vf-1593454373968)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802210605.png)]

 ## @ConfigurationProperties

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E8M4UeVY-1593454373970)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802315628.png)]

使用@ConfigurationProperties注解批量注入属性值时,要保证配置文件中的属性与对应实体类的属性一致,否则无法正确获取并注入属性值。

@Value

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DpyHR4ci-1593454373974)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802333643.png)]

使用@Value注解对每一个属性注入设置,免去了属性setXX()方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gfRD0s7U-1593454373977)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802354917.png)]

@ PropertySource / @ Configuration

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1pzokd6q-1593454373980)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802413217.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XD0fn90p-1593454373982)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802488909.png)]

@Configuration  
public class MyConfig {
    @Bean
    public MyService myService(){
        return new MyService();
    }
}

@Value与@PropertySource的比较

​ 可以看到,@Value注解提供了细粒度的操作,使我们可以自主选择要注入的属性。而当存在大量的属性需要批量注入时,使用@ConfigurationProperties注解可以减少不必要的代码。

PropertySource注解不支持yml文件加载

当配置文件中存在中文时,为了防止乱码需要在PropertySource注解中加入encoding参数。

@ ImportResource**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QDm53N3e-1593454373984)(D:\掌握月\大三(下)\j2ee\笔记\images\1587802459124.png)]

Profile****多环境配置

3.springboot整合jsp

1.引入jsp依赖

<dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>

        <!--jsp页面使用jstl标签-->

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>

2.编写配置文件

server.port=8088
spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp
spring.devtools.restart.enabled=true

3.在WEB-INF编写jsp页面

4.注意事项

(1)@GetMapping("/view")表示处理Get请求

(2)@RequestParam Long id 表示获取前端参数id,并转化为Long类型

(3)return "redirect:/ticket"表示重定向到路径/ticket

(4) return "/ticket/list"表示跳转到/ticket/list.jsp视图

4.springboot整合thymeleaf

1.引入依赖

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

2.编写配置文件

server.port=8088
spring.devtools.restart.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
spring.thymeleaf.suffix=.html
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.cache=false
#spring.thymeleaf.servlet.content-type=text/html

3.在templates下编写html5页面

5.springboot视图技术

1.Thymeleaf的国际化

1.编写国际化配置类

@Configuration
public class MyLocalResovel implements LocaleResolver {
    //自定义区域解析方式

    @Override
    public Locale resolveLocale(HttpServletRequest httpServletRequest) {
        //获取页面手动切换传递的语言参数1
        String l = httpServletRequest.getParameter("l");
        //获取请求头自动传递的语言参数Acceot-Language
        String header = httpServletRequest.getHeader("Accept-Language");
        Locale locale = null;
        //如果手动切换参数不为空,就根据手动参数进行语言切换,否则默认根据请求头信息切换
        if (!StringUtils.isEmpty(l)) {
            String[] split = l.split("_");
            locale = new Locale(split[0], split[1]);
        } else {
            //Accept-Language:en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
            String[] splits = header
                    .split(",");
            String[] split = splits[0].split("-");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

    }

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


<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" media="all"
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
    <title>Title</title>
</head>
<body>
    <p th:text="#{hello}">欢迎进入Thymeleaf的学习</p>
</body>
</html>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-11SszSDp-1593454373986)(images\1587803019778.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QJ34mEmx-1593454373988)(images\1587803032261.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xai5hb9M-1593454373991)(D:\掌握月\大三(下)\j2ee\笔记\images\1587803120290.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0hfNK8YQ-1593454373992)(D:\掌握月\大三(下)\j2ee\笔记\images\1587803143705.png)]

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


spring.thymeleaf.cache = true
spring.thymeleaf.encoding = UTF-8   
spring.thymeleaf.mode = HTML5   
spring.thymeleaf.prefix = classpath:/templates/  
spring.thymeleaf.suffix = .html   


Øclasspath:/META-INF/resources/:项目类路径下的META-INF文件夹下 的resources文件夹下的所有文件。

Øclasspath:/resources/:项目类路径下的resources文件夹下的所有文件。

Øclasspath:/static/:项目类路径下的static文件夹下的所有文件

Øclasspath:/public/:项目类路径下的public文件夹下的所有文件。

@Controller
public class LoginController {
    @GetMapping("/toLoginPage")
    public String toLoginPage(Model model){
        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
        return "login";
    }
}


这里不能用@RestController

<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">
    <title>用户登录界面</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <link th:href="@{/login/css/signin.css}" rel="stylesheet">
</head>


2.常用写法

  • th:replace="fragments/layout::head"
    
    
  • th:href="@{/ticket/list}"
    
    
  • th:object="${ticket}"
    
    
  • th:field="*{id}"
    
    
  • th:fragment="header"
    
    
  • th:src="@{/bootstrap4/js/jquery.min.js}"
    
    
  • th:action="@{/ticket/create}"
    
    
  • th:name="body"
    
    
  • th:text="${status.count}"
    
    
  • th:each="ticket, status : ${ticketList}"
    
    

6.springboot整合三大组件

1.拦截器

注册自定义拦截器MyInterceptor**,实现****HandlerInterceptor** 接口,在该类中编写如下方法

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
    String uri = request.getRequestURI();
    Object loginUser = request.getSession().getAttribute("loginUser");
    if (uri.startsWith("/admin") && null == loginUser) {
        response.sendRedirect("/toLoginPage");
        return false;
    }
    return true;}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
               Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    request.setAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse
        response, Object handler, @Nullable Exception ex) throws Exception {
}


**在自定义配置类MyMVCconfig中,重写addInterceptors()**方法注册自定义的拦截器

其中MyMVCConfig实现WebMvcConfigurer****接口 的配置类

@Autowired
private MyInterceptor myInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
	registry.addInterceptor(myInterceptor)
	             .addPathPatterns("/**")
                             .excludePathPatterns("/login.html");
}

2.Servlet

创建一个自定义ServletMyServlet使用@Component注解将MyServlet类作为组件注入Spring容器。MyServlet类继承自HttpServlet**,通过HttpServletResponse对象向页面输出“hello MyServlet”。**

@Configuration
public class ServletConfig {
    @Bean
    public ServletRegistrationBean getServlet(MyServlet myServlet){
        ServletRegistrationBean registrationBean = 
                                     new ServletRegistrationBean(myServlet,"/myServlet");
        return registrationBean;
    }
}

3.Filter

创建一个自定义ServletMyFilter使用@Component注解将当前MyFilter类作为组件注入到Spring容器中。MyFilter类实现Filter****接口,

@Bean
public FilterRegistrationBean getFilter(MyFilter filter){
	FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
	registrationBean.setUrlPatterns(Arrays.asList("/toLoginPage","/myFilter"));
	return registrationBean;
}


4.Listener

@Component
public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("contextInitialized ..."); }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("contextDestroyed ...");  }
}


@Bean
public ServletListenerRegistrationBean getServletListener(MyListener myListener){
	ServletListenerRegistrationBean registrationBean = 
	new ServletListenerRegistrationBean(myListener);
	return registrationBean;
}

**5.使用路径扫描的方式整合ServletFilter、**Listener

​ **@**WebServlet **@**WebListener

@SpringBootApplication
@ServletComponentScan  // 开启基于注解方式的Servlet组件扫描支持
public class Chapter05Application {
	public static void main(String[] args) {
		SpringApplication.run(Chapter05Application.class, args);
	}
}

7.springboot与文件上传

1.创建上传文件的upload.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>动态添加文件上传列表</title>
    <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
    <script th:src="@{/login/js/jquery.min.js}"></script>
</head>
<div th:if="${uploadStatus}" style="color: red" th:text="${uploadStatus}">
         上传成功</div>
 <form th:action="@{/uploadFile}" method="post" enctype="multipart/form-data">
     上传文件:&nbsp;&nbsp;
     <input type="button" value="添加文件" onclick="add()"/>
     <div id="file" style="margin-top: 10px;" th:value="文件上传区域">  </div>
     <input id="submit" type="submit" value="上传" 
                style="display: none;margin-top: 10px;"/>
 </form>


function add(){
    var innerdiv = "<div>";
    innerdiv += "<input type='file' name='fileUpload' required='required'>" +
                   "<input type='button' value='删除' οnclick='remove(this)'>";
    innerdiv +="</div>";
    $("#file").append(innerdiv);
    $("#submit").css("display","block");
}
function remove(obj) {
    $(obj).parent().remove();
    if($("#file div").length ==0){
        $("#submit").css("display","none");
    }
}

2. 在全局配置文件中添加文件上传的相关配置

# thymeleaf页面缓存设置(默认为true)
spring.thymeleaf.cache=false
# 配置国际化文件基础名
spring.messages.basename=i18n.login
# 单个上传文件大小限制(默认1MB)
spring.servlet.multipart.max-file-size=10MB
# 总上传文件大小限制(默认10MB)
spring.servlet.multipart.max-request-size=50MB

3. 进行文件上传处理实现文件上传功能

创建一个管理文件上传下载的控制类****FileController

@GetMapping("/toUpload")
public String toUpload(){
    return "upload";
}


进行文件上传处理实现文件上传功能

@PostMapping("/uploadFile")
public String uploadFile(MultipartFile[] fileUpload, Model model) {
    model.addAttribute("uploadStatus", "上传成功!");
    for (MultipartFile file : fileUpload) {
        String fileName = file.getOriginalFilename();
        fileName = UUID.randomUUID()+"_"+fileName;
        String dirPath = "F:/file/";File filePath = new File(dirPath);
        if(!filePath.exists()){filePath.mkdirs();}
        try {file.transferTo(new File(dirPath+fileName));
        } catch (Exception e) {e.printStackTrace();
            model.addAttribute("uploadStatus","上传失败: "+e.getMessage());}
    }return "upload";}


8.springboot 与文件下载

1. 添加文件下载依赖

<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.6</version>
</dependency>


2. 定制文件下载页面

<!DOCTYPE html>
 <html lang="en" xmlns:th="http://www.thymeleaf.org">
 <head>
     <meta charset="UTF-8">
     <title>文件下载</title></head><body>
     <div style="margin-bottom: 10px">文件下载列表:</div>
     <table><tr> <td>bloglogo.jpg</td>
             <td><a th:href="@{/download(filename='bloglogo.jpg')}">下载文件</a></td></tr>
         <tr> <td>Spring Boot应用级开发教程.pdf</td>
            <td><a th:href="@{/download(filename='Spring Boot')}">
                下载文件</a></td></tr>  </table></body></html>


3. 编写文件下载处理办法

@GetMapping("/toDownload")
public String toDownload(){
	return "download";
}


@GetMapping("/download")
public ResponseEntity<byte[]> fileDownload(String filename){
	String dirPath = "F:/file/";
	File file = new File(dirPath + File.separator + filename);
	HttpHeaders headers = new HttpHeaders();
	headers.setContentDispositionFormData("attachment",filename);
	headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
     try {return new ResponseEntity<>(FileUtils.readFileToByteArray(file), 
	headers, HttpStatus.OK);} 
       catch (Exception e) {e.printStackTrace();
	return new ResponseEntity<byte[]>(e.getMessage().getBytes(),
                 HttpStatus.EXPECTATION_FAILED);}}



4. 中文名文件下载改进

​ **在FileController类的fileDownload()方法中添加中文的编码处理代码getFilename(**HttpServletRequest request,String **filename)**方法用来根据不同浏览器对下载的中文名进行转码。

@GetMapping("/download")
public ResponseEntity<byte[]> fileDownload(HttpServletRequest request,
String filename) throws Exception{
	String dirPath = "F:/file/";
	File file = new File(dirPath + File.separator + filename);
	HttpHeaders headers = new HttpHeaders();
	filename=getFilename(request,filename);here
	headers.setContentDispositionFormData("attachment",filename);
	headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
	try {return new ResponseEntity<>(FileUtils.readFileToByteArray(file), 
	headers, HttpStatus.OK);} catch (Exception e) {e.printStackTrace();
	return new ResponseEntity<byte[]>(e.getMessage().getBytes(),
	HttpStatus.EXPECTATION_FAILED);}}



9.springboot项目打包

1.jar

项目打包为jar包

执行命令 mvn clean pakcage

使用idea的maven插件

<build>
<plugins>
	<plugin>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-maven-plugin</artifactId>
	</plugin>
</plugins>
</build>


Jar****包方式部署

java -jar xxx.jar

2.war

项目打包为war包

  1. 修改pom.xml文件,将项目的默认打包方式改为war包

    <packaging>war</packaging>
    
    
  2. 声明使用外部tomcat服务器

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <!-- 表明该包只在编译和测试的时候用 -->
        <scope>provided</scope>
    </dependency>
    
    
  3. 为Spring Boot提供启动的Servlet初始化器

    @SpringBootApplication
    public class TicketSysApplication extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(TicketSysApplication.class);
        }
    
        public static void main(String[] args) {
            SpringApplication.run(TicketSysApplication.class, args);
        }
    
    }
    
    
  4. 执行打包命令

    War包形式的打包方式与Jar包的打包方式完全一样

  5. 将生成的war包放入tomcat的webapps文件夹中

    • 将打包好的War包拷贝到Tomcat安装目录下的webapps目录中,执行Tomcat安装目录下bin目录中的startup.bat命令启动War包项目
    • 使用外部Tomcat部署的项目进行访问时,必须加上项目名称(打成war包后的项目全名)
  6. 启动tomcat

10.springboot整合mybatis

1.编写配置文件

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root

#配置MyBatis的xml配置文件路径
mybatis.mapper-locations=classpath:mapper/*.xml
#配置XML映射文件中指定的实体类别名路径
mybatis.type-aliases-package=com.itheima.domain
#开启实体与数据库的字段映射  骆驼峰写法
mybatis.configuration.map-underscore-to-casel-case=true


#配置Mybatis的相关属性
mybatis:
  #指定mapper XML文件的位置
  mapper-locations: classpath:com.zsc.ticketsys.mapper/*.xml
  #指定实体类的别名的映射路径
  type-aliases-package: com.zsc.ticketsys.domain
  configuration:
    #打印输出SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 启动驼峰式转换
    map-underscore-to-camel-case: true
    #开启自增组件
    use-generated-keys: true
    #启动懒加载
    lazy-loading-enabled: true
    #禁用立即加载
    aggressive-lazy-loading: false

2. 设置数据源类型配置

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>


如果在开发过程中,需要对这些第三方Druid的运行参数进行重新设置,必须在application.properties配置文件中进行默认参数覆盖。

spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.initialSize=20
spring.datasource.minIdle=10
spring.datasource.maxActive=100

3.创建Mapper接口文件:@Mapper

@Select("SELECT * FROM t_comment WHERE id =#{id}")
public Comment findById(Integer id);

@Insert("INSERT INTO t_comment(content,author,a_id) " +
            "values (#{content},#{author},#{aId})")
public int insertComment(Comment comment);

@Update("UPDATE t_comment SET content=#{content} WHERE id=#{id}")
public int updateComment(Comment comment);

@Delete("DELETE FROM t_comment WHERE id=#{id}")
public int deleteComment(Integer id);}


4.创建XML映射文件:编写对应的SQL语句

<mapper namespace="com.itheima.mapper.ArticleMapper">
    <select id="selectArticle" resultMap="articleWithComment">
       SELECT a.*,c.id c_id,c.content c_content,c.author
       FROM t_article a,t_comment c
       WHERE a.id=c.a_id AND a.id = #{id}
    </select>
    
    
<update id="updateArticle" parameterType="Article" >
        UPDATE t_article
        <set>
            <if test="title !=null and title !=''">
                title=#{title},
            </if>
            <if test="content !=null and content !=''">
                content=#{content}
            </if>
        </set>
        WHERE id=#{id}
    </update>
    
    
<resultMap id="articleWithComment" type="Article">
        <id property="id" column="id" />
        <result property="title" column="title" />
        <result property="content" column="content" />
        <collection property="commentList" ofType="Comment">
            <id property="id" column="c_id" />
            <result property="content" column="c_content" />
            <result property="author" column="author" />
        </collection>
    </resultMap>


11.springboot整合jpa

Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,它提供了增删改查等常用功能,使开发者可以用较少的代码实现数据操作,同时还易于扩展。

1.编写ORM实体类:实体类与数据表进行映射,并且配置好映射关系。

2.编写Repository接口:针对不同的表数据操作编写各自对应的Repositor接口,并根据需要编写对应的数据操作方法。

1.编写ORM实体类

@Entity(name = "t_comment")
public class Discuss {
    @Id   //主键id 
    @GeneratedValue(strategy =GenerationType.IDENTITY)//主键生成策略
    private Integer id;
    @Column(name = "a_id")//字段映射
    private Integer aId;
	// 省略getXX()和setXX()方法
}


2. 编写Repository接口

//查询author非空的Discuss评论信息
public List<Discuss> findByAuthorNotNull();

//通过文章id分页查询出Discuss评论信息。 
@Query("SELECT c FROM t_comment c WHERE c.aId = ?1")
public List<Discuss> getDiscussPaged(Integer aid,Pageable pageable);  

//通过文章id分页查询出Discuss评论信息。 
@Query(value = "SELECT * FROM t_comment  WHERE  a_Id = ?1",nativeQuery = true)
public List<Discuss> getDiscussPaged2(Integer aid,Pageable pageable);  

注:
与getDiscussPaged()方法的参数和作用完全一样。
区别是该方法上方的@Query注解将nativeQuery属性设置为了true,用来编写原生SQL语句。

//对数据进行更新和删除操作    
	@Transactional
    @Modifying
    @Query("UPDATE t_comment c SET c.author = ?1 WHERE c.id = ?2")
    public int updateDiscuss(String author,Integer id);  
    
@Transactional
@Modifying
@Query("DELETE t_comment c WHERE c.id = ?1")
          public int deleteDiscuss(Integer id); 


3.Repository****继承关系

使用Spring Data JPA自定义Repository接口,必须继承XXRepository******接口。**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q8Mtxarq-1593454373995)(images\1589526146968.png)]

•在自定义的Repository接口中,针对数据的变更操作(修改、删除),无论是否使用了@Query注解,都必须在方法上方添加@Transactional注解进行事务管理,否则程序执行就会出现InvalidDataAccessApiUsageException异常。

•如果在调用Repository接口方法的业务层Service类上已经添加了@Transactional注解进行事务管理,那么Repository接口文件中就可以省略@Transactional注解。

如果自定义接口继承了JpaRepository接口,则默认包含了一些常用的CRUD方法。

自定义Repository接口中,可以使用@Query注解配合SQL语句进行数据的查、改、删操作。

自定义Repository接口中,可以直接使用方法名关键字进行查询操作。

变更操作,要配合使用**@QueryModify****注解**

•在自定义的Repository接口中,使用@Query注解方式执行数据变更操作(修改、删除),除了要使用@Query注解,还必须添加@Modifying注解表示数据变更。

4. pom文件中添加Spring Data JPA****依赖启动器

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


5.编写配置文件

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true


12.springboot整合redis

1.存取速度快:Redis速度非常快,每秒可执行大约110000次的设值操作,或者执行81000次的读取操作。

2.支持丰富的数据类型:Redis支持开发人员常用的大多数数据类型,例如列表、集合、排序集和散列等。

3.操作具有原子性:所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新后的值。

4.提供多种功能:Redis提供了多种功能特性,可用作非关系型数据库、缓存中间件、消息中间件等。

1. pom文件中添加Spring Data Redis****依赖启动器

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


2. 编写实体类****Person

@RedisHash("persons")  
public class Person {
    @Id       
         private String id;
    @Indexed  //二级索引
         private String firstname;
    @Indexed
         private String lastname;
         private Address address;
         private List<Family> familyList;}


3. 编写Repository接口****PersonRepository

public interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByLastname(String lastname);
Page<Person> findPersonByLastname(String lastname, Pageable page);
List<Person> findByFirstnameAndLastname(String firstname, String lastname);
List<Person> findByAddress_City(String city);
List<Person> findByFamilyList_Username(String username);
}


4. Redis****数据库连接配置

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=

13.springboot实现缓存管理

1.springboot默认缓存管理

1.使用@EnableCaching注解开启基于注解的缓存支持

@EnableCaching是由Spring框架提供的,Spring Boot框架对该注解进行了继承,该注解需要配置在类上(在Spring Boot中,通常配置在项目启动类上),用于开启基于注解的缓存支持

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


2.使用@Cacheable注解对数据操作方法进行缓存管理

@Cacheable注解也是由Spring框架提供的,可以作用于类或方法(通常用在数据查询方法上),用于对方法结果进行缓存存储。

@Cacheable注解的执行顺序是,先进行缓存查询,如果为空则进行方法查询,并将结果进行缓存;如果缓存中有数据,不进行方法查询,而是直接使用缓存数据。

@Cacheable(cacheNames = "comment")
public Comment findById(int comment_id){
	Optional<Comment> optional = commentRepository.findById(comment_id);
	if(optional.isPresent()){
		return optional.get();
	}
	return null;
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YnEnfNlj-1593454373997)(D:\掌握月\大三(下)\j2ee\笔记\images\1590229093745.png)]

3.@CachePut

@CachePut注解是由Spring框架提供的,可以作用于类或方法(通常用在数据更新方法上),该注解的作用是更新缓存数据。@CachePut注解的执行顺序是,先进行方法调用,然后将方法结果更新到缓存中

@CachePut注解也提供了多个属性,这些属性与@Cacheable注解的属性完全相同。

4.@CacheEvict

@CacheEvict注解是由Spring框架提供的,可以作用于类或方法(通常用在数据删除方法上),该注解的作用是删除缓存数据。@CacheEvict注解的默认执行顺序是,先进行方法调用,然后将缓存进行清除

@CacheEvict注解也提供了多个属性,这些属性与@Cacheable注解的属性基本相同,除此之外,还额外提供了两个特殊属性allEntries和beforeInvocation。

1allEntries属性

allEntries属性表示是否清除指定缓存空间中的所有缓存数据,默认值为false(即默认只删除指定key对应的缓存数据)。

2beforeInvocation属性

beforeInvocation属性表示是否在方法执行之前进行缓存清除,默认值为false(即默认在执行方法后再进行缓存清除)。

4**.@Caching**

@Caching注解用于针对复杂规则的数据缓存管理,可以作用于类或方法,在@Caching注解内部包含有Cacheable、put和evict三个属性,分别对应于**@Cacheable、@CachePut和@CacheEvict三个注解**。

5**.@CacheConfig**

@CacheConfig注解使用在类上,主要用于统筹管理类中所有使用@Cacheable、@CachePut和@CacheEvict注解标注方法中的公共属性,这些公共属性包括有cacheNames、keyGenerator、cacheManager和cacheResolver

2.基于注解的Redis缓存实现

1.添加****Spring Data Redis 依赖启动器

2.Redis****服务连接配置

3.使用**@Cacheable****、@CachePut@CacheEvict注解定制缓存管理**

4.基于注解的Redis查询缓存测试

5.将缓存对象实现序列化

前4个步骤和默认缓存基本一致,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJ3DmJ8G-1593454373999)(D:\掌握月\大三(下)\j2ee\笔记\images\1590229995125.png)]

3.基于API的Redis缓存实现

1.基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持

2.基于API的Redis缓存实现需要在Spring Boot项目的pom.xml文件中引入Redis依赖启动器,并在配置文件中进行Redis服务连接配置,同时将进行数据存储的Comment实体类实现序列化接口

4.自定义RedisTemplate(为了替换默认的redisAPI序列化机制)

1.使用RedisTemplate进行Redis数据缓存操作时,内部默认使用的是JdkSerializationRedisSerializer序列化方式,所以进行数据缓存的实体类必须实现JDK自带的序列化接口(例如Serializable);

2.使用RedisTemplate进行Redis数据缓存操作时,如果自定义了缓存序列化方式defaultSerializer,那么将使用自定义的序列化方式。

在项目中创建创建一个Redis自定义配置类RedisConfig通过@Configuration注解定义了一个RedisConfig配置类,并使用@Bean注解注入了一个默认名称为方法名的redisTemplate组件(注意,该Bean组件名称必须是redisTemplate)。在定义的Bean组件中,自定义了一个RedisTemplate,使用自定义的Jackson2JsonRedisSerializer数据序列化方式;在定制序列化方式中,定义了一个ObjectMapper用于进行数据转换设置。

 @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //使用jackson序列化
        // 使用JSON格式序列化对象,对缓存数据key和value进行转换
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询时候的转换异常问题
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        // 设置RedisTemplate模板API的序列化方式为JSON
        template.setDefaultSerializer(jackson2JsonRedisSerializer);
        return template;
    }



5.自定义RedisCacheManager(为了替换默认的redis注解序列化机制)

RedisConfig类中添加方法cacheManager**,该方法主要由三部分组成**

RedisSerializer<String> strSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jacksonSeial =
 new Jackson2JsonRedisSerializer(Object.class);


ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSeial.setObjectMapper(om);


RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
		.entryTtl(Duration.ofDays(1))
	         	.serializeKeysWith(RedisSerializationContext.SerializationPair
		.fromSerializer(strSerializer))		              
		.serializeValuesWith(RedisSerializationContext.SerializationPair
		.fromSerializer(jacksonSeial))
		.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager
		.builder(redisConnectionFactory).cacheDefaults(config).build();
		return cacheManager;



分成函数写:

 //    定义cacheManager,统一redis的属性配置
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        // 配置序列化(解决乱码的问题)
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 缓存有效期
                .entryTtl(timeToLive)
                // 使用StringRedisSerializer来序列化和反序列化redis的key值
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2Serializer()))
                // 禁用空值
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();
        return cacheManager;
    }

    /**
     * 配置Jackson2JsonRedisSerializer序列化策略
     */
    private Jackson2JsonRedisSerializer<Object> jackson2Serializer() {
        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
        // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }

14 .springboot整合消息服务

1.引入pom依赖

<dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>

2.配置

# 配置RabbitMQ消息中间件连接配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#配置RabbitMQ虚拟主机路径/,默认可以省略
spring.rabbitmq.virtual-host=/


3.配置类配置

@Configuration
public class RabbitConfig {
    @Value("code-queue")
    private String code_queue;
    @Value("notice-queue")
    private String notice_queue;

    /**
     * json对象解析配置bean
     *
     * @return
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 获取验证码队列bean
     *
     * @return
     */
    @Bean
    public Queue getCodeQueue() {
        return new Queue(code_queue);
    }

    /**
     * 获取通知队列bean
     *
     * @return
     */
    @Bean
    public Queue getNoticeQueue() {
        return new Queue(notice_queue);
    }

    /**
     * 获取验证码消息交换器bean
     *
     * @return
     */
    @Bean
    public Exchange getCodeExchange() {
        return ExchangeBuilder.fanoutExchange("code_exchange").build();
    }

    /**
     * 获取通知消息交换器bean
     *
     * @return
     */
    @Bean
    public Exchange getNoticeExchange() {
        return ExchangeBuilder.fanoutExchange("notice_exchange").build();
    }

    /**
     * 验证码交换器绑定验证码队列
     *
     * @return
     */
    @Bean
    public Binding bindingCode() {
        return BindingBuilder.bind(getCodeQueue()).to(getCodeExchange()).with("").noargs();
    }

    /**
     * 通知交换器绑定通知队列
     *
     * @return
     */
    @Bean
    public Binding bindingNotice() {
        return BindingBuilder.bind(getNoticeQueue()).to(getNoticeExchange()).with("").noargs();
    }

}

4.生产者发送消息

package com.zsc.shixun.utils;

import com.zsc.shixun.model.unauthorize.MessageVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * <p>
 * 生产者发送
 * </p>
 *
 * @author ZWYZY
 * @since 2020/6/11
 */
@Component

public class ProducerUtils {

    @Resource
    public RabbitTemplate rabbitTemplate;

    /**
     * 发送验证码消息
     *
     * @param mailto 邮箱
     * @param title  标题
     * @param cotent 内容
     */
    public void sendCode(String mailto, String title, String cotent) {
        MessageVO messageVO = new MessageVO();
        messageVO.setMailto(mailto);
        messageVO.setTitle(title);
        messageVO.setContent(cotent);

        rabbitTemplate.convertAndSend("code_exchange", "", messageVO);
    }

    /**
     * 发送通知消息
     *
     * @param mailto 邮箱
     * @param title  标题
     * @param cotent 内容
     */
    public void sendNotice(String mailto, String title, String cotent) {
        MessageVO messageVO = new MessageVO();
        messageVO.setMailto(mailto);
        messageVO.setTitle(title);
        messageVO.setContent(cotent);

        rabbitTemplate.convertAndSend("notice_exchange", "", messageVO);
    }

}


5.消费者接收消息

package com.zsc.shixun.utils;

import com.zsc.shixun.model.unauthorize.MessageVO;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * <p>
 * 消费者接收
 * </p>
 *
 * @author ZWYZY
 * @since 2020/6/11
 */
@Component
public class ConsumerUtils {
    @Autowired
    public MailUtils mailUtils;

    @RabbitListener(bindings = @QueueBinding(value = @Queue("code-queue"),
            exchange = @Exchange(value = "code_exchange", type = "fanout")))
    public void psubConsumerCodeAno(MessageVO messageVO) {
        mailUtils.sendCodeEmail(messageVO.getMailto(), messageVO.getTitle(), messageVO.getContent());
        System.out.println("验证码业务接收消息: " + messageVO);
    }

    @RabbitListener(bindings = @QueueBinding(value = @Queue("notice-queue"),
            exchange = @Exchange(value = "notice_exchange", type = "fanout")))
    public void psubConsumerSmsAno(MessageVO messageVO) {
        mailUtils.sendSimpleEmail(messageVO.getMailto(), messageVO.getTitle(), messageVO.getContent());

        System.out.println("通知注册业务接收消息: " + messageVO);
    }

}


15.springboot任务管理

1.异步任务

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


1.无返回值异步调用

  • @Async

  • 使用**@EnableAsync****开启基于注解的异步方法支持**

    @EnableAsync        
    @SpringBootApplication
    public class Chapter09Application {
    	public static void main(String[] args) {
    		SpringApplication.run(Chapter09Application.class, args);
    
        }
    }
    
    
  • 编写测试

    @Async
        public void sendSMS() throws Exception {
            System.out.println("调用短信验证码业务方法...");
            Long startTime = System.currentTimeMillis();
            Thread.sleep(5000);
            Long endTime = System.currentTimeMillis();
            System.out.println("短信业务执行完成耗时:" + (endTime - startTime)); }
    
    
    

2.有返回值异步调用

  • 编写测试方法

    @Async
    public Future<Integer> processB() throws Exception {
    	System.out.println("开始分析并统计业务B数据...");
    	Long startTime = System.currentTimeMillis();
    	Thread.sleep(5000);
    	int count=654321;
    	Long endTime = System.currentTimeMillis();
    	System.out.println("业务B数据统计耗时:" + (endTime - startTime));
    	return new AsyncResult<Integer>(count);
    }
    
    
    
  • 调用方法

@GetMapping("/statistics")
public String statistics() throws Exception {
	Long startTime = System.currentTimeMillis();
	Future<Integer> futureA = myService.processA();
	Future<Integer> futureB = myService.processB();
	int total = futureA.get() + futureB.get();
	System.out.println("异步任务数据统计汇总结果: "+total);
	Long endTime = System.currentTimeMillis();
	System.out.println("主流程耗时: "+(endTime-startTime));
	return "success";}


2.定时任务

  • 编写测试方法
private static final SimpleDateFormat dateFormat =
        new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private Integer count1 = 1;
private Integer count2 = 1;
private Integer count3 = 1;
@Scheduled(fixedRate = 60000)
public void scheduledTaskImmediately() {
    System.out.println(String.format("fixedRate第%s次执行,当前时间为:%s", 
                           count1++, dateFormat.format(new Date())));
}

@Scheduled(fixedDelay = 60000)
public void scheduledTaskAfterSleep() throws InterruptedException {
    System.out.println(String.format("fixedDelay第%s次执行,当前时间为:%s",
                           count2++, dateFormat.format(new Date())));
    Thread.sleep(10000);
}
@Scheduled(cron = "0 * * * * *")
public void scheduledTaskCron(){
    System.out.println(String.format("cron第%s次执行,当前时间为:%s",
                           count3++, dateFormat.format(new Date())));
}




  • 注解开启

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

3.邮件任务

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


注:

当添加上述依赖后,Spring Boot自动配置的邮件服务会生效,在邮件发送任务时,可以直接使用Spring框架提供的JavaMailSender接口或者它的实现类JavaMailSenderImpl邮件发送。

  • 添加配置

    #126免费邮邮件发送服务配置
    spring.mail.host=smtp.126.com
    #spring.mail.port=587
    # 配置126免费邮账户和密码(密码是加密后的授权码)
    spring.mail.username=zhang1417279498@126.com
    spring.mail.password=EUHGPBNEVJIAIOCU
    
    
  • 编写发送邮件任务工具类

    package com.zsc.shixun.utils;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.mail.SimpleMailMessage;
    import org.springframework.mail.javamail.JavaMailSenderImpl;
    import org.springframework.mail.javamail.MimeMessageHelper;
    import org.springframework.stereotype.Service;
    import org.thymeleaf.TemplateEngine;
    import org.thymeleaf.context.Context;
    
    import javax.mail.MessagingException;
    import javax.mail.internet.MimeMessage;
    
    
    /**
     * @Classname MailUtils
     * @Description 邮件发送工具类
     * @Date 2019-3-14 16:25
     * @Created by CrazyStone
     */
    @Service
    public class MailUtils {
        private static final Logger logger = LoggerFactory.getLogger(MailUtils.class);
        @Autowired
        private JavaMailSenderImpl mailSender;
        @Value("${spring.mail.username}")
        private String mailfrom;
        //Thymeleaf提供的模板引擎解析器
        @Autowired
        TemplateEngine templateEngine;
    
        /**
         * 发送简单邮件
         *
         * @param mailto
         * @param title
         * @param content
         */
    
        public void sendSimpleEmail(String mailto, String title, String content) {
            //  定制邮件发送内容
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(mailfrom);
            message.setTo(mailto);
            message.setSubject(title);
            message.setText(content);
            try {
                // 发送邮件
                mailSender.send(message);
                logger.info("发送成功");
            } catch (Exception e) {
                logger.error("简单邮件发送失败");
            }
    
        }
    
    
        /**
         * 发送验证码邮件
         *
         * @param mailto
         * @param title
         * @param code
         */
        public void sendCodeEmail(String mailto, String title, String code) {
            //  定制邮件发送内容
            MimeMessage message = mailSender.createMimeMessage();
            Context context = new Context();
            context.setVariable("code", code);
            String emailContent = templateEngine.process("emailCodeTemplate", context);
            try {
                //在定制Html模板邮件信息时,、使用了MimeMessageHelper类对邮件信息进行封装处理。
                MimeMessageHelper helper = new MimeMessageHelper(message, true);
                helper.setFrom(mailfrom);
                helper.setTo(mailto);
                helper.setSubject(title);
                helper.setText(emailContent, true);
                mailSender.send(message);
                System.out.println("模板邮件发送成功");
            } catch (MessagingException e) {
                System.out.println("模板邮件发送失败 " + e.getMessage());
                e.printStackTrace();
            }
        }
    
    
    }
    
    
    
    
    • 验证码随机生成补充

              <!--RandomString类,用于生成指定长度的随机字符串。需要以下依赖--> 
      <dependency>
                  <groupId>net.bytebuddy</groupId>
                  <artifactId>byte-buddy</artifactId>
              </dependency>
      
      
      
              //RandomString.make(6);
      
      
      

16.springboot安全管理(前后不分离)

1.初始

MVC Security是Spring Boot整合Spring MVC框架搭建的Web应用的安全管理。

WebFlux Security是Spring Boot整合Spring WebFlux框架搭建的Web应用的安全管理。

OAuth2是大型项目的安全管理框架,可以实现第三方认证、单点登录等功能。

Actuator Security用于对项目的一些运行环境提供安全监控,例如Health健康信息、Info运行信息等,它主要作为系统指标供运维人员查看管理系统的运行情况。

•Spring Security的安全管理有两个重要概念,分别是Authentication(认证)和Authorization(授权)。

2.pom依赖

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

3.自定义WebSecurityConfigurerAdapter配置类

@EnableWebSecurity  // 1.开启MVC security安全支持
@EnableGlobalMethodSecurity(securedEnabled = true) // 2.开启控制权限注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
}


注:

@EnableWebSecurity注解是一个组合注解,主要包括@Configuration注解、@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class})注解和@EnableGlobalAuthentication注解

4.认证配置

  • 认证配置
@Autowired
    private UserDetailsServiceImpl userDetailsService;

/** * 重写configure(AuthenticationManagerBuilder auth)方法,进行自定义用户认证 * * @param auth * @throws Exception */@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {    //  密码需要设置编码器    BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();    //UserDetailsService认证处理    auth.userDetailsService(userDetailsService).passwordEncoder(encoder);}

  • 用户认证信息封装
package com.zsc.shixun.service.function;

import com.zsc.shixun.common.ResultCode;
import com.zsc.shixun.config.UserSecurity;
import com.zsc.shixun.entity.User;
import com.zsc.shixun.exception.ApiException;
import com.zsc.shixun.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @Classname UserDetailsServiceImpl
 * @Description 自定义一个UserDetailsService接口实现类进行用户认证信息封装
 * @Date 2019-3-5 16:08
 * @Created by CrazyStone
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private IUserService userService;

    @Override
    public UserDetails loadUserByUsername(String s) {
        // 1.通过业务方法获取用户及权限信息
        User user = userService.getUser(s);
        List<String> authorities = userService.getUserAuthority(s);
        // 2.对用户权限进行封装
        List<SimpleGrantedAuthority> list = authorities.stream().map(authority -> new SimpleGrantedAuthority(authority.toString())).collect(Collectors.toList());
        // 3.返回封装的UserDetails用户详情类
        if (user != null) {
            UserDetails userDetails = new UserSecurity(user, list);//下面的第6点有详细解释

            return userDetails;
        } else {
            4.// 如果查询的用户不存在(用户名不存在),必须抛出此异常
            throw new ApiException(ResultCode.USER_NOT_FOUND);
        }
    }
}



  • 业务方法获取权限及用户信息
/**
     * 获取用户权限
     *
     * @param s
     * @return
     */
public List<String> getUserAuthority(String s) {
        List<String> list = new ArrayList<>(10);
    //取缓存
        Object o = redisTemplate.opsForValue().get(Contant.AUTHORITY_KEY + s);
        if (o != null) {
            list = (List<String>) o;
        } else {
            List<AuthorityVO> authorities = userMapper.getAuthority(s);

            for (AuthorityVO a : authorities
            ) {
                if (a.crudauth.length() != 0) {
                    list.addAll(Arrays.asList(a.getCrudauth().split(",")));
                }
            }
            if (authorities.size() > 0) {
                //存缓存
                redisTemplate.opsForValue().set(Contant.AUTHORITY_KEY + s, list);
            }
        }
        return list;
    }

 /**
     * 获取用户实体
     *
     * @param s
     * @return
     */
@Override
    public User getUser(String s) {
        User user = null;
        //取缓存
        Object o = redisTemplate.opsForValue().get(Contant.MESSAGE_KEY + s);
        if (o != null) {
            user = (User) o;
        } else {
            user = this.getOne(new QueryWrapper<User>().eq("email", s));

            if (user != null) {
                //存缓存
                redisTemplate.opsForValue().set(Contant.MESSAGE_KEY + s, user);
            }
        }
        return user;
    }

5.授权配置

/**
     * 重写configure(HttpSecurity http)方法,进行用户授权管理
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        // 1.定制Remember-me记住我功能
        http.rememberMe()
                .rememberMeParameter("rememberme")
                .tokenValiditySeconds(COOKIE_VALIDITY)
                // 2.对cookie信息进行持久化管理
                .tokenRepository(tokenRepository());
        //3.关闭csrf防护
        http.csrf().disable();
        // 4.自定义用户访问控制
        http.authorizeRequests()
            //5. .permitAll()表示无需授权通过
            //6.。anMatchers()表示捕获指定的请求路径
                .antMatchers("/", "/unAuthorize/**", "/test/**", "/user/**").permitAll()
                .antMatchers("/dist/**", "/docs/**", "/pages/**", "/plugins/**", "/admin/**", "/unauthorize/**", "/deny/**", "/upload/**").permitAll()
            //7.。hasRole()表示指定需要什么权限/角色才可以通过
//                .antMatchers("/admin/**").hasRole("admin")
            //8.表示其余请求需要有权限才可以通过
                .anyRequest().authenticated();
        // 9、自定义用户登录控制
        http.formLogin()
            //10.自定义登录请求页面
                .loginPage("/unAuthorize/login")
            //11.指定用户名及密码
                .usernameParameter("email").passwordParameter("password")
            //12.登录成功之后执行的handler 可自定义fuilter,登录时的逻辑处理及页面跳转
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        String url = httpServletRequest.getParameter("url");
                        System.out.println("之前得url:"+url);
                        // 获取被拦截的原始访问路径
                        RequestCache requestCache = new HttpSessionRequestCache();
                        SavedRequest savedRequest = requestCache.getRequest(httpServletRequest, httpServletResponse);
                        if (savedRequest != null) {
                            // 如果存在原始拦截路径,登录成功后重定向到原始访问路径
                            httpServletResponse.sendRedirect(savedRequest.getRedirectUrl());
                        } else if (url != null && !url.equals("")) {
                            // 跳转到之前所在页面
                            URL fullURL = new URL(url);
                            System.out.println("执行");
                            System.out.println(fullURL.getQuery());
                            System.out.println(fullURL.getPath());
                            if(fullURL.getQuery()!=null)
                            {
                                httpServletResponse.sendRedirect(fullURL.getPath()+"?"+fullURL.getQuery());
                            }else{
                                httpServletResponse.sendRedirect(fullURL.getPath());
                            }

                        } else {
                            // 直接登录的用户,根据用户角色分别重定向到后台首页和前台首页
                            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                            boolean isAdmin = authorities.contains(new SimpleGrantedAuthority("ROLE_admin"));
                            if (isAdmin) {
                                httpServletResponse.sendRedirect("/home/index");
                            } else {
                                httpServletResponse.sendRedirect("/");
                            }
                        }
                    }
                })
            //13.登录失败之后的执行的handler 可自定义fuilter
                .failureUrl("/unAuthorize/login?error");        

        // 14.自定义用户退出控制
        http.logout().logoutUrl("/logout").logoutSuccessUrl("/unAuthorize/index");
        // 15.针对访问无权限页面出现的403页面进行定制处理
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                // 如果是权限访问异常,则进行拦截到指定错误页面
                //返回json形式的错误信息
                httpServletResponse.setCharacterEncoding("UTF-8");
                httpServletResponse.setContentType("application/json");
                //阿里巴巴fastjson  json解析库
                httpServletResponse.getWriter().write(JSON.toJSONString(new CommonResult(ResultCode.AUTHORITY_HAS_EXIST), SerializerFeature.WriteMapNullValue));

                httpServletResponse.getWriter().flush();
            }
        });
    }

6登录信息子类扩展实现登录成功除了能获取用户名、密码、权限外,还可以获取用户其他信息

  • 定义子类继承org.springframework.security.core.userdetails.User;
package com.zsc.shixun.config;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;

import java.util.List;

/**
 * <p>
 * 登录用户封装
 * </p>
 *
 * @author ZWYZY
 * @since 2020/6/14
 */

public class UserSecurity extends User {

    public com.zsc.shixun.entity.User loginUser;

    public UserSecurity(com.zsc.shixun.entity.User user, List<SimpleGrantedAuthority> authorities) {
        super(user.getUsername(), user.getPassword(), true, true, true, true, authorities);
        this.loginUser = user;
    }

    public com.zsc.shixun.entity.User getLoginUser() {
        return loginUser;
    }

    public void setLoginUser(com.zsc.shixun.entity.User loginUser) {
        this.loginUser = loginUser;
    }
}


  • 用户信息工具类----------自定义获取登录信息类及方法供我们调用
package com.zsc.shixun.utils;

import com.zsc.shixun.config.UserSecurity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

/**
 * <p>
 *
 * </p>
 *
 * @author ZWYZY
 * @since 2020/6/15
 */
@Service
public class LoginMessageUtils {
    /**
     * 根据登录后获取登录信息
     *
     * @return
     */
    public UserSecurity getMessage() {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        UserSecurity principal = (UserSecurity) authentication.getPrincipal();
        return principal;
    }

    /**
     * 动态更新登录信息
     *
     * @param userDetails
     */
    public void setMessage(UserDetails userDetails) {
        SecurityContextHolder.getContext().setAuthentication(
                new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()));
    }


}


7.控制器安全权限注解

具体配置见第2点

控制器添加**@Secured**

@Secured(value = "ROLE_article_select")

8.security控制前端页面(以thymeleaf为例)

  • pom文件引入依赖
<!-- thymeleaf模板整合security控制页面安全访问依赖 -->

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

  • thymeleaf引入标签

     xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
    
    
    
  • 用法如下

    • sec:authorize="isAuthenticated()"
      
      

    表示用户认证通过才可以显示的html元素

    • sec:authorize="hasRole('ROLE_admin')"
      
      

    表示用户必须具有指定权限才可以显示

    • sec:authorize="isAnonymous()"
      
      

    表示用户未认证前显示的,认证通过后不显示

    • th:src="@{${#authentication.principal.loginUser.img}}"
      
      
    • th:text="${#authentication.principal.username}"
      
      

    结合thymeleaf来显示用户登录成功后的其他信息

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值