什么是Spring
Spring是一个开源框架,Spring是为了解决企业级应用开发的复杂性而创建的,简化开发
Spring如何简化开发
无需添加太多第三方组件,只会拓展,不会改动太多代码
可以通过切面和模版减少样式代码
什么是Spring Boot
Spring Boot是基于简化Spring的应用开发而设计出来的一款开源框架,=就是一个javaweb的开发框架,类似于SpringMVC,但是更加简便。
其可以减少项目的配置和启动时间,并且简化项目部署的难度
但是Spring Boot本身并不提供Sping框架的核心特征和拓展,其整合了所有框架,并且mave整合了所有jar包,使用十分简便快速
什么是微服务
微服务并不是一种具体的服务框架,微服务是一种架构风格,它要求我们在开发一个应用是,这个应用必须构建成一系列小服务的组合,也就是通过多个小服务来组成一个大型应用,它可以通过http等方式进行互通。
单体应用架构
单体应用架构(all in one) 这个架构是指我们将一个应用中的所有应用服务都封装在一个应用中。
也就是说我们将数据库,web访问等任何服务功能都放到一个war包中。
这样其实可以预想到,对于开发来说确实十分方便和部署,但是如果后期进行任何维护时,我们需要将所有服务都停掉才能进行更新,十分的不便,并且当有很多人访问时,对服务器的配置要求随之增高,对于一些大型项目来说,all in one结构也很难合作开发。
因此,微服务等框架开始流行
微服务架构(Microservice Architecture)
微服务架构风格也就是将原来一个应用程序插分成多个单独的服务,并将其自由组装,其主要使用的风格也就是面向接口编程,每个微服务通常可能暴力一组清晰的API接口,这些接口定义了外部系统与微服务之间的交互方式,并且其也关系服务的独立性和分布式部署。
也就是说相对于all in one架构来说,微服务风格将每一个业务分割成了相对独立的不同服务,并且将这些不同服务进行组装,让其实现与一个和整体架构同样的功能。
这样的模式可以节省调用的资源,并且每个功能可以独自升级,互不影响,并且在开发时,所有的业务都可以由单独的团队进行,这使应用的开发变得更加高效。
当然,微服务也有很多的缺点,它将运维难度大大增大,因为每一个业务都是独立的,所以每一个业务都需要单独运维。
推荐文章:
https://martinfowler.com/articles/microservices.html
https://www.cnblogs.com/liuning8023/p/4493156.html
第一个Spring boot程序
使用idea里面的Spring Initializr进行创建,然后等他加载就行
生成出来主函数长这样
package org.example.spring_booot_learn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBoootLearnApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoootLearnApplication.class, args);
}
}
然后创建一个controller包,下面写一个Hello类
package org.example.spring_booot_learn.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Hello {
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
}
访问127.0.0.1:8080即可看到返回Hello World。
更改配置文件与banner界面
在,application.properties文件中,这里面默认为空,也就是一切都是默认配置
其中可以更改很多东西
#更改端口号
server.port=8068
修改banner界面
再resources包中创建一个banner.txt
然后将想放的东西放进去就行
___ __ ____
/ __) /__\ (_ _)
( (__ /(__)\ )(
\___)(__)(__)(__)
这样即可修改banner界面
配置原理
自动配置:
pom.xml
- spring-boot-dependencies:核心依赖在父工程中,也就是spring-boot-starter-parent,可以跳转进入查看其中的配置文件。
- 在写和引入一些Spring-Boot依赖时,不需要指定版本,因为moven里有对应的版本库
启动器:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 启动器:也就是Spring-Boot的启动场景;
- 比如spring-boot-starter-web,其就会直接自动导入web环境所有的依赖
- springboot会将所有的场景和功能都变成一个个启动器
- 如果需要使用任何功能,只需要找到对应的启动器即可
主程序
package org.example.spring_booot_learn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication //标注这个类是一个springboot的应用
public class SpringBoootLearnApplication {
public static void main(String[] args) { //将springboot应用启动
SpringApplication.run(SpringBoootLearnApplication.class, args);
}
}
- 注解
- 可以跳转到SpringBootApplication注解中,发现它是一个复合注解,里面有很多的其他注解。
@SpringBootConfiguration //SpringBoot的配置,再次跳转这个注解
@Configuration:spring配置类
@Conponent:说明这也是一个spring的组件
@EnableAutoConfiguration //自动配置
@AutoConfigurationPackge //自动导入配置包
@Import({AutoConfigurationPackages.Registrar.class}) //自动配置"包注册"
@Import({AutoConfigurationPackages.class}) //自动配置导入选择
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); //获取所有的配置
获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
META-INF/spring.factories:自动配置的核心文件,在17中源码里为这个包
org.springframework.boot.autoconfigure
可以进入包中查看,spring-autoconfigure-metadata.properties文件,在这个文件里有很多的配置文件。
【SpringBoot】自动装配原理(简单易懂)-腾讯云开发者社区-腾讯云
- springboot在启动中,从类路径下/META-INF/spring.factories获取指定的值
- 将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置
- 以前我们需要自己配置的东西,现在spring-boot已经可以自动配置了
- 整合javaEE,解决方案和自动配置的东西都在spring-boot-a下utoconfigure.jar这个包下
- 它会把所有需要导入的组件,以类名的方式返回,,然后根据类名把这个组件导入进去,这些组件就会被添加到容器。
- 容器中也会存在很多的xxxAutoConfiguration的文件,就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig
- 有了自动配置类,可以让我们不用手动编写配置文件。
结论:Spring-Boot所有的自动配置都是在启动的时候扫描并加载:spring.factpries所有的自动配置类都在这里面,但是不一定生效,要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器,自动装配就会生效,然后配置成功
yaml语法
同样在application文件里,但是不要使用其自带的.properties文件,而是新建一个.yaml文件
也就是说,我们可以将idea生成的application.properties删除,新建一个名字为application.yaml的文件。
yaml是什么
yaml是一种可读性高,用来表达数据序列化的格式。
yaml格式:
server:
port: 8081
语法
# 普通的key-value
name: cat
# 对象
student:
name: cat
age: 100
# 行内写法
student: {name: cat,age: 3}
# 数组
pets:
- cat
- dog
- pig
pets: [cat,dog,pig]
tip:yaml对空格要求十分严格,要注意使用
优势
yaml可以直接给实体类赋值
先定义一个Dog类
package org.example.spring_booot_learn.pojo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Dog {
@Value("dog")
private String name;
@Value("100")
private Integer age;
public Dog(){}
public Dog(String name,Integer age){
this.age=age;
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后去test里面进行测试
package org.example.spring_booot_learn;
import org.example.spring_booot_learn.pojo.Dog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBoootLearnApplicationTests {
@Autowired
private Dog dog;
@Test
void contextLoads() {
System.out.println(dog);
}
}
然后运行可以输出dog的信息
然后构造一个person类
package org.example.spring_booot_learn.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Dog dog;
public Person(String name, Dog dog) {
this.name = name;
this.dog = dog;
}
public Person(){}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", dog=" + dog +
'}';
}
}
使用application.yaml,将其进行赋值
person:
name: cat
dog:
name: dog
age: 100
其中person类里,@ConfigurationProperties(prefix = “person”) 注释将yaml和person类相绑定,使其有注入的值
package org.example.spring_booot_learn;
import org.example.spring_booot_learn.pojo.Dog;
import org.example.spring_booot_learn.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBoootLearnApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
运行可以发现成功输出对应值。
并且,在yaml中可以使用很多功能,比如:可以在yaml中使用随机符号,还有初始值
person:
name: cat${random.uuid} //生成随机uuid
dog:
name: ${person.hello:hello}_dog //如果没有person.hello则输出hello_dog
age: 100
同样,也可以加到Dog中
person:
name: cat${random.uuid}
dog:
name: ${person.hello:hello}_dog
age: 100
dog:
name: goog
age: 8888
同样可以进行赋值
JSR303数据校验
JSR303是什么
JSR303是java的一种规范,定义了用于数据验证(数据教验)的标准接口和注解。JSP 303主要是用于对java类的字段进行验证,比如确保数据符合一定的约束条件(例如非空,最大值,最小值,长度,正则表达式等)。
通俗来说就是规定输入数据的格式。
实现
在spring boot中,是通过@Validated注解的方式进行校验的
import javax.validation.constraints.*;
public class User {
@NotNull(message = "用户名不能为空")
private String username;
@Email(message = "请输入有效的电子邮件地址")
private String email;
@Min(value = 18, message = "年龄不能小于18")
@Max(value = 120, message = "年龄不能大于120")
private int age;
// Getters and Setters
}
Spring Boot Web开发
导入静态资源
在java17中,我们找到WebMvcAutoConfiguration文件中的加载静态资源的源码
![[Pasted image 20241120210151.png]]
继续跟进到getStaticLocations中
![[Pasted image 20241120210233.png]]
看到这里,发现他的staticLocations=CLASSPATH_RESOURCE_LOCATIONS
而CLASSPATH_RESOURCE_LOCATIONS在前面被定义了
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};
也就是说静态资源加载位于
classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/
这些目录中,
![[Pasted image 20241120210429.png]]
所以只要我们将.js,.html,.css等文件放入即可加载到项目中
并且优先级如下:resources>static>public
首页编写
可以再到WebMvcAutoConfiguration中查看源码,看到WelcomePageNotAcceptableHandlerMapping类。
其中代码如下
public WelcomePageNotAcceptableHandlerMapping welcomePageNotAcceptableHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
return (WelcomePageNotAcceptableHandlerMapping)this.createWelcomePageHandlerMapping(applicationContext, mvcConversionService, mvcResourceUrlProvider, WelcomePageNotAcceptableHandlerMapping::new);
}
private <T extends AbstractUrlHandlerMapping> T createWelcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider, WelcomePageHandlerMappingFactory<T> factory) {
TemplateAvailabilityProviders templateAvailabilityProviders = new TemplateAvailabilityProviders(applicationContext);
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
T handlerMapping = factory.create(templateAvailabilityProviders, applicationContext, this.getIndexHtmlResource(), staticPathPattern);
handlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
handlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return handlerMapping;
}
看到这一行
T handlerMapping = factory.create(templateAvailabilityProviders, applicationContext, this.getIndexHtmlResource(), staticPathPattern);
发现getIndexHtmlResource,跟进多次后可以看到其默认主页为index.html
private Resource getIndexHtmlResource(String location) {
return this.getIndexHtmlResource(this.resourceLoader.getResource(location));
}
private Resource getIndexHtmlResource(Resource location) {
try {
Resource resource = location.createRelative("index.html");
if (resource.exists() && resource.getURL() != null) {
return resource;
}
} catch (Exception var3) {
}
thymeleaf模版引擎
去pom.xml中选择编辑启动器添加Thymeleaf即可
![[Pasted image 20241120212842.png]]
同样进入ThymeleafProperties中
可以看到他解析的目录和要求
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
就是templates目录下
并且templates目录下的文件只允许由Controller进行调用,不允许直接调用
在templates目录下新建一个test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
HELLO
</body>
</html>
并且写一个testController类
package com.learn.weblearn.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/")
public String index(){
return "test";
}
}
访问http://127.0.0.1:8080/即可直接访问到test.html中
怎么传入数据到html里
使用th:方法进行传入
test.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${msg}"></h1>
</body>
</html>
任何标签都可以使用th进行绑定
package com.learn.weblearn.controller;
import jdk.javadoc.doclet.DocletEnvironment;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/")
public String index(Model module){
module.addAttribute("msg","flag");
return "test";
}
}
安全的转义和不安全的转义,使用utext标签可能会导致xss漏洞。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${msg}"></div>
<div th:utext="${msg}"></div>
</body>
</html>
package com.learn.weblearn.controller;
import jdk.javadoc.doclet.DocletEnvironment;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/")
public String index(Model module){
module.addAttribute("msg","<h1>flag</h1>");
return "test";
}
}
运行后可以发现后面的msg将h1进行解析了
遍历数据,使用each进行遍历
package com.learn.weblearn.controller;
import jdk.javadoc.doclet.DocletEnvironment;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Arrays;
@Controller
public class IndexController {
@RequestMapping("/")
public String index(Model module){
module.addAttribute("msg","<h1>flag</h1>");
module.addAttribute("users", Arrays.asList("folly","cat"));
return "test";
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div th:text="${msg}"></div>
<div th:utext="${msg}"></div>
<h3 th:each="user:${users}" th:text="${user}"></h3>
</body>
</html>
员工管理系统学习
首先是静态资源和html代码,使用的是网上的模版,课程为狂神的spingboot课程,更改成了springboot最新版可以运行的版本
其中实现了员工功能的增删改查功能
主要代码
实现登录拦截权限管理
package com.learn.weblearn.config;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
@Configuration
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception{
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser==null){
request.setAttribute("msg","没有权限,请先登录");
request.getRequestDispatcher("/").forward(request,response);
return false;
}else {
return true;
}
}
}
实现重定向和拦截器的规定
package com.learn.weblearn.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//拓展springmvc
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
//重写父类或者接口的方法
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//拦截不包括。。。的所有请求 /** registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login.html", "/", "/user/login",
"/asserts/**","/favicon.ico","/delemp/**");
}
}
员工增删改查
package com.learn.weblearn.controller;
import com.learn.weblearn.dao.DepartmentDao;
import com.learn.weblearn.dao.EmployeeDao;
import com.learn.weblearn.pojo.Department;
import com.learn.weblearn.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
@Autowired
private DepartmentDao departmentDao;
@RequestMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emp",employees);
return "emp/list.html";
}
@GetMapping("/emp")
public String toAddpage(Model model){
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "emp/add";
}
@PostMapping("/emp")
public String addEmp(Employee employee){
System.out.println("save"+employee);
employeeDao.save(employee);
return "redirect:/emps";
}
@GetMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id")Integer id,Model model){
Employee employee =employeeDao.getEmployeeById(id);
model.addAttribute("emp",employee);
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments",departments);
return "emp/update.html";
}
@PostMapping("/updateEmp")
public String updaateEmp(Employee employee){
employeeDao.save(employee);
return "redirect:/emps";
}
@PostMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id")Integer id){
employeeDao.delete(id);
return "redirect:/emps";
}
}
所有源码分享(百度网盘版)
通过百度网盘分享的文件:weblearn.zip
链接:https://pan.baidu.com/s/1Ebg8Cu0qSYfukhHdCY8yQg?pwd=4444
提取码:4444
--来自百度网盘超级会员V5的分享
标签:SpringBoot,springframework,name,学习,org,import,public,String
From: https://blog.csdn.net/2301_80148821/article/details/144277331