Spring Boot 2.5.0
Spring Boot 2.5.0
SSM环境搭建
spring
springmvc
mybatis
spring springmvc mybatis 简单功能 员工添加 查询 所有
1 2 3 4 5 6 7 8 9 # 项目 - 需求分析 概要设计(库表设计) 详细设计(验证库表正确性) 编码(环境搭建+业务代码) 测试 部署上线# 员工添加 查询所有功能 SSM - 库表 库: ssm 数据库:mysql 表: id name birthday salary# 编码 环境搭建 ssm spring springmvc 一个团队开发 无缝整合 - springmvc spring mybatis
引入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.11</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-core</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-beans</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-aop</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context-support</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-web</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.3.6</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.4</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.38</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.6</version > </dependency > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis-spring</artifactId > <version > 2.0.6</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.9.3</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjweaver</artifactId > <version > 1.9.5</version > </dependency > <dependency > <groupId > org.aspectj</groupId > <artifactId > aspectjrt</artifactId > <version > 1.9.5</version > </dependency > </dependencies >
创建spring配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <context:component-scan base-package ="com.baizhi.service" > <context:exclude-filter type ="aspectj" expression ="com.baizhi.controller..*" /> </context:component-scan > <bean id ="dataSource" class ="com.alibaba.druid.pool.DruidDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/ssm?characterEncoding=UTF-8" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </bean > <bean id ="sqlSessionFactory" class ="org.mybatis.spring.SqlSessionFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="mapperLocations" value ="classpath:com/baizhi/mapper/*.xml" /> <property name ="typeAliasesPackage" value ="com.baizhi.entity" /> </bean > <bean class ="org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name ="sqlSessionFactoryBeanName" value ="sqlSessionFactory" /> <property name ="basePackage" value ="com.baizhi.dao" /> </bean > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean > <tx:annotation-driven transaction-manager ="transactionManager" />
创建springmvc配置 1 2 3 4 5 6 7 8 9 10 11 <context:component-scan base-package ="com.baizhi.controller" /> <mvc:annotation-driven /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" > <property name ="prefix" value ="/" /> <property name ="suffix" value =".jsp" /> </bean >
配置web.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <context-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:spring.xml</param-value > </context-param > <listener > <listener-class > org.springframework.web.context.ContextLoaderListener</listener-class > </listener > <servlet > <servlet-name > springmvc</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc.xml</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > springmvc</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping >
现有SSM开发存在问题
大量maven冗余配置
每次构建项目都要书写大量相同配置极大浪费了项目开发时间
每次整合第三方技术都需要编写相关配置文件
项目测试每次都需要部署到tomcat
注意:这就是早期的SSM或者SSH开发存在问题,是不是很麻烦☹️☹️☹️
SpringBoot的引言 Spring Boot是由Pivotal团队提供的全新框架
,其设计目的是用来简化Spring应用的 初始搭建以及开发过程
。该框架使用了特定的方式来进行配置
,从而使开发人员不 再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应 用开发领域(rapid application development)成为领导者。
Spring Boot 全新框架 作用: 简化spring应用初始搭建和开发过程
如何简化: 开发人员使用springboot只要基于特定方式进行配置 简化spring使用
SpringBoot 微框架: 5分钟 完成之前ssm中环境
springboot(微框架) = springmvc(控制器) + spring core(项目管理)
SpringBoot的优势
创建完整的独立的Spring应用程序
spring springmvc 只有一个容器
嵌入的Tomcat,无需部署WAR文件
springboot 内嵌tomcat 应用跑在内嵌服务器
简化Maven配置,自动配置Spring Springmvc,没有XML配置
几个依赖
用来springboot spring应用在无xml
SpringBoot的约定
springboot 项目中必须在src/main/resources中放入application.yml(.properties)核心配置文件 名字必须为:application
环境搭建 环境要求 1 2 3 4 5 6 7 8 9 10 # 1.System Requirements JDK1.8+ MAVEN3.2+ Spring Framework 5.x+ # 2.ServletContainers: Tomcat 9.0+ # 3.开发工具 IDEA 2021版本
新建项目中引入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.5.0</version > </parent > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > </dependencies >
引入配置文件 项目中 src/main/resources/application.yml
编写入口类 1 2 3 4 5 6 7 8 9 10 11 12 @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class,args); } }
运行main启动项目 1 2 3 4 o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port (s) : 8080 (http) com.baizhi.Application : Started Application in 2.152 seconds (JVM running for 2.611 )
注意:到这里项目环境已经搭建成功了,看看仅仅需要5分钟😄😁😁
建包并创建控制器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Controller @RequestMapping("/hello") public class HelloController { @RequestMapping("/hello") @ResponseBody public String hello () { System.out.println("======hello world=======" ); return "hello" ; } }
访问项目 1 2 3 4 5 6 /* * 原来的测试地址:http://localhost:端口号/项目名/请求路径 * springboot项目启动没有项目名:http://localhost:端口号/请求路径 * */ # 注意: springboot的项目启动默认项目名 - 访问路径: http://localhost:8080/hello/hello
修改内嵌服务器端口
修改应用(项目)名称 1 2 3 server: servlet: context-path: /springboot_day1
相关注解说明 1 2 3 4 5 6 7 8 # 入口类 SpringApplication - SpringBootApplication: 全局入口类 有且只能有一个- mian 函数参数可以在启动时指定jvm参数覆盖默认配置# @SpringBootApplication 注解等价于: - @SpringBootConfiguration 标识这是一个springboot的配置类,默认自定配置Spring环境- @EnableAutoConfiguration 自动与项目中集成的第三方技术进行集成- @ComponentScan 扫描入口类所在子包以及子包后代包中注解
1 2 3 4 5 6 7 8 9 /* * @SpringBootApplication:(组合注解)* 组合注解:由多个注解组合而成的* 元注解:jdk提供的用来修饰注解的注解 @Target:指定注解范围 @Retention:指定注解什么时候有效* *@SpringBootConfiguration:这个注解就是用来自动配置spring、springmvc(初始化、servlet...)相关环境 @EnableAutoConfiguration:开启自动配置,自动配置核心注解,自动配置spring相关环境,自动与项目中第三方技术进行自动配置其环境 @ComponentScan: 组件扫描 默认扫描当前包和其子包。 * */
配置文件 配置文件的拆分 说明: 在实际开发过程中生产环境和测试环境有可能是不一样的 因此将生产中的配置和测试中的配置拆分开,是非常必要的在springboot中也提供了配置文件拆分的方式. 这里以生产中项名名称不一致为例:
生产中项目名为: springboot
测试中项目名为: springboot_day1
端口同时为: 8080
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - application.yml server: port: 8080 - application-pord.yml server: context-path: /cmfz - application-dev.yml server: context-path: /springboot spring: profiles: active: dev
启动指定配置文件 说明: 往往在企业级开发过程中为SpringBoot应用启动时指定一个完整外部配置 也是经常用到的,在SpringBoot中也提供这个方式来启动项目如:
工厂创建对象 创建单个对象 在springboot中可以管理单个对象可以直接使用spring框架中注解形式创建。
@Component
通用的对象创建注解
@Controller
用来创建控制器对象
@Service
用来创建业务层对象
@Repository
用来创建DAO层对象
以上注解都有value属性,value属性用来指定工厂中对象名称
1 2 3 4 @Service public class DemoServiceImpl implements UserService { }
1 2 3 4 5 6 7 @Controller @RequestMapping("hello") public class HelloController { @Autowired private DemoService demoService; }
创建多个对象 如何在springboot中像spring框架一样通过xml创建多个对象,在SpringBoot中也提供了相同注解如@Configuration + @Bean
注解进行创建
@Configuration
代表这是一个spring的配置类相当于Spring.xml配置文件
@Bean
用来在工厂中创建这个@Bean注解标识的对象
默认使用@Bean创建对象在工厂中唯一标识为方法名称
修改在工厂中对象标识可以在使用@Bean(“工厂中名字”)指定一个名字
1 2 3 4 5 6 7 @Configuration public class Beans { @Bean public Calendar calendar () { return Calendar.getInstance(); } }
1 2 3 4 5 6 7 @Controller @RequestMapping("hello") public class HelloController { @Autowired private Calendar calendar; ...... }
1 2 3 # 注意: 1.@Configuration 用来在工厂中一次性创建多个对象 2.@Component 用来创建单个对象
属性注入 基本属性注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @RestController public class HelloController { @Value("${name}") private String name; @Value("${age}") private Integer age; @Value("${sex}") private Boolean sex; @Value("${price}") private Double price; @Value("${bir}") private Date bir; @Value("${qqs}") private String[] qqs; @Value("${lists}") private List<String> lists; @Value("#{${maps}}") private Map<String,String> maps; }
1 2 3 4 5 6 7 8 9 10 name: 小陈 age: 23 price: 23.23 sex: true bir: 2012 /12/12 qqs: 123 ,3434,3434 lists: xiaochen,xiaoming,xiaosan maps: "{'aa':'1234','bb':'2344'}"
对象方式注入 注意: 这种方式必须提供SET方法
1 # 1. @ConfigurationProperties(prefix="前缀")
1 2 3 4 5 6 7 8 9 @Component @ConfigurationProperties(prefix = "user") public class User { private String id; private String name; private Integer age; private String bir; }
1 2 3 4 5 user: id: 24 name: xiaohei age: 23 bir: 2012 /12/12
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency >
JSP模板集成 在SpringBoot框架中默认模板推荐使用Thymeleaf模板,这里我们优先讲与JSP模板集成
引入jsp的集成jar包 1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > jstl</groupId > <artifactId > jstl</artifactId > <version > 1.2</version > </dependency > <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-jasper</artifactId > </dependency >
引入jsp运行插件 1 2 3 4 5 6 7 8 9 10 <build > <finalName > springboot_day1</finalName > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build >
配置视图解析器 1 2 3 4 5 6 spring: mvc: view: prefix: / suffix: .jsp
第一种方式使用插件启动访问JSP页面
第二种方式使用idea中指定工作目录启动 访问JSP
启动项目测试 1 http://localhost:8989/index.jsp
修改jsp无须重启应用 1 2 3 4 5 server: servlet: jsp: init-parameters: development: true
整合Mybatis框架 引入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.4</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 5.1.38</version > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > 2.1.4</version > </dependency >
配置配置文件 1 2 3 4 5 6 7 8 9 10 11 spring: mvc: view: prefix: / suffix: .jsp datasource: type: org.apache.commons.dbcp.BasicDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm?characterEncoding=UTF-8 username: root password: root
加入mybatis配置 1 2 3 4 5 mybatis: mapper-locations: classpath:com/baizhi/mapper/*.xml type-aliases-package: com.baizhi.entity
1 2 3 4 5 6 7 8 @SpringBootApplication @MapperScan("com.baizhi.dao") public class Application { public static void main (String[] args) { SpringApplication.run(Application.class,args); } }
建表 1 2 3 4 5 6 CREATE TABLE `t_clazz` ( `id` varchar (40 ) NOT NULL , `name` varchar (80 ) DEFAULT NULL , `no ` varchar (90 ) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8;
开发实体类 1 2 3 4 5 6 public class Clazz { private String id; private String name; private String no; }
开发DAO接口以及Mapper 1 2 3 public interface ClazzDAO { List<Clazz> findAll () ; }
1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.baizhi.dao.ClazzDAO" > <select id ="findAll" resultType ="Clazz" > select * from t_clazz </select > </mapper >
开发Service以及实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface ClazzService { List<Clazz> findAll () ; } @Service @Transactional public class ClazzServiceImpl implements ClazzService { @Autowired private ClazzDAO clazzDAO; @Transactional(propagation = Propagation.SUPPORTS) @Override public List<Clazz> findAll () { return clazzDAO.findAll(); } }
开发Controller 1 2 3 4 5 6 7 8 9 10 11 @RestController public class ClazzController { @Autowired private ClazzService clazzService; @RequestMapping("findAll") public List<Clazz> findAll () { return clazzService.findAll(); } }
启动项目访问测试 1 http://localhost:8989/项目名/findAll
本地测试
往往在开发过程中业务代码课程非常复杂频繁启动服务器测试,非常麻烦!这个时候使用本地测试就是一个很好的解决方案,springboot也提供了本地测试解决方案!
引入测试依赖 1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency >
编写测试类
@SpringBootTest
修饰范围: 用在类上
作用: 用来启动本地Spring环境
1 2 3 4 5 6 7 8 9 10 11 @SpringBootTest public class TestEmpService { @Autowired private EmpService empService; @Test public void test () { empService.findAll().forEach(emp-> System.out.println(emp)); } }
热部署工具 为了进一步提高开发效率,springboot为我们提供了全局项目热部署,日后在开发过程中修改了部分代码以及相关配置文件后,不需要每次重启使修改生效,在项目中开启了springboot全局热部署之后只需要在修改之后等待几秒即可使修改生效。
开启热部署 项目中引入依赖 (每次搭建项目都需要引入) 1 2 3 4 5 6 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <optional > true</optional > </dependency >
设置idea中支持自动编译(只需要设置一次) 1 2 3 4 5 6 # 1.开启自动编译 Preferences | Build, Execution, Deployment | Compiler -> 勾选上 Build project automatically 这个选项 # 2.开启允许在运行过程中修改文件 ctrl + alt + shift + / ---->选择1.Registry ---> 勾选 compiler.automake.allow.when.app.running 这个选项
启动项目检测热部署是否生效
1 2 3 4 5 6 7 2019 -07 -17 21 :23 :17 .566 INFO 4496 --- [ restartedMain] com.baizhi .InitApplication : Starting InitApplication on chenyannandeMacBook-Pro.local with PID 4496 (/Users/chenyannan/IdeaProjects/ideacode/springboot_day1/target/classes started by chenyannan in /Users/chenyannan/IdeaProjects/ideacode/springboot_day1)2019 -07 -17 21 :23 :17 .567 INFO 4496 --- [ restartedMain] com.baizhi .InitApplication : The following profiles are active: dev2019 -07 -17 21 :23 :17 .612 INFO 4496 --- [ restartedMain] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework .boot .context .embedded .AnnotationConfigEmbeddedWebApplicationContext @66 d799c5: startup date [Wed Jul 17 21 :23 :17 CST 2019 ]; root of context hierarchy2019 -07 -17 21 :23 :18 .782 INFO 4496 --- [ restartedMain] s.b .c .e .t .TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8989 (http)2019 -07 -17 21 :23 :18 .796 INFO 4496 --- [ restartedMain] o.apache .catalina .core .StandardService : Starting service [Tomcat]2019 -07 -17 21 :23 :18 .797 INFO 4496 --- [ restartedMain] org.apache .catalina .core .StandardEngine : Starting Servlet Engine: Apache Tomcat/8 .5 .20
注意:日志出现restartedMain代表已经生效,在使用热部署时如果遇到修改之后不能生效,请重试重启项目在试
日志处理 引言 springboot框架 集成日志 logback 日志
Logback是由log4j 创始人设计的又一个开源日志组件。目前,logback分为三个模块:logback-core,logback-classic和logback-access。是对log4j日志展示进一步改进!
总结: logback 也是一个开源日志组件 和 log4j作用一致 都是用来生成日志 logback更加轻量
日志的级别
1 2 3 4 5 6 7 8 9 10 11 12 > All < Trace < `DEBUG < INFO < WARN < ERROR` < Fatal < OFF - OFF | 关闭:最高级别,不打印日志。 - FATAL | 致命:指明非常严重的可能会导致应用终止执行错误事件。- ERROR | 错误:指明错误事件,但应用可能还能继续运行。 - WARN | 警告:指明可能潜在的危险状况。 - INFO | 信息:指明描述信息,从粗粒度上描述了应用运行过程。 - DEBUG | 调试:指明细致的事件信息,对调试应用最有用。- TRACE | 跟踪:指明程序运行轨迹,比DEBUG级别的粒度更细。 - ALL | 所有:所有日志级别,包括定制级别。> 日志级别由低到高: `日志级别越高输出的日志信息越多`
项目中日志分类 1 2 3 4 # 日志分类: - 一种是rootLogger(根全局日志) : 用来监听项目中所有的运行日志 包括引入依赖jar中的日志 - 一种是logger(指定包级别日志) : 用来监听项目中指定包中的日志信息
配置日志 注意:SpringBoot框架中默认根日志为INFO
1 2 3 4 5 6 7 logging: level: root: debug com.baizhi.dao: debug file: name: aaa.log path: ./
项目中使用日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller public class HelloController { private static final Logger log = LoggerFactory.getLogger(HelloController.class); @RequestMapping("/hello") @ResponseBody public String hello () { System.out.println("======hello world=======" ); logger.debug("DEBUG,{}" ,"信息" ); logger.info("INFO,{}" ,"信息" ); logger.warn("WARN,{}" ,"信息" ); logger.error("ERROR,{}" ,"信息" ); return "hello" ; } }
切面编程 引言
springboot是对原有项目中spring框架和springmvc的进一步封装,因此在springboot中同样支持spring框架中AOP切面编程,不过在springboot中为了快速开发仅仅提供了注解方式的切面编程.
使用 引入依赖 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-aop</artifactId > </dependency >
相关注解 1 2 3 4 5 # 切面注解 - @Aspect 用来类上,代表这个类是一个切面- @Before 用在方法上代表这个方法是一个前置通知方法 - @After 用在方法上代表这个方法是一个后置通知方法 @Around 用在方法上代表这个方法是一个环绕的方法- @Around 用在方法上代表这个方法是一个环绕的方法
前置切面 1 2 3 4 5 6 7 8 9 10 11 @Aspect @Configuration public class MyAspect { @Before("execution(* com.baizhi.service.*.*(..))") public void before (JoinPoint joinPoint) { System.out.println("前置通知" ); joinPoint.getTarget(); joinPoint.getSignature(); joinPoint.getArgs(); } }
后置切面 1 2 3 4 5 6 7 8 9 10 11 @Aspect @Configuration public class MyAspect { @After("execution(* com.baizhi.service.*.*(..))") public void before (JoinPoint joinPoint) { System.out.println("后置通知" ); joinPoint.getTarget(); joinPoint.getSignature(); joinPoint.getArgs(); } }
注意: 前置通知和后置通知都没有返回值,方法参数都为joinpoint
环绕切面 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Aspect @Configuration public class MyAspect { @Around("execution(* com.baizhi.service.*.*(..))") public Object before (ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("进入环绕通知" ); proceedingJoinPoint.getTarget(); proceedingJoinPoint.getSignature(); proceedingJoinPoint.getArgs(); Object proceed = proceedingJoinPoint.proceed(); System.out.println("目标方法执行之后回到环绕通知" ); return proceed; } }
注意: 环绕通知存在返回值,参数为ProceedingJoinPoint,如果执行放行,不会执行目标方法,一旦放行必须将目标方法的返回值返回,否则调用者无法接受返回数据
切入点表达式·:
1.execution:方法级别的切入点表达式;
@Around(“execution( com.baizhi.service. .*(..))”)
2.within:类级别的切入点表达式;
@Around(“within( com.baizhi.service. “)
3.@annotation:基于注解的切入点表达式
文件上传下载 文件上传 用户访问当前系统,将自己本地计算机中文件通过浏览器上传到当前系统所在的服务器过程中称之为文件的上传
文件上传: 用户将自己计算机中文件 上传到 项目所在服务器过程、文件服务器、OSS 称之为文件上传
添加依赖 1 2 3 4 5 <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-jasper</artifactId > </dependency >
准备上传页面 1 2 3 4 5 6 7 8 9 <form action="路径...." method="post" enctype="multipart/form-data" > <input type="file" name="aa" > <input type="submit" value="上传" > </form> <!-- 1. 表单提交方式必须是post 2. 表单的enctype属性必须为multipart/form-data 3. 后台接受变量名字要与文件选择name属性一致 -->
编写控制器 1 2 3 4 5 6 7 8 9 10 @Controller @RequestMapping("/file") public class FileController { @RequestMapping("/upload") public String upload (MultipartFile aa, HttpServletRequest request) throws IOException { String realPath = request.getRealPath("/upload" ); aa.transferTo(new File (realPath,aa.getOriginalFilename())); return "index" ; } }
修改文件上传大小 1 2 3 4 5 6 7 8 9 10 11 nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (38443713) exceeds the configured maximum (10485760) spring: http: multipart: max-request-size: 209715200 max-file-size: 209715200 spring.servlet.multipart.max-file-size=500MB spring.servlet.multipart.max-request-size=500MB
文件下载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @Slf4j @Controller @RequestMapping("/file") public class FileController { @Value("${file.download.dir}") private String realPath; @RequestMapping("download") public void download (String fileName, HttpServletResponse response) throws IOException { log.debug("下载的文件名为:{}" ,fileName); log.debug("文件下载的目录为:{}" ,realPath); File file = new File (realPath,fileName); FileInputStream inputStream = new FileInputStream (file); response.setHeader("content-disposition" ,"attachment;fileName=" + URLEncoder.encode(fileName,"UTF-8" )); ServletOutputStream outputStream = response.getOutputStream(); FileCopyUtils.copy(inputStream,outputStream); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 server: servlet: context-path: /springboot04 jsp: init-parameters: development: true spring: mvc: view: prefix: / suffix: .jsp logging: level: com.meng: debug file: download: dir: D:\idea\springboot04\download
1 2 3 4 5 <body > <h1 > 测试文件下载</h1 > <a href ="${pageContext.request.contextPath}/file/download?fileName=HELP.md" > HELP.md</a > <br > <a href ="${pageContext.request.contextPath}/file/download?fileName=项目介绍.txt" > 项目介绍.txt</a > <br > </body >
文件下载: 将服务器某个资源文件下载到用户本地计算机过程称之为文件下载
提供下载文件链接 1 <a href ="../file/download?fileName=corejava.txt" > corejava.txt</a >
开发控制器 1 2 3 4 5 6 7 8 9 10 @RequestMapping("/download") public void download (String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception { String realPath = request.getRealPath("/upload" ); FileInputStream is = new FileInputStream (new File (realPath, fileName)); ServletOutputStream os = response.getOutputStream(); response.setHeader("content-disposition" ,"attachment;fileName=" + URLEncoder.encode(fileName,"UTF-8" )); IOUtils.copy(is,os); IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); }
拦截器 开发拦截器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { System.out.println("======1=====" ); return true ; } @Override public void postHandle (HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception { System.out.println("=====2=====" ); } @Override public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception { System.out.println("=====3=====" ); } }
配置拦截器 1 2 3 4 5 6 7 8 9 10 @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors (InterceptorRegistry registry) { registry.addInterceptor("拦截器" ) .addPathPatterns("拦截路径" ) .excludePathPatterns("排除路径" ) .order("指定执行顺序" ) } }
注意:order用来执行多个拦截器的执行顺序,order书写是自然数,按照自然数顺序执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 拦截器: 拦截器的底层是aop。 1.创建interceptor包,在包中自定义拦截器类,该类需要implements HandlerInterceptor接口 2.重写接口的方法,三个方法安需要重写,方法体中可以写需要实现的功能; 3.创建Config包,自定义拦截器的配置类,配置类需要加@Configuration,配置类implements WebMvcConfigurer接口,重写接口中关于拦截器的方法addInterceptors(),通过方法的参数registry调用addInterceptor()方法进行设置需要配置的拦截器, addPathPatterns()方法可以配置拦截的路径,excludePathPatterns()方法可以配置拦截时排除哪些路径, order()方法可以在多个拦截器生效时,对拦截器的执行顺序排序。 多个拦截器的执行顺序:如有两个拦截器:拦截器1和拦截器2 拦截器中有三个方法: 1.preHandle() 2.postHandle() 3.afterCompletion() 1.如果在配置类中没有设置order(),则按配置顺序(拦截器1、拦截器2)执行。 1.1由于拦截器的三个方法执行顺序不同,所以,先执行拦截器1的preHandle方法,在执行拦截器2的preHandle,然后执行controller类等执行拦截器2的postHandle,再执行拦截器1的postHandle,最后执行拦截器2的afterCompletion,再执行拦截器1的afterCompletion。 2.如果在配置类中设置了order(),则按order中数字的自然顺序执行,数字相同按第一种方式执行。后续的执行则和1.1中的一样
war包部署 设置打包方式为war 在pom.xml文件中:
war
在插件中指定入口类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <fork > true</fork > <jvmArguments > -Dfile.encoding=UTF-8</jvmArguments > <mainClass > com.baizhi.Application</mainClass > </configuration > </plugin > </plugins > </build >
排除内嵌的tomcat 1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-tomcat</artifactId > <scope > provided</scope > </dependency > <dependency > <groupId > org.apache.tomcat.embed</groupId > <artifactId > tomcat-embed-jasper</artifactId > <scope > provided</scope > </dependency >
配置入口类 1 2 3 4 5 6 7 8 9 10 11 public class Application extends SpringBootServletInitializer { public static void main (String[] args) { SpringApplication.run(Application.class,args); } @Override protected SpringApplicationBuilder configure (SpringApplicationBuilder builder) { return builder.sources(Application.class); } }
打包测试 1 2 3 # 一旦使用war包部署注意: - 1. application.yml 中配置port context-path 失效- 2. 访问时使用打成war包的名字和外部tomcat端口号进行访问项目
Jar包部署 设置打包方式为jar
jar
注意:默认方式也是jar
执行打包
测试访问
注意:springboot部署jsp时,插件版本必须指定为1.4.2版本,并进行jsp打包配置才可以,其他版本均不支持!!!
修改插件版本
在项目的pom.xml配置文件中build标签中修改
1 2 3 4 5 6 7 8 <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > 1.4.2.RELEASE</version > </plugin > </plugins >
指定jsp打包配置
在项目的pom.xml配置文件中build标签中加入配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <resources > <resource > <directory > src/main/webapp</directory > <targetPath > META-INF/resources</targetPath > <includes > <include > **/**</include > </includes > </resource > <resource > <directory > src/main/resources</directory > <includes > <include > **/**</include > </includes > <filtering > false</filtering > </resource > </resources >
重新打包测试即可
Thymeleaf
Thymeleaf是一个用于web和独立环境的现代服务器端Java模板引擎。
—摘自官网https://www.thymeleaf.org/
Thymeleaf
是跟Velocity、FreeMarker类似的模板引擎,它可以完全替代JSP,相较与其他的模板引擎相比, Thymeleaf在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。
集成Thymeleaf模板 引入依赖 1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-thymeleaf</artifactId > </dependency >
编写配置 1 2 3 4 5 spring: thymeleaf: cache: false prefix: classpath:/templates/ suffix: .html
编写控制器测试 1 2 3 4 5 6 7 8 9 10 @Controller @RequestMapping("hello") public class HelloController { @GetMapping("hello") public String hello () { System.out.println("测试与 thymeleaf 的集成" ); return "index" ; } }
在templates目录中定义模板
测试访问 1 http://localhost:8989/springboot_day3/hello/hello
查看结果
模板基本语法 1 2 使用时必须在页面中加入thymeleaf如下命名空间: <html lang="en" xmlns:th="http://www.thymeleaf.org">
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.meng.controller;import com.meng.entity.User;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;import java.util.ArrayList;import java.util.Arrays;import java.util.Date;import java.util.List;@Controller @RequestMapping("demo") public class DemoController { @RequestMapping("demo") public String demo (HttpServletRequest request, Model model, HttpSession session) { System.out.println("demo ok" ); String name="李思思" ; Integer age=24 ; String content="<a href='http://www.baidu.com'>百度一下</a>" ; request.setAttribute("name" ,name); model.addAttribute("age" ,age); model.addAttribute("content" ,content); User user = new User (1 ,"李思思" ,8900.00 ,new Date ()); request.setAttribute("user" ,user); List<User> users= Arrays.asList(new User (2 ,"李萌萌" ,8500.00 ,new Date ()), new User (3 ,"李小小" ,8500.00 ,new Date ()), new User (4 ,"李陌陌" ,8500.00 ,new Date ())); model.addAttribute("users" ,users); session.setAttribute("xxname" ,"梨花" ); return "demo" ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <!DOCTYPE html > <html lang ="en" xmlns:th ="www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > thymeleaf</title > </head > <body > <h1 > thymeleaf</h1 > <h4 > 获取单个数据:<span th:text ="${name}" > </span > </h4 > <h4 > 获取单个数据:<span th:text ="${age}" > </span > </h4 > <h4 > 获取单个数据:<span th:utext ="${content}" > </span > </h4 > <input type ="text" name ="username" th:value ="${name}" > <input type ="text" name ="age" th:value ="${age}" > <h4 > 获取对象类型的数据:<br > id:<span th:text ="${user.id}" /> <br > name:<span th:text ="${user.name}" /> <br > salary:<span th:text ="${user.salary}" /> <br > birthday:<span th:text ="${#dates.format(user.birthday,'yyyy/MM/dd HH:mm:ss')}" /> <br > </h4 > <h4 > 获取集合类型数据:</h4 > <ul > <li th:each ="user,state:${users}" > state count:<span th:text ="${state.count}" /> state odd:<span th:text ="${state.odd}" /> state even:<span th:text ="${state.even}" /> state size:<span th:text ="${state.size}" /> id:<span th:text ="${user.id}" /> name:<span th:text ="${user.id}" /> salary:<span th:text ="${user.id}" /> birthday:<span th:text ="${#dates.format(user.birthday,'yyyy-MM-dd HH:mm:ss')}" /> </li > </ul > <h4 > 有条件的展示数据</h4 > <div style ="width: 100px;height: 100px; background: deepskyblue;" th:if ="${age>=25}" > 我是deepskyblue </div > <div style ="width: 100px;height: 100px; background: antiquewhite;" th:if ="${age>=23}" > 我是antiquewhite </div > <h4 > 获取session域中的数据</h4 > <h4 > <span th:text ="${session.xxname}" > </span > </h4 > </body > </html >
展示单个数据 设置数据 1 model.addAttribute("name" ,"张三" ); 或 request.setAttribute("name" ,"小黑" );
获取数据 1 <span th:text ="${name}" /> --->获取数据
解析含有html标签数据 1 2 model.addAttribute("name" ,"<a href=''>张三</a>" ); model.addAttribute("username" ,"小陈" );
1 <span th:text ="${name}" />
1 <span th:utext ="${name}" />
将数据赋值给表单元素
1 <input type ="text" th:value ="${username}" />
1 2 3 4 # 总结 - 1.使用 th:text="${属性名}" 获取对应数据,获取数据时会将对应标签中数据清空,因此最好是空标签- 2.使用 th:utext="${属性名}" 获取对应的数据,可以将数据中html先解析在渲染到页面- 3.使用 th:value="${属性名}" 获取数据直接作为表单元素value属性
展示对象数据 1 2 3 4 5 model.addAttribute("user",new User("21","xiaochen",23,new Date())); id:<span th:text ="${user.id}" > </span > name:<span th:text ="${user.name}" > </span > age:<span th:text ="${user.age}" > </span > bir: <span th:text ="${user.bir}" > </span > ==== <span th:text ="${#dates.format(user.bir, 'yyyy-MM-dd HH:mm')}" > </span > 日期格式化
条件展示数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 model.addAttribute("user",new User("21","xiaochen",23,new Date())); <span th:if ="${user.age} eq 23" > 青年 </span > <span th:if ="${user.age == 23}" > 青年 </span > # 运算符 gt:great than(大于)> ge:great equal(大于等于)>= eq:equal(等于)== lt:less than(小于)< le:less equal(小于等于)<= ne:not equal(不等于)!=
展示多条数据
1 2 3 4 5 6 7 <ul th:each ="user:${users}" > <li th:text ="${user.id}" > </li > <li th:text ="${user.name}" > </li > <li th:text ="${user.age}" > </li > <li th:text ="${#dates.format(user.bir,'yyyy-MM-dd')}" > </li > </ul >
1 2 3 4 5 6 <ul th:each ="user,userStat:${users}" > <li > <span th:text ="${userStat.count}" /> -<span th:text ="${user.id}" /> </li > 获取遍历次数 count 从1开始 index 从0开始 <li > <span th:text ="${userStat.odd}" /> -<span th:text ="${user.name}" /> </li > 获取当前遍历是否是奇数行 <li > <span th:text ="${userStat.even}" /> -<span th:text ="${user.age}" /> </li > 获取当前遍历是否是偶数行 <li > <span th:text ="${userStat.size}" /> -<span th:text ="${user.bir}" /> </li > 获取当前集合的总条数 </ul >
引入静态资源 1 # 使用thymeleaf模板项目中静态资源默认放在resources路径小static目录中
在js代码中获取项目名 1 2 3 <script> const ctx = '[[@{/}]]' ; </script>
注意:[[书写thymeleaf语法]],这里[[]]是thymeleaf内嵌表达式
RestFul 引言
REST全称是Representational State Transfer
,中文意思是表述(编者注:通常译为表征)性状态转移。 它首次出现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP规范的主要编写者之一。 他在论文中提到:”我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。REST指的是一组架构约束条件和原则 。” 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。
RestFul: 是一种以网络为基础构架的一种架构风格 ,一个架构符合Rest设计原则和约束成这个架构为RestFul。
Rest 词: 没有更新技术、组件、服务 ,只是为了让我们web请求能够利用web中标准 和 能力 更好描述架构
REST本身并没有创造新的技术、组件或服务,而隐藏在RESTful背后的理念就是使用Web的现有特征和能力, 更好地使用现有Web标准中的一些准则和约束。虽然REST本身受Web技术的影响很深, 但是理论上REST架构风格并不是绑定在HTTP上,只不过目前HTTP是唯一与REST相关的实例。 所以我们这里描述的REST也是通过HTTP实现的REST。
总结
Restful 一种软件架构风格、设计风格,而不是 标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
web开发: 实现所有功能
restful: 软件设计风格 标准 简洁 层次 优雅
rest设计原则 和 约束架构称之为restFul
URL定义
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST(添加)、DELETE(删除)、PUT(修改)、GET(查询),使用不同请求方法对资源进行操作。
删除 delete
查询 get
添加 post
修改 put (修改全部字段)| patch(更新部分字段)
传统方式操作资源
http://127.0.0.1/item/queryUser.action?id=1 查询,GET
http://127.0.0.1/item/saveUser.action 新增,POST
http://127.0.0.1/item/updateUser.action 更新,PUT
http://127.0.0.1/item/deleteUser.action?id=1 删除,DELETE
注意: 传统的操作是没有问题的,大神认为是有问题的,有什么问题呢?你每次请求的接口或者地址,都在做描述,例如查询的时候用了queryUser,新增的时候用了saveUser ,修改的时候用了updateUser,其实完全没有这个必要,我使用了get请求,就是查询.使用post请求,就是新增的请求,PUT就是修改,delete就是删除,我的意图很明显,完全没有必要做描述,这就是为什么有了restful.
使用RESTful操作资源
【PUT】 /users/1001 # 更新用户信息(全部字段)
【PATCH】 /users/1001 # 更新用户信息(部分字段)
【DELETE】 /users/1001 # 删除用户信息
Rest API设计风格原则 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 # 1.使用名词而不是动词 - 不要使用: 如: /getAllUsers get /users get /users/002 /createNewUser post /users /deleteAllUser delete /users/001 # 2.Get方法和查询参数不应该涉及状态改变 - 使用PUT, POST 和DELETE 方法 而不是 GET 方法来改变状态,不要使用GET 进行状态改变# 3.使用复数名词 - 不要混淆名词单数和复数,为了保持简单,只对所有资源使用复数。 如: /cars 而不是 /car /users 而不是 /user /products 而不是 /product /settings 而不是 /setting /orders 而不是 /order # 4. 使用子资源表达关系 - 如果一个资源与另外一个资源有关系,使用子资源: 如: GET /cars/711/drivers/ 返回 car 711的所有司机 GET /cars/711/drivers/4 返回 car 711的4号司机 GET /users/11/pets 返回 user 11的所有宠物 GET /users/11/pets/2 返回 user 11的2号宠物 # 5.使用Http头声明序列化格式 - 在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定 如: Content-Type 定义请求格式 Accept 定义系列可接受的响应格式 # 6.为集合提供过滤 排序 选择和分页等功能 - Filtering过滤:使用唯一的查询参数进行 GET /cars?color=red 返回红色的cars GET /cars?seats<=2 返回小于两座位的cars集合 - Sorting排序:允许针对多个字段排序 GET /cars?sort=-manufactorer,+model 这是返回根据生产者降序和模型升序排列的car集合 - Field selection 移动端能够显示其中一些字段,它们其实不需要一个资源的所有字段,给API消费者一个选择字段的能力,这会降低网络流量,提高API可用性。 GET /cars?fields=manufacturer,model,id,color - Paging分页 使用 limit 和offset.实现分页,缺省limit=20 和offset=0; GET /cars?offset=10&limit=5 为了将总数发给客户端,使用订制的HTTP头: X-Total-Count. 链接到下一页或上一页可以在HTTP头的link规定,遵循Link规定: Link: <https://blog.mwaysolutions.com/sample/api/v1/cars?offset=15&limit=5>; rel="next",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=50&limit=3>; rel="last",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=0&limit=5>; rel="first",<https://blog.mwaysolutions.com/sample/api/v1/cars?offset=5&limit=5>; rel="prev", # 7.版本化你的API 支付宝 v1 v2 v3 - 使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5. 一般在Url后面使用?v /blog/api/v1 # 8. 使用Http状态码处理错误 - 如果你的API没有错误处理是很难的,只是返回500和出错堆栈不一定有用- Http状态码提供70个出错,我们只要使用10个左右: `200 – OK – 一切正常 `201 – OK – 新的资源已经成功创建 `204 – OK – 资源已经成功删除 `304 – Not Modified – 客户端使用缓存数据 `400 – Bad Request – 请求无效,需要附加细节解释如 "JSON无效" `401 – Unauthorized – 请求需要用户验证 `403 – Forbidden – 服务器已经理解了请求,但是拒绝服务或这种请求的访问是不允许的。 `404 – Not found – 没有发现该资源 `422 – Unprocessable Entity – 只有服务器不能处理实体时使用,比如图像不能被格式化,或者重要字段丢失。 `500 – Internal Server Error – API开发者应该避免这种错误。 使用详细的错误包装错误: 状态码 数据 header头信息 { "errors": [ { "userMessage": "Sorry, the requested resource does not exist", "internalMessage": "No car found in the database", "code": 34, "more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345" } ] }
Rest API案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 @RestController @RequestMapping("v1/users") public class UserController { private UserService userService; @Autowired public UserController (UserService userService) { this .userService = userService; } @PostMapping public ResponseEntity<Void> saveUser (@RequestBody User user) { try { userService.save(user); } catch (Exception e) { e.printStackTrace(); return new ResponseEntity <Void>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity <Void>(HttpStatus.CREATED); } @DeleteMapping("{id}") public ResponseEntity<Void> deleteUser (@PathVariable("id") Integer id) { try { userService.delete(id); } catch (Exception e) { e.printStackTrace(); return new ResponseEntity <Void>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity <Void>(HttpStatus.NO_CONTENT); } @PutMapping public ResponseEntity<Void> updateUser (@RequestBody User user) { userService.update(user); return new ResponseEntity <Void>(HttpStatus.NO_CONTENT); } @GetMapping("{id}") public ResponseEntity<User> findUserById (@PathVariable("id") Integer id) { User user = userService.findById(id); if (user == null ) { return new ResponseEntity <User>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity <User>(user, HttpStatus.OK); } @GetMapping public ResponseEntity<List<User>> users () { List<User> users = null ; try { users = userService.findAll(); } catch (Exception e) { e.printStackTrace(); return new ResponseEntity <List<User>>(HttpStatus.INTERNAL_SERVER_ERROR); } return new ResponseEntity <List<User>>(users, HttpStatus.OK); } }
1 2 3 4 5 6 7 8 9 10 11 12 标准的restful应该在返回时返回响应状态码 ResponeEntity: springmvc封装的一个专用于restful的响应类,这个类在响应时可以提供响应的状态码,同时还可以自定义响应头信息 HttpStatus: springmvc封装的一个枚举类类型,这个类中都是响应状态码。 /** *查询某个用户的详细信息 */ @GetMapping("/{id}") public ResponeEntity<User> user(@PathVariable("id") Integer id){ User user = new User(id,"李思思",8900.5); return new ResponeEntity<>(user,HttpStatus.OK) }
异常处理 传统开发的异常处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 resolveException:当控制器中任意一个方法出现异常时,如果该控制器没有自己的异常处理(try ...catch ),则会进入当前方法。 @Component public class GlobalExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { return new ModelAndView ("500" ); } } 可以通过判断异常的类型,进行不同页面的调转。
RestFul的异常处理(前后端分离) 1 2 3 4 5 6 7 8 9 10 11 @ControllerAdvice public class GlobalExceptionHandlerRest { @ExceptionHandler(value = Exception.class) @ResponseBody public ResponseEntity<String> exceptionHandler (Exception e) { return new ResponseEntity <>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } }
1 传统的开发和前后端分离的开发,在针对控制器中出现的异常,如果控制器方法中没有设置处理方式(try..。catch),那么传统的开发在处理这些异常时会创建一个异常处理类,实现HandlerExceptionResolver接口,在这个类上加上注释@Component,然后通过重写接口中的resolveException方法,就可以在方法中进行自定义的异常处理,可以根据异常的类型进行不同的操作;而前后端分离在处理时,会创建一个异常处理类,只需要在该类上加上@ControllerAdvice注解,那么就可以在这个类中自定义方法去处理异常,控制器中的方法可以根据异常的类型进行选择用哪个异常处理方法处理异常。 在方法上加@ExceptionHandler注解,可以通过其value属性来指定异常的类型,可以是自定义的一些异常。
CORS 跨域 CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
。
它允许浏览器向跨源服务器,发出XMLHttpRequest(ajax)请求,从而克服了AJAX只能同源使用的限制
。
跨域:端口号或者域名不一样
同源策略
同源策略
[same origin policy]是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。 同源策略是浏览器安全的基石。
源
同源
1 2 3 4 5 6 7 8 9 协议:http 域名:www.a.com 端口号:8080 # 同源举例 - 例如判断下面的URL是否与 http://www.a.com/test/index.html 同源 http://www.a.com/dir/page.html --------->同源 http://www.child.a.com/test/index.html ->不同源,域名不相同 https://www.a.com/test/index.html ------>不同源,协议不相同 http://www.a.com:8080/test/index.html -->不同源,端口号不相同
哪些操作不受同源限制
哪些操作受到同源限制
在浏览器中发起一个AJAX请求,会受到同源策略限制。
出现错误:Access-Control-Allow-Origin
使用CORS解决同源限制
@CrossOrigin注解 这个注解用在类上,类中所有方法(请求)允许被其他域中资源访问
1 2 3 4 5 6 7 8 9 10 @RestController @RequestMapping("demos") @CrossOrigin public class DemoController { @GetMapping public String demos () { System.out.println("========demo=======" ); return "demo ok" ; } }
全局解决跨域问题 创建配置类,在类中创建CorsFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter () { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource (); CorsConfiguration corsConfiguration = new CorsConfiguration (); corsConfiguration.addAllowedOrigin("*" ); corsConfiguration.addAllowedHeader("*" ); corsConfiguration.addAllowedMethod("*" ); source.registerCorsConfiguration("/**" , corsConfiguration); return new CorsFilter (source); } }
Jasypt 加密 引言 Jasypt 也即Java Simplified Encryption是Sourceforge.net上的一个开源项目。在当地时间11月23号的通告中,Jasypt 1.4的新特征包括:加密属性文件(encryptable properties files)、Spring Framework集成、加密Hibernate数据源配置、新的命令行工具、URL加密的Apache wicket集成以及升级文档。
根据Jasypt文档,该技术可用于加密任务与应用程序,例如加密密码、敏感信息和数据通信、创建完整检查数据的sums. 其他性能包括高安全性、基于标准的加密技术、可同时单向和双向加密的加密密码、文本、数字和二进制文件。Jasypt也可以与Acegi Security整合也即Spring Security。Jasypt亦拥有加密应用配置的集成功能,而且提供一个开放的API从而任何一个Java Cryptography Extension都可以使用Jasypt。
Jasypt还符合RSA标准的基于密码的加密,并提供了无配置加密工具以及新的、高可配置标准的加密工具。
整合SpringBoot
1 2 3 4 5 <dependency > <groupId > com.github.ulisesbocchio</groupId > <artifactId > jasypt-spring-boot-starter</artifactId > <version > 2.0.0</version > </dependency >
1 2 3 4 jasypt: encryptor: algorithm: PBEWithMD5AndDES password: cf150b74e4824146ad76e9ebe757ba76
通过程序传递密钥
1 -Djasypt.encryptor .password =cf150b74e4824146ad76e9ebe757ba76
加密、解密
1 2 3 4 5 6 7 8 9 10 11 12 @Autowired StringEncryptor encryptor; @Test public void testSecret () { String url = encryptor.encrypt("localhost" ); System.out.println(url); String decrypt = encryptor.decrypt("nRmCj//nOuErKepiS6dyBO/9Ff7LGCXZ" ); System.out.println(decrypt); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 mysql: host: ENC(pNVN9SKixSnfJ8yZjjIESZuP8HTwG0Hm) spring: thymeleaf: suffix: .html prefix: classpath:/templates/ cache: false datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${mysql.host}:3306/emp?characterEncoding=UTF-8 username: ENC(M9LpI0WkgVh0rpx+BkOGew==) password: ENC(M9LpI0WkgVh0rpx+BkOGew==)