spring系列更加讲究高内聚,低耦合
springboot可以更快的构建一个个功能独立的微服务应用单元,快速构建应用,大型分布式网络服务的调用,就用springcloud来完成,来实现分布式,在分布式中间进行数据计算、批处理就是用springclouddataflow。
代码研究越来越深学的越来越多就会发现框架这些东西的产生都是为了解决代码的复杂性而创建的,都是为了高内聚,低耦合,springboot是一个能够快速开发应用的一个框架,它解决了ssm框架配置的繁琐性,让开发更加简单。
第一个springboot程序
pom依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--声明这是一个springboot项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.zjh</groupId>
<artifactId>springboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--包含创建web项目的各种依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--springboot打包成jar包的必须配置-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
springboot运行类:必须加上注解
package cn.zjh;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
controller:
package cn.zjh.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//是responsebody让方法返回的是字符串而不是跳转页面注解
//和controller的合体注解
@RestController
public class Controller {
@RequestMapping("/hello")
public String test(){
return "hello,word!";
}
}
application.properties:springboot的配置
基础配置:更改端口号:
server.port=8888
configuration配置
在spring中给类的属性赋值需要在配置文件中,springboot没有配置文件,但是可以使用configuration注解来实现配置
package cn.zjh.config;
import cn.zjh.pojo.Dog;
import cn.zjh.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration//表示这是一个配置类,即spring的配置文件
public class Myconfig {
@Bean//给容器添加组件,方法名就是组件id,返回类型就是组件类型,返回的值就是容器中的实例
public User user(){
return new User("张三",18);
}
@Bean
public Dog dog(){
return new Dog("旺财");
}
}
configuration注解的类本身也是一个组件,类里面的方法注册成组件之后就只有一个实例对象,不管new出多少个都是同一个,即被注解的类的方法默认单实例,如果需要改变就需要添加以下代码:
@Configuration(proxyBeanMethods = false)
这样创建的每个对象都不是同一个对象了,如果需求没有要求对象都是同一个,可以在注解上加上这一配置,可以加快类加载速度。不用再去扫描这个方法有没有被new过。
检查:
package cn.zjh;
import cn.zjh.pojo.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// 返回到IOC容器
ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
// 从容器中获取组件
User user = run.getBean("user", User.class);
System.out.println(user);
}
}
在运行之后也是能够查看到成功给类属性赋值。
配置绑定
类的属性赋值不仅可以在config里面赋值,也可以在springboot配置文件中进行一个赋值,在配置文件中创建一个对象:
mycat.name=chenglang
mycat.age=18
这个对象叫做mycat,有两个属性,即name和age。
在类中进行绑定:
@Component
//注册到容器中
@ConfigurationProperties(prefix = "mycat")
//绑定配置文件中的对象
public class Cat {
private int age;
private String name;
}
测试:
@Autowired
Cat cat;
@RequestMapping("/cat")
public Cat cat(){
return cat;
}
第二种方法:
在config中配置到容器中,即类中只需要绑定配置文件中的对象:
@ConfigurationProperties(prefix = "mycat")
public class Cat {
private int age;
private String name;
}
config装配到容器:
@EnableConfigurationProperties(Cat.class)
public class Myconfig {
yml配置
基本格式:
user:
# string类型
name: zhansan
# 布尔类型
boos: true
# 日期
birth: 1999/07/07
# 数值
age: 18
# 引用类型
dog:
name: 旺财
# 数组
perple: [张三,李四,王五]
# list
animal:
- 狗
- 猫
设置yml提示:导入依赖
<!--让yml具有提示功能,方便写代码-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.6.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--让打包的文件不带上spring-boot-configuration-processor这个jar包,节省资源-->
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
web开发场景
静态资源配置
静态资源一般放在static包下,当然还可以放在其他包下,可以新建public,
resources,或者新建META-INF下resources,这四个路劲都可以通过域名加静态资源名直接访问。
当然在使用拦截器等等的时候,不希望也让静态资源使用到拦截器,就需要更改静态资源的访问路径因为默认的访问路径为
http:localhost:8080/**
可以在springboot配置文件中添加以下代码更改静态资源访问路径:
spring:
mvc:
static-path-pattern: /自定义访问路径名/**
这样静态访问路径就变成
http:localhost:8080/自定义访问路径名/**
当然也不是一定要存放在这四个文件夹下,可以自定义静态资源存放包:
然后通过springboot来约束访问路径:
spring:
resources:
static-locations: [classpath: /自定义的包名/]
欢迎页支持
欢迎页支持有两种表现方式,
一是只要在静态资源包下命名index的页面都会当成欢迎页。
二是controller会处理/index
自定义网站图标
制作web网站,springboot有一个非常使用且方便的功能,就是自定义网站图标,只要在静态资源包下把图标名字改成favicon.ico即可。
欢迎页和制作网站图标都有一个缺点,那就是不能让静态资源的访问路径自定义。加上了就会失效。
controller常用参数注解
@pathvariable
添加这个注解会获取地址栏的变量值
@RestController
public class Controller {
@RequestMapping("/{id}/{name}")
public Map<String,Object> User(@PathVariable("id") Integer id,
@PathVariable("name") String name,
@PathVariable Map<String,String> ma){
Map<String, Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("ma",ma);
return map;
}
}
前台:
<a href="3/张三">你好</a>
输出:
{"ma":{"name":"张三","id":"3"},"name":"张三","id":3}
@requestparm
获取请求参数:
@RequestMapping("/{id}/{name}")
public Map<String,Object> User(@RequestParam("age") Integer age,
@RequestParam("hoppy") List<String> hoppy,
@RequestParam Map<String,String> params){
Map<String, Object> map = new HashMap<>();
map.put("age",age);
map.put("hoppy",hoppy);
map.put("params",params);
return map;
}
前台:
<a href="3/张三?age=18&hoppy=篮球&hoppy=足球">你好</a>
输出:
{"params":{"age":"18","hoppy":"篮球"},"age":18,"hoppy":["篮球","足球"]}
@requestattribute
获取请求域中的值,在页面跳转到下个页面时,当前页面的值会放入到请求域中,就可以使用这个注解获取到请求域中的值。
@Controller
public class Mycontroller {
@RequestMapping("/u1")
public String u1(HttpServletRequest request){
request.setAttribute("age",18);
request.setAttribute("name","张家豪");
return "forward:/u2";
}
@ResponseBody
@RequestMapping("/u2")
public Map<String,Object> u2(@RequestAttribute("age") Integer age,
@RequestAttribute("name") String name){
HashMap<String, Object> map = new HashMap<>();
map.put("age",age);
map.put("name",name);
return map;
}
}
或者是直接使用请求域的方法获取:
@ResponseBody
@RequestMapping("/u2")
public Map<String,Object> u2(HttpServletRequest request){
HashMap<String, Object> map = new HashMap<>();
Object age = request.getAttribute("age");
Object name = request.getAttribute("name");
map.put("age",age);
map.put("name",name);
return map;
}
}
输出:
{"name":"张家豪","age":18}
Thymeleaf
表达式名字 | 语法 | 作用 |
---|---|---|
变量取值 | ${} | 获取请求域中的值 |
选择变量 | *{} | 获取上下文对象值 |
消息 | #{} | 获取国际化等值 |
连接 | @{} | 生成连接 |
片段表达式 | ~{} | 引入公共页面 |
初体验
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Thymeleaf是html页面,需要放在templates包下。
html页面使用thymeleaf需要添加头文件
<html lang="en" xmlns:th="http://www.thymeleaf.org">
编写controller:
package cn.zjh.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@org.springframework.stereotype.Controller
public class Controller {
@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","hello,word");
model.addAttribute("go","http://www.baidu.com");
return "hello";
}
}
编写页面:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf</title>
</head>
<body>
<!--使用thymeleaf要加上th,需要改变什么值就在冒号后面写上什么值,然后从作用域获取数据-->
<!--改变html中h1标签文本值-->
<h1 th:text="${msg}">hello</h1>
<!--改变跳转链接-->
<a href="#" th:href="${go}">跳转</a>
<!--因为获取的是一个超链接,也可以使用@{http://www.baidu.com}来获取-->
</body>
</html>
thymeleaf语法一般用在标签内部,如果需要在行内获取只需要添加两个【】【】即可,例如:
[[th:href="${go}]]
公共页面
公共页面的代码可以放在同一个html下,实现公共页面:
公共页面代码可以放在一个div里面:
<div th:fragment="hello">
公共页面代码
</div>
表示这个div下面的是公共页面代码,并取名叫做hello
获取公共页面代码有三种方式:
第一种方式使用insert获取,结果就是把整个公共代码包括这个div都放进h1 中
<h1 th:insert="div :: hello"></h1>
获取结果:
<h1>
<div>
公共代码
</div>
</h1>
第二种方式使用replace获取,结果就是把整个公共代码替换成h1的区域
<h1 th:replace="div :: hello"></h1>
获取结果:
<div>
公共代码
</div>
第三种方法使用include获取,结果就是把公共区域的容器标签替换成获取标签
<h1 th:include="div :: hello"></h1>
获取结果:
<h1>
公共代码
</h1>
语法解释:
div :: hello div不是指这个容器,而是这个html的名字,html页面下的公共代码区名字,th:fragment="hello"就是给公共代码区命名
web开发
登入问题
简单登入功能:
创建user类:
@Data
public class Account {
private String user;
private String pwd;
}
controller
@Controller
public class AccountController {
@RequestMapping(value = {"/","/login"})
public String login(Account account, Model model){
if("张家豪".equals(account.getUser()) && "123456".equals(account.getPwd())){
return "page";
}else {
return "login";
}
}
}
页面:
<form action="/login" method="post">
账号:<input type="text" name="user" id="user">
<br/>
密码:<input type="text" name="pwd" id="pwd">
<br/>
<input type="submit" value="登入">
<br/>
</form>
表单重复提交问题
这是一个最简单的登入功能,这样的登入功能有一个弊端,就是登入成功之后进入首页再次刷新会再进行一次登入,这样会出现表单重复提交问题,无疑是大大降低效率。可以使用重定向来解决问题。
@Controller
public class AccountController {
@RequestMapping(value = {"/","/login"})
public String login(Account account, Model model){
if("张家豪".equals(account.getUser()) && "123456".equals(account.getPwd())){
// 当登入成功之后,重定向转发到/page
return "redirect:/page";
}else {
return "login";
}
}
@RequestMapping("/page")
public String page(){
return "page";
}
}
这样也衍生出一个问题,当登入成功之后这个页面复制到别的浏览器不用登入也能打开,这样安全性就大大降低了,所以我们需要一个判断,判断是否登入成功。这样我们就要再添加一些功能。
@Controller
public class AccountController {
@RequestMapping(value = {"/","/login"})
public String login(Account account, Model model, HttpSession session){
if("张家豪".equals(account.getUser()) && "123456".equals(account.getPwd())){
// 登入成功之后把账号密码存放到session当中,用来判断是否登入
session.setAttribute("account",account);
// 当登入成功之后,重定向转发到/page
return "redirect:/page.html";
}else {
return "login";
}
}
@RequestMapping("/page.html")
public String page(HttpSession session){
// 判断是否登入,可以用拦截器,过滤器
Object account = session.getAttribute("account");
if (account != null){
return "page";
}else {
return "login";
}
}
}
拦截器
在解决表单重复提交的时候,判断是否登入这一功能模块应该使用拦截器去代替,使用拦截器需要单独创建一个类,这个类需要继承HandlerInterceptor。
package cn.zjh.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
public class LoginInterceptor implements HandlerInterceptor {
}
实现HandlerInterceptor三个方法
public class LoginInterceptor implements HandlerInterceptor {
// 目标方法执行之前执行什么操作
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return false;
}
// 目标方法执行之后执行什么操作
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
}
// 页面渲染以后执行什么操作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
}
}
判断用户是否登入应该放在方法执行之前进行一个判断,所以把判断用户是否登入的操作代码放进preHandle方法即可
// 目标方法执行之前执行什么操作
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 从session中获取user对象判断是否为null
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if(user == null){
// 拦截
request.setAttribute("msg","请登入:");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
// 放行
return true;
}
因为拦截器是web开发定制功能,所以需要配置进springboot容器中,所以需要一个config类来配置。
package cn.zjh.config;
import cn.zjh.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
//因为拦截器是web操作,所以需要继承WebMvcConfigurer
public class UserConfig implements WebMvcConfigurer {
// 配置拦截器需要实现一个专门的方法
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(new LoginInterceptor())
// 拦截所有请求
.addPathPatterns("/**")
// 放行返回到登入页面的请求
.excludePathPatterns("/login");
}
}
这个时候有涉及到应该拦截什么请求和放行什么请求了,一般拦截器使用都是拦截所有请求使用/**表示,需要放行的请求则需要继续添加,比如css样式等等的静态资源也应该放行,放行静态资源有两种方法
一个是在放行请求中慢慢添加静态资源包下资源
// 放行返回到登入页面的请求,比如返回登入请求,在static下面css、js包下的所有的静态样式
.excludePathPatterns("/login","/css/**","/js/**");
第二种方法是给所有的静态资源添加一个访问路径,这样就可以不用一个一个去添加了,在application.yml中配置:
spring.mvc.static-path-pattern=/statis/** 就可以直接拦截静态资源 .excludePathPatterns("/login","/static/**");
但是这样也有一个坏处,就是前端引用静态资源样式路径都需要重新改一遍。
整合mybaits
整合mybatis需要添加三个依赖,jdbc依赖,mysql依赖和mybatis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
注意事项:springboot的mysql默认版本是8版本以上,如果电脑安装的mysql是5版本需要更改依赖版本:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
整合mybatis同样需要它的核心配置文件,配置文件可以放在
resources-->mybatis中,给接口写的数据库操作mapper.xml也可以在
resources-->mybatis-->mapper包中。
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
mapper.xml
<?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>
</mapper>
创建完成之后则需要在springboot的核心配置文件中配置
#配置mybatis
mybatis:
#声明mybatis配置文件位置
config-location: classpath:mybatis/mybatis-config.xml
# 声明mapper.xml位置
mapper-locations: classpath:mybatis/mapper/*.xml
连接数据库:
连接数据库的操作可以在mybatis中进行,但是我们使用的是springboot,所以最好在springboot的配置文件中配置
# 连接数据库
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/user
driver-class-name: com.mysql.jdbc.Driver
开启驼峰命名法:
开启驼峰命名法有两种方式,一种是在mybatis核心配置文件中进行配置,一种实在springboot中配置
使用驼峰命名法数据库中像user_id这种字段名就可以在java中用userId表示
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
推荐在springboot中配置,因为这是一个springboot项目,在springboot中配置mybatis的设置就可以不需要mybatis核心配置文件,也不用在声明myabtis核心配置文件的位置,因为这样会起冲突,系统不知道应该按照哪一个文件去进行配置:
#配置mybatis
mybatis:
#声明mybatis配置文件位置
# config-location: classpath:mybatis/mybatis-config.xml
# 声明mapper.xml位置
mapper-locations: classpath:mybatis/mapper/*.xml
# 开启驼峰命名法
configuration:
map-underscore-to-camel-case: true