约定大于配置:简化M-V-C开发模式;使编码、配置、部署、监控变得简单
为基于Spring的开发提供更快的体验,创建可以独立运行的Spring应用
直接内嵌Servlet容器:Tomcat或Jetty服务器,可以打包成war文件,也可以以jar包形式独立运行:java -jar xxx.jar
提供pom.xml文件来简化Maven配置:starters中自动依赖与版本控制;尽可能的根据项目依赖来自动配置Spring框架
提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查;提供http,ssh,telnet等对运行时项目进行监控
开箱即用,无需xml文件配置,不借助代码生成来实现;还可以修改默认值来满足特定需求
能与主流框架集成;其他大量项目如SpringCloud都基于SpringBoot
缺点:
依赖太多;缺少服务的注册和发现等解决方案;缺少监控集成方案,安全管理方案
spring-boot-starter:场景启动器,用于帮助导入各个场景所需依赖的组件(jar),如spring-boot-starter-web,aop,cache,data-jdbc,data-jpa,data-redis,data-rest,freemarker,jdbc,json,test等;这样免去了在xml中定义bean对象
@SpringBootApplication:标注启动类,该程序是一个SpringBoot应用
@SpringBootConfiguration:标注配置类,同Spring的@Configuration
@EnableAutoConfiguration:开启自动配置,其原理(核心):(为什么配置文件能定义那么多属性,为什么它们能生效)SpringBoot启动时,加载主配置类,开启自动配置功能;利用@Import导入各种组件,包括配置类
@Import({AutoConfigurationImportSelector.class}):导入组件,一个包含了众多自动配置类xxxAutoConfiguration的List;如AopAutoConfiguration,开发无需再手动编写配置和注入这些组件的工作;组件从jar包下MATA-INF/spring.factories中读取@EnableAutoConfiguration标注的组件,如视图解析器等
@ConditionalOnXXX:扩展的Spring底层注解@Conditional,用于判断条件是否满足,满足则该类可用;如在配置类上的@ConditionalOnWebApplication,当为web应用时本配置类有效;(配置debug=true可以在日志中看生效了的配置类)
配置类的作用:创建bean,即为容器添加各种组件(@Bean),这些组件的属性来自对应的properties类,这些属性就对应配置文件中的每一个key(同自定义配置后,映射到自定义类中属性一样)
@AutoConfigurationPackage:自动配置包;其下@Import({Registrar.class});两个注解就把启动类所在包及子包中的组件扫描到容器,所以特别注意其位置
main():运行SpringApplication.run();启动流程:
run()中调用构造器,初始化SpringApplication实例:
public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// main方法中的args参数即主类;可接收命令行启动时添加的参数
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 确认当前应用程序的类型;一般都是Servlet
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 加载ApplicationContextInitializer初始化类获取Spring工厂实例
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载ApplicationListener类,监听类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 获取main方法所在类
this.mainApplicationClass = deduceMainApplicationClass();
}
private Class> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
执行run():
从同路径下获取SpringApplicationRunlisteners,启动它们的starting()
配置环境ConfigurableEnvironment;回调listener的environmentPrepared()
创建打印Banner,即控制台输出的那个图案
创建ApplicationContext,即IoC容器;初始化容器
javax.servlet.ServletContainerInitializer:封装web.xml
这个类会在web容器启动阶段被回调,可以在onStartup方法里做一些servlet、filter、listener的注册等操作。springboot在META-INF/services下配置了这个类,让整个web容器启动后可以找到并启动这个类,即它把web.xml封装成了配置类
全局配置文件:.yml或.properties;修改SpringBoot自动配置的默认值
语法:
以数据为中心,比xml(标签)和json更适合做配置文件
默认无引号,如果加了双引号,特殊字符依旧有效,如/n换行,而单引号则直接原样输出
集合/数组:元素前使用-列举;或者数组用逗号分隔开的枚举,集合再用[]包含数组
占位符:
配合使用预定义方法,如${random.int}或${random.uuid}随机数
引用已经存在的key,如${key};若key不存在,则key为默认值;使用:可自定义默认值;如${key: value}
properties配置文件可能乱码,与IDEA默认UTF8编码不符,需勾选file encoding下的transparent native-to-acscii convension即转码本地到ASICC
使用配置:
@Value():Spring单一注入,用于属性或setter()上;需要一个一个的注入;支持SpringEL;适用于业务中获取某个值
@ConfigurationProperties(prefix=“配置文件中属性”):SringBoot把配置和Bean相关联来实现;修改SpringBoot自动配置的默认值有效就是此理,所以定义值后能自动注入;支持:
批量注入;适用于映射整个Bean
松散绑定,如userName与user-name一致
JSR303数据校验,如@Validated开启校验支持后,属性上可使用@Null,@Min,@Email等控制输入规范
对象、集合等复杂类型如对象封装
添加组件:
@ImportResource(loactions={"classpth:bean.xml}):加载Spring的配置文件
@Configuration:推荐使用配置类;配合@Bean
多环境配置:
文件名前缀固定的前提下,不同标识指定不同环境,如application-test.yml与application-dev.yml;默认的application.yml中spring.profiles.active=dev激活指定文件
yml支持一个文件中的配置块:---分开的配置,如配置两个server.port;每个块中单独指定激活的文件
命令行指定激活文件:edit configuratuin设置中设置program arguments值为--spring.profiles.active=dev;或者启动最终打包项目时的启动命令后加此命令
多数据源配置:
dataSource下配置多个数据源的属性信息
分别编写两个配置类对应两个数据源,mapper包扫码各自的,service层调用各自的dao层
# thymeleaf配置:即ThymeleafAutoConfiguration配置类
#server:
# servlet:
# context-path:
test:
id: 1
name: user
max: true
# 占位符:${SpEL},当SpEL不存在,则将他解析为等值的字符串,后接冒号可自定义默认值
# name: zg${test.id}h
#===========================================
# 多profile配置文件
# 默认为此文件;当需要切换环境如测试环境,开发环境,生产环境时,可定义多个profile(注意命名)
# 然后在此指定使用目标配置文件激活:
# (另一种激活方法:命令行参数:在edit Configurations-spring boot里面的Program arguments中输入命令:--spring.profiles.active=dev)
# (另另一种激活方法:虚拟机参数:在edit Configurations-spring boot里面的VM options中输入:-Dspring.profiles.active=dev)
# (另另另一种激活方法:命令行参数:在项目打包后,在命令窗口启动项目时,在最后添加同上的命令行参数命令)
#spring:
# profiles:
# active: dev
# yml文件还有文档块写法:---隔开
#server:
# port: 8080
#spring:
# profiles:
# active: dev
#---
#server:
# port: 8081
#spring:
# profiles: dev
#---
#server:
# port: 8082
#spring:
# profiles: prod
#================================================================
# 配置文件加载位置:
# springboot的4个默认有效位置:优先级依次降低;内容互补,高优先级配置会覆盖低优先级中存在的,而不是有高优先级就不加载低优先级的
#-file:/config/,即放在与src同级目录config中的文件
#-file:/,即放在与src同级的文件
#-classpath:/config,即放在resources下config下的文件
#-classpath:/,即放在resources下的文件,项目最初始时的默认模样
# 指定任意文件为配置文件:配置spring.config.location=绝对路径;运维常用此方式,在打包项目后启动命令加此命令;(命令行参数不仅这些,还可以是任意配置,如加--server.prot=8081)
#项目内部配置
# 加载指定的配置文件:@PropertySource(value="classthpath:xxx");application.yaml/properties是默认能识别的配置文件,自定义的不能识别,需要手动指定加载
#spring:
# config:
# location:
#项目外部配置:参考官方文档
logging:
file:
name: log/springboot.log
spring:
messages:
basename: i18n/message
#禁用模板引擎的缓存,默认为true;开发时关闭,应用上线时开启以降低数据库压力
thymeleaf:
cache: false
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/dbtest?useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
#使用自定义数据源druid
type: com.alibaba.druid.pool.DruidDataSource
# schema:
# - classpath: sql/user.sql
# schema用来从IDE中生成数据库中的表:user.sql是位于templates/sql下的一张表
#黄色背景的说明是自定义属性,不能与Bean绑定;代码提示出来的都是默认的hikari,dbcp2,tomcat连接池;
#使用自定义的Druid,需要向容器添加映射组件@Configuration
#如与DataSource相关的DataSourceProperties中存在上面无背景的属性,不存在下面黄色背景的属性;需要自定义映射
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# 文件上传:单个文件最大大小;上传的文件总大小
servlet:
multipart:
max-file-size: 200MB
max-request-size: 500MB
#初始大小;最小;最大;连接等待超时时间(毫秒);检测(需要关闭的空闲连接)一次的间隔,;个连接在池中的最小生存时间
#检测数据库连通性(不同数据库的validationQuery值不同,返回一条不为空的SQL);获取连接时测试连接是否有效(都为true时,testOnBorrow优先级高)
#是否打开PSCache(PreparedStatements 缓存);打开时设置缓存大小;监控统计拦截的filters(监控统计stat,防御SQL注入wall,日志log4j(2.0版本后不要这个了?))
mybatis:
config-location: classpath:mybatis/config/mybatis-config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
SpringBoot默认的DataSource:org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource
自定义DataSource:配置返回DataSource的Bean,return dataSourceProperties.initializeDataSourceBuilder().build()
Druid:引入依赖;配置spring.dateSource.type为druid(屏蔽默认的Tomcat.jdbc);配置该类Bean;配置它的数据监控/拦截器
Mybatis:MybatisAutoConfiguratuin;@Mapper定义或@MapperScan扫描
Spring Data JPA:
@Entity标注映射到数据库表的Bean;@Table映射表名;@Id标注Id;@GeneratedValue标注主键增长策略
编写Dao接口:继承JpaRepository;已经有了基本的CURD方法
复杂的CURD及SQL语句编写
https://www.cnblogs.com/TechSnail/p/7242777.html
文件:i18n目录下配置默认的、en_US、zh_CN三个文件;页面切换语言环境后会自动适配对应文件中的值
ResourceBundleMessageSource:文件管理;MessageSourceAutoConfiguration:配置类
获取:前端使用#{key}
Locale:区域信息对象;默认区域信息解析器根据请求头中环境信息来设置;可自定义
静态资源:
可直接页面访问;如导入依赖bootstrap
静态资源的访问路径:classpath/下的/resources或/static或/public下(优先级依次降低),或/webjars/下即maven依赖的META-INF/resources/webjars下的jar包可访问,因此pom也可导入前端jar包且页面访问时,打开了源码;可配置文件中自配置资源访问路径
引用:如引用css文件,< link th:href="@{/webjars/bootstrap/4.0.0/css/bootstrap.css}">
首页/欢迎页:源码getIndexHtml()设置为"访问路径+index.html"
动态资源/模板引擎:
位于/templates下的文件,需要编译(模板渲染)而不能直接打开;只能通过Controller访问且默认为index.html
定义:实现HandlerInterceptor的preHandle(),postHandler()等方法,如登录拦截器,在登录之前的判断等
注册:实现HandlerInterceptorAdapter的addInterceptor(拦截器),addPathPatterns("拦截路径"),excludePathPatterns(”忽略的路径“)等
@Controller
public class SpringMVCController {
@Autowired
private JdbcTemplate jdbcTemplate;
@ResponseBody
@GetMapping("/hello")
public String hello() {
return "hello world!";
}
// 如果不写方法,则默认访问的是static下的index.html
// @GetMapping({"/", "/login.html"})
// public String getIndex() {
// return "login";
// }
@GetMapping("/user")
public String toLogin() {
return "login";
}
@PostMapping("/login")
public String login(@RequestParam("userName") String userName,
@RequestParam("password") String password,
Map message) {
if (!StringUtils.isEmpty(userName) && "123456".equals(password)) {
return "user/main";
}
message.put("message", "用户名或密码错误,请检查!");
return "ymldefinetest";
}
/**
* 使用原生JDBC连接和操作数据库:查询所有后直接以文本显示在页面上
*/
@ResponseBody
@GetMapping("/users")
public List
ErrorMvcAutoConfiguration:错误处理的自动配置类;添加的组件中设置了响应规则,如产生错误后去到页面template/error下,DefaultErrorViewResolver映射到指定错误页面,设置错误响应页面的样式;信息包含了时间戳、状态码、错误提示、异常对象及信息等;错误页面不在templates下,则查找statics下,都没有则得到SpringBoot默认错误提示页面(一段写在后端的页面代码)
错误处理:
使用默认规则:即错误页面放于/error下,命名以4或5开头;优先匹配精确的状态码如404.html
自定义异常处理器:@ControllerAdvice标注的类,@ExceptionHandler标注错误处理逻辑,包括写入异常信息(异常信息也可自定义实现Exception),返回的视图等;实现DefaultErrorAttributes的getErrorAttributes()自定义错误属性,可可以包含异常信息
实现类logback + 调用顶层抽象slf4j
导入spring-boot-starer-logging
日志级别:.trace()、.debug()、.info()默认、.warn()、.error();仅打印该级别及以后的信息
@RunWith(SpringRunner.class)与@SpringBootTest:可导入Junit做单元测试
pom.xml中添加插件< build>< plugin>spring-boot-maven-plugin,即将应用打包为可执行jar
执行maven-package,最终打包在target下;执行java -jar xxx.jar启动
@EnableCaching开启缓存(bean需要实现可序列化)
Spring注解;使用注解时,会触发一个post processor,这会扫描每一个bean,查看是否已经存在注解对应的缓存。如果找到,则会自动创建一个代理拦截方法调用,使用缓存的bean执行处理
使用SpringBoot Data Redis:lettuce替换了jedis,集成Redis
配置各种属性(实质就是RedisTemplate的属性)即可
spring:
redis:
host: 127.0.0.1
port: 6379
开发中自定义RedisTemplate,如自定义序列化机制;然后编写工具类RedisUtil,注册该类即可,而不是使用原生API
@Cacheable(value=“sampleCache”), key="")
用于方法上;调用方法时会从value为sampleCache的缓存(缓存本质是一个map)中查询key为id的值,如果不存在,则执行实际的方法(即查询数据库等服务逻辑),并将执行的结果存入缓存中,否则返回缓存中的对象。key默认是只有一个参数时方法的参数,多参数时需指定
core:设置定时执行的表达式,如 0 0/5 * * * ?每隔5分钟执行一次
zone:执行时间所在时区
fixedDelay / fixedDelayString:固定延迟时间执行,上个任务完成后,延迟多久执行
fixedRate / fixedRateString:固定频率执行,上个任务完成后,多久后开始执行\
initialDelay / initialDelayString:初始延迟时间
定时任务表达式:秒、分、时、天、月...,取值为各自默认范围,如秒为0-59
2 * * * * ?:每一分钟的第2秒执行;其他同理,如2 35 18 * * ?为每天的18时35分2秒时执行
0/2 * * * * ?:等步长序列,从0秒开始,每隔2秒执行,即步长为2;如1/15为从1秒开始,1,16,31执行;其他同理
1-2 * * * * ?:范围,每分钟的1-2秒内执行
1,2 * * * * ?:枚举,每分钟的1秒和2秒执行
*匹配任意,?匹配占位
Spring3.0后自带的Scheduled定时任务器
@EnableScheduling:开启对定时任务的支持
@Scheduled:在方法上声明需要执行的定时任务
@Component
@Configuration
@EnableScheduling
public class ScheduleUtil {
/**
* 测试定时任务:每隔2秒执行
* 由控制台输出,可知每隔两秒执行一次方法
*/
@Scheduled(cron = "0/2 * * * * ?")
public void testScheduled() {
System.out.println("自带触发定时器任务:" + new Date());
}
}
第三方定时任务调度框架Quartz(复杂)
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
/**
* 定时任务类:第三方任务调度框架Quartz
*/
public class ScheduleQuartzUtil implements Job {
/**
* 唯一重写的方法:任务触发时执行
* @param jobExecutionContext
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("第三方触发定时器任务:" + new Date());
}
// Quartz的简单使用;与SpringBoot整合参见配置类
public static void main(String[] args) throws SchedulerException {
// 1. 创建Job对象,即定义定时任务的内容
JobDetail jobDetail = JobBuilder.newJob(ScheduleQuartzUtil.class).build();
// 2. 创建Trigger对象,即定义触发时间,等同于@Schedule()的cron
// Quartz提供了简单的时间定义封装SimpleScheduleBuilder
Trigger trigger = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(2)).build();
// 复杂的自定义定时时间CronScheduleBuilder
// Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")).build();
// 3. 创建Scheduler对象,联结定时任务和时间
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
// 4. 启动定时器
scheduler.start();
}
}
/**
* 第三方定时调度任务框架Quartz的复杂应用:整合SpringBoot
*/
@Configuration
@EnableScheduling
public class QuartzConfig {
// 1. 创建Job对象,即定义定时任务的内容
@Bean
public JobDetailFactoryBean getJobDetailFactoryBean() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(ScheduleQuartzUtil.class);
return factoryBean;
// 注意此处,ScheduleQuartzUtil的创建交给了Factory,其源码采用的是反射机制构建,即没有被Spring容器管理,容器中不存在该实例
// 因此当此config注入其他Bean即有@Autowired属性时,报空指针异常;
// 解决方案:重写创建定时器实例的类AdaptableJobFactory的方法,在返回该实例之前,手动将其注入到Spring容器
// @Component("myAdaptableJobFactory")
// class MyAdaptableJobFactory extends AdaptableJobFactory {
// @Autowired
// private AutowireCapableBeanFactory autowireCapableBeanFactory; // 此对象帮助将自定义Bean注入到容器
// @Override
// protected Object createJonInstance(TriggerFiredBundle bundle) throws Exception {
// // return super.createJobInstance(bundle); // 修改此处为下面的
// Object object = super.createJobInstance(bundle);
// this.autowireCapableBeanFactory.autowireBean(object);
// return object;
// }
// }
// 然后在第三步,即创建Scheduler对象时,传入此实例,即factoryBean.setTriggers(myAdaptableJobFactory);
}
// 2. 创建Trigger对象,即定义触发时间,等同于@Schedule()的cron
// Quartz提供了简单的时间定义封装SimpleScheduleBuilder
@Bean
public SimpleTriggerFactoryBean getSimpleTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject()); // 关联定时任务
factoryBean.setRepeatInterval(2000); // 设置执行的毫秒数
factoryBean.setRepeatCount(5); // 设置执行的次数
return factoryBean;
}
// 复杂的自定义定时时间CronScheduleBuilder
@Bean
public CronTriggerFactoryBean getCronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
factoryBean.setCronExpression("0/2 * * * * ?");
return factoryBean;
}
// 3. 创建Scheduler对象,联结定时任务和时间
@Bean
public SchedulerFactoryBean getSchedulerFactoryBean(SimpleTriggerFactoryBean simpleTriggerFactoryBean) {
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
factoryBean.setTriggers(simpleTriggerFactoryBean.getObject());
// factoryBean.setTriggers(cronTriggerFactoryBean.getObject());
return factoryBean;
}
// 4. 启动定时器:开启@EnableScheduling
}
常见数据接收和发送,参见SpringMVC
系统中发送http请求并传递json数据(常见场景为调用第三方请求并写入请求参数)
RestTemplate / HttpClient / OkHttpClient:同步的web客户端http请求工具,前者不再更新和维护;HttpClient常用于网络爬虫中,系统模拟浏览器发送请求获取数据;OkHttpClient高效低损
使用:注册Bean到容器即可,如@Bean public RestTemplate restTemplate() { return new RestTemplate(); }
请求及方法:
get请求无限制;post请求及方法:传递的参数如果用map类型,则必须是MultivalueMap
getForObject() / postForObject():获取对象;返回类型为对象
getForEntiry() / postForEntiry():获取对象,状态码,请求头信息等;返回类型为ResponseEntity封装的泛型对象
// 注意上面说的Get或Post指的时url指向的第三方请求的请求方式
public Map getResult() {
String url = "http://localhost:8080/add/...";
return restTemplate.getForObject(url, Map.class); // 后面可以指定请求需要的参数列表
}
@GetMapping(...)
public Map getResult() {
String url = "http://localhost:8080/add/...";
Map params = new HashMap<>();
ResponseEntity responseEntity = restTemplate.getForEntity(url, HashMap.class, params); // 后面可以指定请求需要的参数列表
HttpStatus statusCode = responseEntity.getStatusCode(); // 状态码
HttpHeaders headers = responseEntity.getHeraders(); // 请求头信息
return responseEntity.getBody();
}
// 注意返回类型
@GetMapping(...)
public User getResult() {
String url = "http://localhost:8080/add/...";
MultivalueMap params = new LinkedMultivalueMap<>();
params.put("age", 1);
params.put("name", "MyName"); // 注意,使用了MultivalueMap,则add()方法中的参数接收不能用User对象,而应该用@ReqeustParam即具体的属性参数
return restTemplate.postForObject(url, params, User.class);
// 如果解析出来转换失败,可以使用ObjectMapper工具类
}
@GetMapping(...)
public User getResult() {
String url = "http://localhost:8080/add/..."; // 如果add()中参数接收用了对象,则需要写入请求体并设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType("application/json");
User user = new User(); // 当使用json数据传递的时候,封装为JSONObject即可
user.setAge(age);
user.setName(myName);
HttpEntity entityParam = new HttpEntity(user, headers); // 写入
return restTemplate.postForObject(url, entityParam, User.class); // 注意传参不同
}
HttpClient应用之简单的网络爬虫(详情参见其他.md):
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
/**
* 网络爬虫-模拟浏览器发送请求
*/
@Slf4j
public class HttpClientUtil {
public static void main(String[] args) throws IOException {
// 1、创建HttpClient实例,模拟打开浏览器
CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
// 2、创建HttpGet实例;模拟发送请求一般都为get请求
HttpGet httpGet = new HttpGet("http://www.baidu.com");
// 3、执行get请求
CloseableHttpResponse response = closeableHttpClient.execute(httpGet);
// 4、解析响应
if (200 == response.getStatusLine().getStatusCode()) {
HttpEntity entity = response.getEntity(); // 获取返回实体
String content = EntityUtils.toString(entity, "utf8");
log.info("获取请求响应数据:" + content); // 实体内容以字符串返回
}
response.close();
closeableHttpClient.close();
}
}
依赖基于starter启动器;源码类ThymeleafProperties
语法:
导入名称空间:xmlns:th="http://www.thymeleaf.org";然后th:接管所有他html元素
/**
* thymeleaf模板引擎用法:
* requestMapping:一般都为资源文件HTML名称
* 引入th名称空间xmlns(xml namespace);HTML任意属性都可用th:替换
* 特殊的标签内容:th:utext,它不转义特殊字符;如"内容
"
* 内容:用th:text得到并显示"内容
",即<被转义,类似Java中的/后边的特殊字符被转义;用th:utext得到并显示"内容",即特殊字符<没被转义,解析为HTML了
* 片段包含:th:insert,th:replace;
* 遍历:th:each;
* 条件判断:th:if;th:unless;th:switch;th:case;
* 变量声明:th:object;th:with;
* 属性修改:th:attr;th:attrprepend;th:attrappend;th:value;th:href;th:src;。。。。。。
* 片段声明:th:fragment;
* 移除:th:remove;
* 配合表达式取值:
* ${...}:变量表达式
* 获取变量值;调用方法;
* 使用内置基本对象如#ctx,#vars,#locale,#request,#response,#session,#servletContext
* 使用内置工具(类)对象如#strings,#numbers,#dates...等工具类
* 另一种获取方式:[[]]或[()]转义字符行内转义,如 等同于[[${user}]]
* *{...}:变量表达式:${...}的补充使用
* 如${session.user}获取user,${session.user.name}获取user中的name
* 而使用*{name}也可以获取user中的name(但注意作用域,在${session.user}所在标签及字标签内),即此时*等同于${session.user}
* #{...}:获取国际化内容
* @{...}:定义URL
* 参数列表用小括号包裹,key-value形式,逗号隔开,value还可用变量表达式获取;若以/开头,表示路径为当前项目下
* ~{...}:片段引用表达式:尤其是引用公用的fragment
* 字面量:字符串,数字,布尔值,null
* 文本操作:字符串拼接:+;字符串替换:|...|
* 数学运算:+,-,*,/,%
* 布尔运算:and,or,!,not
* 比较运算:>,<,==,!=等,及gt,lt,ge,le,eq,ne
* 条件运算:if ? then :else;value ? defaultValue
*
*/
@Controller
public class ThymeleafUsageController {
// 相应处理:一种返回页面路径+名称,方法参数为Model,map,RedirectAttributes等类型
@GetMapping("/prompt")
public String promptSuccess(Map map) {
map.put("text", "提示:成功!
");
map.put("utext", "提示:成功!
");
map.put("users", Arrays.asList("user1","user2","user3"));
return "ymldefinetest";
}
// 另一种响应方式:HTML数据,即返回类型为ModelAndView或者Model,将页面路径+名称写入其中
@GetMapping("/prompt2")
public ModelAndView promptSuccess() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("text", "提示:成功!
");
modelAndView.addObject("utext", "提示:成功!
");
modelAndView.addObject("users", Arrays.asList("user1","user2","user3"));
modelAndView.setViewName("ymldefinetest");
return modelAndView;
}
/**
* 与http请求和响应相关的数据
* @param request
* @param response
*/
@RequestMapping("/http")
public void http(HttpServletRequest request, HttpServletResponse response) {
// request get...
System.out.println(request.getMethod());
System.out.println(request.getServletPath());
Enumeration headerNames = request.getHeaderNames();
// response set...
response.setContentType("text/html;charset=utf-8");
}
}
https://www.e-learn.cn/thymeleaf
https://www.cnblogs.com/wldbk/p/12198419.html
以后写项目,可以不用管前端了,直接百度bootstarap模板后导入
项目功能规划:
后端管理:管理员/用户登录,博客管理(增删查改),分类管理(增删查改),标签管理(增删查改)
前端展示:首页(博客分页列表,top标签,top分类,最新推荐,博客详情),分类(分类列表,分类下的博客列表,博客详情),标签(标签列表,标签下的博客列表,博客详情),归档(按年度归档展示博客列表,博客详情)
项目搭建:
选择Spring initializr,选择预先加载的依赖:DevTools热部署,Web(Spring web),thymeleaf,mysql,data jpa等
检查依赖是否正确;配置application.yml文件;检查单元测试(测试类中引入junit来做单元测试)
前端框架SemanticUI
遇到的坑:
MySQL的driver-class-name改了:com.mysql.cj.jdbc.Driver
启动SpringBoot报错MySQL存在问题:原因可能是前面选的MySQL下载的版本太高;需要在pom文件中指定version
启动SpringBoot报错Error creating bean with name 'inMemoryDatabaseShutdownExecutor:还不清楚错误原因;解决方案是在启动入口的@SpringBootApplication注解后面加上(exclude = {DataSourceAutoConfiguration.class })
彩蛋-自定义启动时图像:网上搜索SpringBoot启动时Banner生成
异常:
SpringBoot中有static和templates两个路径,分别用于存放静态页面和动态页面(模板如themeleaf等);静态页面可直接访问,动态页面则会请求后台;例如分别在其下建hello.html文件;输入localhost:8080/hello.html,则始终返回static下的文件;使用controller访问即输入localhost:8080/hello,static下也可访问
@GetMapping("/hello")
public String hello() {
return "hello.htnl";
}
但templates下的则报错;原因:SpringBoot默认推荐thymeleaf,访问动态资源需要请求后台,即框架会分配一个ViewResolver去解析,因此需要书写controller,注意return中没有.html后缀;且访问的path不能与视图名重合,否则会抛出Circular view path异常,如此处不能写为@GetMapping("/hello")
@GetMapping("/hi")
public String hello() {
return "hello";
}
记一个遇到的访问不了templates下文件的巨坑:页面一直报错:
Writelabel Error Page
This application has no explicit mapping for /error,so you are seeing this as a fallback.
网上找了的很多方案(基本都是spring boot启动类因该放在controller外层的问题,于我不适用);最后发现代码中提示:Cannot resolve MVC View 'xxx';不能处理视图,那大概就是引入的thymeleaf依赖有冲突或不匹配;检查代码,引入的dependency没有问题,但这个地方的version和layout-dialect的version不匹配(下面是匹配的,不过我是直接将这两行代码删除了,可行)
1.8
3.0.11.RELEASE
2.2.2
定义错误页面、全局异常处理与日志处理:
404/500:在templates下新建/error目录用于存储自定义错误页面,页面起名404.html/500.html,Springboot会在后台发生异常时去寻找这个目录,并跳转到对应页面
自定义error:自定义标注为@ControllerAdvice的拦截器ExceptionHandler,拦截所有标注@Controller的控制器,使产生异常时跳转到自定义的error page
自定义Exception:继承RuntimeException,此异常可由一些注解标注为何种异常,如@ResponseStatus(HttpStatus.Not_Found)标注为404;在出现异常的地方throw new MyException()
AOP方式以切面@Aspect拦截请求:配合@Pointcut,@Before,@After等,在日志输出一些信息如url,ip,args等,涉及的类HttpServletRequest;或者事务处理,缓存,权限校验等
将写好的前端静态页面整合进来:因为项目使用的thymeleaf模板引擎(默认不支持JSP),需要修改适配:
fragment:将公用部分(包括JS)提取到模板,在需要的页面引用
由此生成的网页都是动态网页,需要服务器启动后才能访问(直接浏览器打开可看到不起作用)
一些前端插件:编辑器Markdown:推荐editor.md;内容排版typo.css:;动画animate.css:;代码高亮prism: ;滚动侦测waypoints:;平滑滚动:jquery.scrollTo:;目录生成Tocbot:;二维码生成qrcode.js
生成实体类:
使用jpa自动生成数据库表:(似乎需要配置idea到mysql的datasource????引入数据源,一直刷不出来数据表);曾经生成不了表,只能手动导入SQL
记录一个巨坑:
书写实体bean,并且@ManyToOne等一系列注解配置好数据表之间的关联关系后,启动springboot,正常情况是会在数据库中自动生成对应的数据表,包括中间表,有自联关系的,表字段也会体现出来;但是我的一直没有生成数据表,并且@Autowired注解标注的bean也在启动时报错找不到;查阅很多资料都说是路径问题导致扫描不到,但我确定我的路径没有问题。后来终于在网上找到答案:@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}):作用是排除自动注入数据源的配置(取消数据库配置)!!!!如果只需要简单的启动运行,则客家exclude,否则需要在配置文件中配置DataSource;因此不需要括号中的内容,于是数据表能够生成,且自动注入的bean也能找到了!汗!!!!!
dao-service-repository:
各种数据操作CURD;定义Repository,方法命名严格遵守jpa规范;service使用CURD的结果作业务处理
一个坑:查询时,jpa规范方法名findXXX()如findOne(id),但Springboot版本原因,可能的方法是其他如getOne(id)
controller:
登录:用户合法验证,未登录用户拦截;输入非空验证;错误提示;密码MD5加密;登录成功与失败的页面跳转
博客管理,分类管理,标签管理,归档管理,关于我,前端博客详情,评论回复;
记一个坑:遇到这么个问题:解析三目运算符的时候,有id的(编辑)路径正常,没有id(即新增)的报错ip不存在
原先采用了复制一份页面分开写;后来找到原因:在Controller中,去到新增的请求中要实例化一个对象:
// 点击新增,去到添加type的页面
@GetMapping("/types/input")
public String goToAddType(Model model) {
model.addAttribute("type", new Type()); //这里需要添加这一行代码
return "admin/types-input";
}
// ......
// 点击编辑去到修改Type页面
@GetMapping("/types/{id}/input")
public String editType(@PathVariable Long id, Model model) {
model.addAttribute("type", typeService.queryType(id));
return "admin/types-input";
}
项目打包与部署运行:
右侧maven中package,日志中可看到XXX-0.0.1-SNAPSHOT.jar的打包路径(一般都在target目录下)
复制此jar到任意目录下,运行命令java -jar jar包名来启动(当报错Unable to access jarfile FileEnDecrypt.jar时,可能环境变量配置有问题,jar包名使用绝对路径即可)
SpringBoot整合JSP:https://blog.csdn.net/hp_yangpeng/article/details/80521866