1.依赖管理
修改默认依赖版本:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<mysql.version>5.1.48</mysql.version>
</properties>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
再刷新maven即可, 默认不需要需改依赖版本号
spring-boot-starter-*:spring-boot某种场景依赖的集成、包括redis、cache等;
spring-boot所以支持的场景:https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
自定义与第三方依赖场景的命名规则:*-spring-boot-starter
所有场景启动器最底层的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.6.7</version>
<scope>compile</scope>
</dependency>
2.自动配置
spring-boot已经为我们配置好了所有web开发的常见场景
默认包结构:
主程序(main方法)所在的包及其下面的所有子包的组件都会被默认扫描进来
指定扫描包路径:
@RestController
@SpringBootApplication(scanBasePackages = "com")
public class MyApplication {...}
//或者
@RestController
//@SpringBootApplication(scanBasePackages = "com")
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com")
public class MyApplication {...}
各种配置都有默认值,这些配置最终都是映射到某个类上,这些类会在容器中创建对象
spring-boot是按需加载自动配置项的,按需加载取决于引入了哪些场景
spring-boot的所有配置功能都在
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.6.7</version>
<scope>compile</scope>
</dependency>
将组件配置入容器:
只有在容器中的组件才能使用springboot的强大功能
①编写配置类,用@Configuration标注,里面编写要注册入容器的类;
②向容器注册组件(类),可以使用@Bean,@Import,@ConditionalOnBean(条件注入)注入
/**
* @Configuration标注配置类、配置文件
* 配置类里面用@Bean给容器注册组件,默认也是单实例的
* proxyBeanMethods属性:代理bean方法,如果proxyBeanMethods属性是true,那么spring使用代理对象调用方法,多次调用组件方法springboot总会去容器里面获取这个对象,保持组件的单实例
* proxyBeanMethods=true是@Configuration的full模式,每次调用该类的方法返回的都是容器中的对象
* proxyBeanMethods=false是@Configuration的lite模式,每次调用该类的方法返回的是新的对象
* 设置true开启单例,false不开启单例
*/
@Configuration(proxyBeanMethods = true)
/**
* 在容器中自动创建这两个类型的组件
* 默认组件的id是组件的全类名
*/
//@Import({Director.class, JDBC42Helper.class})
public class MyConfig {
/**
* 被作为依赖的bean一定要写在依赖方法的上方,因为需要先加载被依赖的bean,才能加载依赖方法
* @return
*/
@Bean("myplayer")
public Player player(){
return new Player("刘德华",55);
}
/**
* 给容器添加组件,以方法名作为组件的id,返回值类型就是组件的类型,返回的值就是组件在容器中的实例
* @Bean的value属性可以自定义组件id
* @return
*/
@ConditionalOnBean(name = "myplayer")
@Bean("dianying")
public Movie movie(){
Movie cjh=new Movie("长津湖",2021);
cjh.setPlayer(player());
return cjh;
}
@Bean
public Director director(){
return new Director("张艺谋",35);
}
}
使用配置文件向容器注入组件:
@ImportResource(locations = "classpath:bean.xml") //往配置类中导入文件资源
public class MyConfig {...}
配置绑定:
方式一:@Component+@ConfigurationProperties(prefix = “mycom”)
@Component
@ConfigurationProperties(prefix = "mycom")
public class Company {
private String name;
private double price;
...
}
server.port=8888
mycom.name="北京文化"
mycom.price=5.78
方式二:@ConfigurationProperties(prefix = “mycom”)+@EnableConfigurationProperties(Company.class)
开启配置绑定功能,把组件自动注册到容器中
其中@EnableConfigurationProperties写在配置类上方
@EnableConfigurationProperties(Company.class)
public class MyConfig {...}
@ConfigurationProperties(prefix = "mycom")
public class Company {...}
3.自动配置源码解析
@SpringBootApplication由以下3个注解合成
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
@SpringBootConfiguration //代表当前是一个配置类
@ComponentScan //指定扫描的类、包
@EnableAutoConfiguration由以下构成
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}
@AutoConfigurationPackage解析:
@Import({Registrar.class}) //给容器导入组件
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
//利于Registrar给容器中导入一系列组件
//将主方法所在的包下的所有组件注册入容器
@Import({AutoConfigurationImportSelector.class})解析:
利用this.getAutoConfigurationEntry(annotationMetadata)给容器导入一些组件
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes) 获取所以需要导入到容器中的配置类
利用工厂加载Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) ,得到所有组件
从META-INF/spring.factories加载文件,默认扫描当前系统所有jar包里的META-INF/spring.factories位置的文件
spring-boot-test-autoconfigure-2.6.7.jar中的META-INF/spring.factories文件写死了spring-boot一启动就要往容器中注册的所有配置类
int c = run.getBeanDefinitionCount(); //c是143
//总共有143个自动配置启动的时候默认全部加载,但最终会按需加载,因为很多类都是@ConditionalOnXXX注解,只有在某种条件下才会注册入容器
SpringBoot会在底层配置好所有的组件,如果用户已经配置了,以用户的优先
总结:
- SpringBoot先加载所有的自动配置类,XXXXAutoConfiguration
- 每个自动配置类按照条件生效,并且都会默认绑定相应的配置类,XXXXProperties.class,配置类又正好绑定了XXXX.properties文件
- 生效的配置类就会往容器里面注册很多组件,于是这些功能就生效了
- 如果用户已经配置了组件,以用户的配置类优先
- 定制化配置的两种方式
- 用户直接自己@Bean替换底层组件
- 修改配置文件(application.properties)即可,此方式更常用
4.开发中实用的注解
简化Bean开发:lombok
- 安装依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
@ToString //生成ToString
@Data //设置getter和setter方法
@AllArgsConstructor //全部有参构造方法
@NoArgsConstructor //无参构造方法
@EqualsAndHashCode //重写Equals和HashCode方法
@Component
public class Adder {
private String province;
private String city;
}
@RestController
@Slf4j //日志注解
public class TestCon1 {
@RequestMapping("/test1")
public void test1(){
//打印日志
log.info("test1请求");
System.out.println(new Adder("浙江省","温州市"));
}
}
dev-tools:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
添加依赖,ctrl+f9进行热更新
5.yaml配置
SpringBoot同时会将application.properties与application.yml作为配置文件,如果两个文件配置了相同的属性,properties文件优先级更高
person:
#pName: '刘德华\n dadad' #单引号将特殊字符转义
#pName: "刘德华\n dadad" #双引号不转义特殊字符
pName: 刘德华\n dadad #不加引号效果与单引号一致,转义特殊字符
married: true
birth: 2013/05/04
age: 58
# interests: [唱,跳,rap] #数组写法
interests: #数组写法
- 篮球
- 足球
- 网球
animal: [猫,狗,猪]
# score: #对象写法
# English: 80
# Math: 75
# Chinese: 65
scope: {English: 80,Math: 75,Chinese: 65} #对象写法
salary:
- 9999
- 8888
- 7777
movie:
mName: 无间道
mUpYear: 2003
allMovie:
war: #数组里面存对象写法
- {mName: 辛德勒名单,mUpYear: 1998}
- mName: 敦刻尔克
mUpYear: 2008
science:
- mName: 后天
mUpYear: 2002
- mName: 降临
mUpYear: 2012
spring:
banner:
charset: UTF-8
对比properties,yml文件结构层次更清晰
开启yam文件对bean属性的自动提示功能(测试过但是没有效果)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
6.WEB开发简单功能分析
静态资源访问:
官方说明:By default, Spring Boot serves static content from a directory called /static
(or /public
or /resources
or /META-INF/resources
) in the classpath or from the root of the ServletContext
.
即类路径下的/static
(or /public
or /resources
or /META-INF/resources
)文件夹下的文件可以通过http://localhost:8080/+文件名访问,例如http://localhost:8080/5.jpg
原理:静态映射规则是/**,SpringBoot先去Controller找相应的请求,如果没有相应的请求,就去静态资源目录里面找资源,如果静态资源也没有找到则返回404
配置静态资源访问前缀,在使用拦截器的时候可以放行对应的静态资源:
spring:
mvc:
static-path-pattern: /res/** #该配置同时
配置了配置静态资源访问前缀后,静态资源文件的访问规则就变成了:当前项目+/res/+静态资源文件名,例如:http://localhost:8080/res/1.jpg
配置欢迎页:
在任意静态资源文件夹下配置index.html,即可通过http://localhost:8080/访问欢迎页,如果配置了静态资源访问前缀,就必须通过http://localhost:8080/res/index.html访问欢迎页
配置favicon.ico:
将favicon.ico文件放入static文件夹下,重启IDEA即可
7.获取请求参数
SpringBoot的底层仍然是SpringMVC,所以SpringMVC获取参数的方式依然可用,在此再补充几个获取请求参数的方式
使用Map集合获取Rest风格请求参数:
@GetMapping("/t1/{id}/aa/{name}")
public Map<String,String> test1(@PathVariable("id") int id,@PathVariable("name") String name,@PathVariable Map<String,String> pv){
return pv;
}
使用Map集合获取Header参数:
@GetMapping("/t1/{id}/bb/{name}")
public Map<String,String> test2(@RequestHeader Map<String,String> pv2){
return pv2;
}
使用List跟Map获取普通参数
@RequestParam("type") List<String> list,
@RequestParam Map<String,String> map
获取cookie参数:
@CookieValue("_bl_uid") String cookie
//或者
@CookieValue("_bl_uid") Cookie cookie2
获取POST请求体
@PostMapping("/t2")
public Map<String,Object> test4(@RequestBody String body){
Map<String,Object> allMap =new HashMap<>();
allMap.put("body",body);
return allMap;
}
获取请求域属性:
@GetMapping("/t2")
@ResponseBody
public Map<String,Object> t2(@RequestAttribute("msg") String msg,
@RequestAttribute("code") int code,
HttpServletRequest request){
...
}
获取矩阵变量:
//矩阵变量必须要有url路径变量才能解析
//请求:http://localhost:8080/con1/t3/adf;aa=aaa,bbb,ccc;dd=dddd
@GetMapping("/t3/{a}")
@ResponseBody
public Map<String,Object> t3(@PathVariable("a")String a,
@MatrixVariable("aa")List<String> list,
@MatrixVariable("dd") String d){
Map<String,Object> m=new HashMap<>();
m.put("a",a);
m.put("list",list);
m.put("d",d);
return m;
}
//http://localhost:8080/con1/t3/11;id=121/22;id=333
@GetMapping("/t3/{a}/{b}")
@ResponseBody
public Map<String,Object> t3(@MatrixVariable(value = "id",pathVar = "a") int aID,@MatrixVariable(value = "id",pathVar = "b") int bID){
Map<String,Object> m=new HashMap<>();
m.put("aid",aID);
m.put("bid",bID);
return m;
}
//有些版本的SpringBoot需要开启识别url里面的分号:
//在配置类里面:
//方式一
// @Override
// public void configurePathMatch(PathMatchConfigurer configurer) {
// UrlPathHelper urlPathHelper=new UrlPathHelper();
// urlPathHelper.setRemoveSemicolonContent(false);
// configurer.setUrlPathHelper(urlPathHelper);
// }
//方式二
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper=new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
通过对象属性获取参数:
@PostMapping("/t6")
@ResponseBody
public Movie t6(Movie movie) {
return movie;
}
//请求信息
mName----霸王别姬
mUpYear----2013
player.pName----张国荣
player.pAge----32
Model、Map里面的数据会被放在同一个请求域中:
@GetMapping("/t4")
public String t4(Map<String,Object> map, Model model, HttpServletRequest request, HttpServletResponse response){
map.put("wjd","无间道");
model.addAttribute("bwbj","霸王别姬");
request.setAttribute("ht","后天");
response.addHeader("header","这是header");
return "forward:/con1/t5";
}
@GetMapping("/t5")
@ResponseBody
public Map<String,Object> t5(HttpServletRequest request){
Map<String,Object> m=new HashMap<>();
m.put("data1",request.getAttribute("wjd"));
m.put("data2",request.getAttribute("bwbj"));
m.put("data3",request.getAttribute("ht"));
return m;
}
8.内容协商
根据请求头的accept与服务器协商响应格式
Accept | application/xml |
---|---|
Accept | application/json |
开启基于参数的内容协商:
spring:
mvc:
contentnegotiation:
favor-parameter: true
请求路径带上内容协商格式参数:
http://localhost:8080/con1/t4?format=json
http://localhost:8080/con1/t4?format=xml
自定义内容协商:
@Configuration
public class MyConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new YwxkConverter());
}
};
}
}
/**
* 自定义converter
*/
public class YwxkConverter implements HttpMessageConverter<Movie> {
@Override
public boolean canRead(Class clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class clazz, MediaType mediaType) {
return clazz.isAssignableFrom(Movie.class);
}
/**
* 设置服务器能输出的自定义协商内容
* @return
*/
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/ywxk");
}
@Override
public Movie read(Class<? extends Movie> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
@Override
public void write(Movie movie, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//写出去自定义数据
String strs=movie.getmName()+";"+movie.getmUpYear();
OutputStream out=outputMessage.getBody();
out.write(strs.getBytes(StandardCharsets.UTF_8));
}
}
//浏览器参数format=yw方式
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
//map参数
Map<String, MediaType> map=new HashMap<>();
map.put("json",MediaType.APPLICATION_JSON);
map.put("xml",MediaType.APPLICATION_XML);
map.put("yw",MediaType.parseMediaType("application/ywxk"));
//指定支持解析哪些参数对应的哪些媒体类型
ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(map);
//设置format参数为ff参数
//strategy.setParameterName("ff");
HeaderContentNegotiationStrategy headerStrategy = new HeaderContentNegotiationStrategy();
configurer.strategies(Arrays.asList(strategy,headerStrategy));
}
9.视图解析与模板引擎
SpringBoot默认不支持jsp,需要引入第三方模板引擎实现视图渲染
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
10.配置拦截器
@Configuration
public class MyConfig implements WebMvcConfigurer {
//添加拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截器默认拦截所有请求,包括所有静态资源路径,不拦截/con1/t7,/con1/t8
registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/con1/t7","/con1/t8");
}
}
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//编写拦截逻辑
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
11.文件上传
@RequestMapping("/image")
public String upload(@RequestPart("file1")MultipartFile file1,
@RequestPart("file2")MultipartFile[] file2) throws IOException {
if(!file1.isEmpty()){
String newFile1=file1.getOriginalFilename();
file1.transferTo(new File("D:/temp/"+newFile1));
}
if(file2.length>0){
for (MultipartFile file2Con: file2) {
if(!file2Con.isEmpty()){
String file2ConName=file2Con.getOriginalFilename();
file2Con.transferTo(new File("D:/temp/"+file2ConName));
}
}
}
log.info("单个文件大小:"+file1.getSize()+"---多文件数量:"+file2.length);
return "测试";
}
spring:
servlet:
multipart:
max-file-size: 15MB #单文件最大上传大小
max-request-size: 100MB #一次请求最大上传大小
12.异常处理
如果是客户端发送的请求发生异常,SpringBoot返回json;
如果是浏览器发送的请求发生异常,SpringBoot返回error页面;
配置错误静态页自动映射异常
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.html
+- <other templates>
错误页面配置在public/error或者templates/error目录下均可
SpringBoot会根据异常状态码找到error目录下相应的静态页
自定义处理异常:
方案一:@ControllerAdvice+@ExceptionHandler,会自动捕获对应的异常
底层是ExceptionHandlerExceptionResolver提供的支持
/**
* 处理整个web controller异常
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
//异常处理器
@ExceptionHandler({ArithmeticException.class,NullPointerException.class})
public String mathExceptionHandler(Exception e){
log.error("异常是:{}",e);
//返回视图地址
return "error/mathError";
}
}
方案二:自定义异常类,需要手动抛出
底层是ResponseStatusExceptionResolver提供的支持
@ResponseStatus(value=HttpStatus.BAD_GATEWAY,reason = "我的错误提示")
public class MyException extends RuntimeException{
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
}
@GetMapping("/t10")
@ResponseBody
public String t10() {
if(1==1){
throw new MyException();
}
return "t9方法";
}
//框架底层的处理异常
DefaultHandlerExceptionResolver
方案三:基于HandlerExceptionResolver的自定义异常
@Order(value = Ordered.HIGHEST_PRECEDENCE) //异常优先级,也可以给数字,数字越小,优先级越高
@Component
public class CustomerHandlerException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
response.sendError(520,"520错误");
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
13.SpringBoot注入原生Servlet、listener、Filter
第一种方式:
@ServletComponentScan(basePackages = "com.ywxk.demo3")
@SpringBootApplication
public class Demo3Application {
public static void main(String[] args) {
SpringApplication.run(Demo3Application.class, args);
}
}
@Slf4j
@WebFilter(urlPatterns = {"/con1/*"})
public class MyFilter implements Filter {
//实现方法
}
@WebServlet(urlPatterns = "/my")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("myServlet");
}
}
@WebListener
@Slf4j
public class MyListener implements ServletContextListener {
//重写方法
}
第二种方式:
@ServletComponentScan(basePackages = "com.ywxk.demo3")
@SpringBootApplication
public class Demo3Application {
public static void main(String[] args) {
SpringApplication.run(Demo3Application.class, args);
}
}
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("myServlet");
}
}
//filter、listener与上面一样,不需要@WebListener、@WebFilter
//proxyBeanMethods = true保证依赖的组件始终是单实例的
@Configuration(proxyBeanMethods = true)
public class MyRegisterConfig {
@Bean
public ServletRegistrationBean myServlert(){
MyServlet myServlet = new MyServlet();
return new ServletRegistrationBean(myServlet,"/my","/my2");
}
@Bean
public FilterRegistrationBean myFilter(){
MyFilter myFilter = new MyFilter();
//return new FilterRegistrationBean(myFilter,myServlert());
//或者
FilterRegistrationBean<MyFilter> myFilterBean = new FilterRegistrationBean<>(myFilter);
myFilterBean.setUrlPatterns(Arrays.asList("/my","/con1/*"));
return myFilterBean;
}
@Bean
public ServletListenerRegistrationBean myListener(){
return new ServletListenerRegistrationBean(new MyListener());
}
}
14.jdbc方式访问数据库
①导入jdbc场景与驱动
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
②配置数据库信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/movie
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jdbc:
template:
query-timeout: 3 #超时时间3秒
③使用
@Autowired
JdbcTemplate jdbcTemplate;
@Test
public void t1(){
int c= jdbcTemplate.queryForObject("select count(*) from y_movie",Integer.class);
log.info("条数:{}",c);
}
15.整合Druid数据源
方式一:手动引入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
@Configuration
public class MyDataSourceConfig {
//从yaml文件引入数据库名称,账号,密码
@ConfigurationProperties("spring.datasource")
@Bean
public DataSource dataSource() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
//也可以在yaml文件设置spring.datasources.filters: wall,stat
druidDataSource.setFilters("stat,wall");
return druidDataSource;
}
/**
* 配置druid监控页功能
* @return
*/
@Bean
public ServletRegistrationBean servletRegistrationBean(){
StatViewServlet statViewServlet=new StatViewServlet();
ServletRegistrationBean<StatViewServlet> registrationBean=new ServletRegistrationBean<>(statViewServlet,"/druid/*");
//设置监控页的访问账号密码
registrationBean.addInitParameter("loginUsername","root");
registrationBean.addInitParameter("loginPassword","root");
return registrationBean;
}
/**
* WebStatFilter 采集web-jdbc关联监控的数据
*/
@Bean
public FilterRegistrationBean webStatFilter(){
WebStatFilter webStatFilter = new WebStatFilter();
FilterRegistrationBean<WebStatFilter> bean = new FilterRegistrationBean<>(webStatFilter);
bean.setUrlPatterns(Arrays.asList("/*"));
bean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return bean;
}
}
方式二:druid-spring-boot-starter
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
扩展配置项:
spring:
datasource:
url: jdbc:mysql://localhost:3306/movie
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
druid:
filters: stat,wall #开启底层功能,stat(sql监控功能),wall(防火墙功能)
filter:
stat:
slow-sql-millis: 1000 #超过1秒的语句都是慢查询
log-slow-sql: true #记录慢查询
enabled: true
wall:
enabled: true
stat-view-servlet: #配置监控页功能
enabled: true
login-username: root
login-password: root
reset-enable: false
web-stat-filter: #监控web
enabled: true
url-pattern: /*
aop-patterns: com.ywxk.demo3.* #监控SpringBean
16.整合mybatis
方式一:配置文件模式
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
mybatis:
#config-location: classpath:mybatis/mybatis-config.xml #mybatis全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml #mapper映射文件位置
configuration: #指定mybatis的全局配置,该配置与config-location不能同时存在
map-underscore-to-camel-case: true
//mapper接口一定要标注@Mapper
//或者在Application类上@MapperScan("com.ywxk.demo3.mapper")来批量扫描mapper文件进入容器
@Mapper
public interface MovieMapper {
public Movie getMovie(int id);
}
@Service
public class MovieService {
@Autowired
MovieMapper movieMapper;
public Movie getMovieById(int id){
return movieMapper.getMovie(id);
}
}
@GetMapping("/t12")
@ResponseBody
public Movie t12(@RequestParam("id") int id){
Movie movie=movieService.getMovieById(id);
return movie;
}
方式二:纯注解方式
@Mapper
public interface DirectorMapper {
@Select("select * from y_director where d_id=#{id}")
public Director getDirectorById(int id);
}
@Service
public class DirectorService {
@Autowired
public DirectorMapper directorMapper;
public Director getDirectorById(int id){
return directorMapper.getDirectorById(id);
}
}
@GetMapping("/t13")
@ResponseBody
public Director t13(@RequestParam("id") int id){
return directorService.getDirectorById(id);
}
方式三:混合写法
<mapper namespace="com.ywxk.demo3.mapper.DirectorMapper">
<insert id="insertDirector" useGeneratedKeys="true" keyProperty="d_id">
insert into y_director(d_name,d_age)
values (#{d_name},#{d_age})
</insert>
</mapper>
@Mapper
public interface DirectorMapper {
@Select("select * from y_director where d_id=#{id}")
public Director getDirectorById(int id);
// @Insert("insert into y_director(d_name,d_age) values (#{d_name},#{d_age})")
// @Options(useGeneratedKeys = true,keyProperty = "d_id")
public int insertDirector(Director director);
}
public int insertDirector(Director director){
return directorMapper.insertDirector(director);
}
@GetMapping("/t15")
@ResponseBody
public Director t15(Director director){
directorService.insertDirector(director);
return director;
}
17.整合Mybatis-plus
详见:https://blog.csdn.net/y_w_x_k/article/details/125208908
18.整合Redis
待补充…
19.单元测试JUnit5
https://blog.csdn.net/y_w_x_k/article/details/125221282
20.指标监控
开启指标监控有两种方式:
第一种:在cmd里输入jconsole
第二种: http方式
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management: #management是所有actuator的配置起点
endpoint: #详细配置某一个端点
health:
show-details: always
enabled: true #开放指定端点
info:
enabled: true
endpoints:
enabled-by-default: true #是否开启所有监控端点
web:
exposure:
include: '*' #以web方式暴露所有端点
info:
appName: testSpringBoot
appVersion: 1.0
mavenProjectName: @project.artifactId@
mavenParentName: @project.parent.artifactId@
使用:
http://localhost:8080/actuator/beans
http://localhost:8080/actuator/metrics
http://localhost:8080/actuator/metrics/application.ready.time
常用的endpoints:
Health:监控状况
Metrics: 运行时指标
Loggers: 日志记录
定制EndPoint:
//定制Health
@Component
public class MyComHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
//这里编写判断应用健康代码
Map<String,Object> map=new HashMap<>();
if(1==2){
builder.status(Status.UP);
map.put("wjd","无间道");
map.put("bwbj","霸王别姬");
}else {
builder.status(Status.OUT_OF_SERVICE);
map.put("dkek","敦刻尔克");
}
builder.withDetail("code",200).withDetails(map);
}
}
//定制Info
@Component
public class AppInfo implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("blsm","北落师门").withDetail("lh","猎户座").withDetails(Collections.singletonMap("tjs","天津四"));
}
}
//定制Metrics
@Service
public class MovieService {
@Autowired
MovieMapper movieMapper;
Counter counter;
public MovieService(MeterRegistry meterRegistry){
counter = meterRegistry.counter("moviecounter");
}
public Movie getMovieById(int id){
counter.increment();
return movieMapper.getMovie(id);
}
}
//定制endpoint
@Component
@Endpoint(id = "myServiceEndpoint")
public class MyServiceEndpoint {
@ReadOperation
public Map getDockerInfo(){
return Collections.singletonMap("dockerInfo","docker started");
}
@WriteOperation
public void updateDocker(){
System.out.println("docker stop");
}
}
21.服务器监控
监控应用配置:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.5.1</version>
</dependency>
server.port=8888
@Configuration
@EnableAdminServer
@SpringBootApplication
public class Demo4Application {
public static void main(String[] args) {
SpringApplication.run(Demo4Application.class, args);
}
}
被监控应用:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.5.1</version>
</dependency>
spring:
boot:
admin:
client:
url: http://localhost:8888/
instance:
prefer-ip: true
22.profile生产与开发环境切换
application.properties:默认配置环境,永远会被加载,与环境配置文件同时生效,如果有同名配置,以环境配置文件优先
server.port=8080
#配置环境
spring.profiles.active=test
application-prod.yaml: 生产环境配置文件
movie:
name: prod-十面埋伏
server:
port: 8000
application-test.yaml: 测试环境配置文件
movie:
name: test-天地英雄
server:
port: 7000
也可以命令行修改运行环境与配置:
java -jar demo3-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod --movie.name=无极
@Profile条件装配
public interface Movie2 {
String getName();
int getUpYear();
}
//生产环境是LoveMovie生效
@Profile("prod")
@Component
@ConfigurationProperties("movie2")
@Data
public class LoveMovie implements Movie2{
private String name;
private int upYear;
}
//测试环境是WarMovie生效
@Profile("test")
@Component
@ConfigurationProperties("movie2")
@Data
public class WarMovie implements Movie2{
private String name;
private int upYear;
}
配置分组
#如果环境配置内容较多的话,可以将一个配置文件分成多个,然后这些配置文件归为一组配置,同组文件内容会自动合并
#生产环境组
spring.profiles.group.production[0]=prod
spring.profiles.group.production[1]=prod2
#测试环境组
spring.profiles.group.production[0]=prod
23.配置文件
SpringBoot默认会去以下路径读取application.properties与application.yaml配置文件
- resources目录下
- resources目录下的config目录下
- jar包同级的目录下(可以在服务器上临时修改配置)
- jar包同级的目录下的config目录下(可以在服务器上临时修改配置)
- jar包同级的目录下的config目录下的一级子目录下(可以在服务器上临时修改配置)
如果配置属性相同,指定环境优先,jar包外部优先