B站:狂神说Java-->https://www.bilibili.com/video/BV1PE411i7CV?p=1
微服务阶段
javase: OOP
mysql:持久化
html+css+js+jquery+框架
javaweb:可以独立开发MVC3 层架构的网站了,比较原始
ssm框架:简化了我们的开发流程,配置也开始较为复杂;
打包方式:war:tomcat运行
spring再简化--->SpringBoot,微服务架构!
打包方式:SpringBoot - jar:内嵌tomcat;
服务越来越多: springcloud;
学习路线
什么是SpringBoot
Spring是一个开源框架,2003年兴起的一个轻量级的Java开发框架,作者: Rod Johnson。
Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。
Spring是如何简化Java开发的
为了降低Java开发的复杂性,Spring采用 了以下4种关键策略:
1、基于POJO的轻量级和最小侵入性编程;
2、通过I0C,依赖注入(DI) 和面向接口实现松耦合;
3、基于切面(AOP)和惯例进行声明式编程;
4、通过切面和模版减少样式代码;
记住:约定大于配置
到底多么简单
官方:提供了一个快速生成的网站!
官网Spring Initializr搭建一个springboot的HelloWord项目
点击绿色运行按钮即可运行程序
可以看到Tomcat已经打开8080端口
到此一个完整的spring项目框架已经搭建好了。
写一个打印输出接口
必须在同级目录下建包
HelloController类
package com.dahai.helloWord.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping("/hello") public String hello(){ //调用业务,接收前端参数 return "hello,World"; } }
运行后,直接访问:localhost:8080/hello
我们可以发现我们没有配置任何文件,这就是springboot的核心机制:自动装配
使用idea创建Springboot项目
新建项目New Project
Next后
待idea下载完依赖就OK了
tips
在application.properties更改项目端口号
#更改项目的端口号 server.port=8081
这样就更改项目发布的端口号了(默认是8080)
修改springboot-banner
生成ASCII的在线地址
这就是springboot-banner(图标的样子)
在resource目录下新建txt文本
再次运行就OK了
自动装配:
pom.xml
启动器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
主程序
package com.dahai; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //@SpringBootApplication :标注这个类是一个springboot的应用 @SpringBootApplication public class Springboot01HelloworldApplication { //将springboot应用启动 public static void main(String[] args) { SpringApplication.run(Springboot01HelloworldApplication.class, args); } }
注解
@SpringBootConfiguration:springboot的配置
@Documented:spring配置类
@Configuration:说明这也是一个spring的组件
@EnableAutoConfiguration:自动配置
@AutoConfigurationPackage:自动配置包
@Import({Registrar.class}):导入选择器,包注册
。
(待完善)
SpringBoot使用一个全局的配置文件,配置文件名称是固定的
application.properties
语法结构: key=value
server.port=8081
application.yml
语法结构: key:空格value
server: port: 8081
配置文件的作用:修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;
YAML是 "YAML Ain't a Markup Language" (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)
这种语言以数据作为中心,而不是以标记语言为重点!
以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml
传统xml配置:
<server> <port>8081<port> </server>
yaml配置:
server: prot: 8080
说明:语法要求严格!
1、空格不能省略
2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
3、属性和值的大小写都是十分敏感的。
字面量:普通的值 [ 数字,布尔值,字符串 ]
字面量直接写在后面就可以 , 字符串默认不用加上双引号或者单引号;
k: v
注意:
“ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
比如 :name: "kuang \n shen" 输出 :kuang 换行 shen
'' 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
比如 :name: ‘kuang \n shen’ 输出 :kuang \n shen
用以前的spring注解方式给实体类赋值
package com.dahai.pojo; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; //把下面的内容添加到spring的组件中 @Component public class Dog { @Value("旺财") private String name; @Value("3") private Integer age; ...无参 ...有参 ...get ...set ...toString }
用Springboot专门的测试类测试
package com.dahai; import com.dahai.pojo.Dog; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot02ConfigApplicationTests { @Autowired private Dog dog; @Test void contextLoads() { System.out.println(dog); } }
运行结果
爆红导入这个依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
Person类
package com.dahai.pojo; import java.util.Date; import java.util.List; import java.util.Map; @Component //加上下面这行就可以和application.yaml的person连接起来了 @ConfigurationProperties(prefix = "person") public class Person { private String name; private Integer age; private Boolean happy; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; ...无参 ...有参 ...get ...set ...toString }
application.yaml
person: name: dahai age: 3 happy: true birth: 2000/01/01 maps: {k1: v1,k2: v2} lists: - code - girl - music dog: name: 旺财 age: 3
测试类
package com.dahai; import com.dahai.pojo.Dog; import com.dahai.pojo.Person; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot02ConfigApplicationTests { @Autowired private Person person; @Test void contextLoads() { System.out.println(person); } }
运行结果
application.properties给实体类赋值
检查File Encodings
application.properties
name=dahai
Person类
package com.dahai.pojo; import java.util.Date; import java.util.List; import java.util.Map; @Component //加载指定的配置文件 @PropertySource(value = "classpath:application.properties") public class Person { //通过SPEL表达式取出配置文件的值 @Value("${name}") private String name; private Integer age; private Boolean happy; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; ...无参 ...有参 ...get ...set ...toString }
测试类
package com.dahai; import com.dahai.pojo.Dog; import com.dahai.pojo.Person; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class Springboot02ConfigApplicationTests { @Autowired private Person person; @Test void contextLoads() { System.out.println(person); } }
运行结果
总之:还是推荐使用yaml给实体类赋值
运行结果
数据校验要导入下面这个包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
运行之后,爆红
将email填写为正确的邮箱格式后
message表示如果邮箱输入格式不正确后,打印的一句话。
运行之后,正确输出toString
JSR-303数据校验参考博客:https://blog.csdn.net/qq_42681138/article/details/122731958
application.yaml优先级
官方文档:
说明可以配置多套环境,那么怎么切换环境呢?下面开始解决多环境如何切换问题
application.yaml
配置多套环境
server: port: 8081 # 使用dev环境 spring: profiles: active: dev # 用---(三个-)分隔环境 --- server: port: 8082 # dev环境 spring: profiles: dev --- server: port: 8083 # test环境 spring: profiles: test
(待完善...)
jar:webapp !
自动装配
springboot到底帮我们配置了什么?我们能不能进行修改?能修改哪些东西?能不能扩展?
要解决的问题:
maven的方式引入jQuery(静态资源)
resourse目录下的包引入静态资源
总结:
1、在springboot,我们可以使用以下方式处理静态资源
localhost:8080/webjars/
localhost:8080/
2、优先级: resources>static(默认)>public
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index.html</title> </head> <body> <h1>首页</h1> </body> </html>
运行结果
前端交给我们的页面,是html页面。如果是我们以前开发,我们需要把他们转成jsp页面,jsp好处就是当我们查出一些数据转发到JSP页面以后,我们可以用jsp轻松实现数据的显示,及交互等。
jsp支持非常强大的功能,包括能写Java代码,但是呢,我们现在的这种情况,SpringBoot这个项目首先是以jar的方式,不是war,像第二,我们用的还是嵌入式的Tomcat,所以呢,他现在默认是不支持jsp的。
那不支持jsp,如果我们直接用纯静态页面的方式,那给我们开发会带来非常大的麻烦,那怎么办呢?
SpringBoot推荐你可以来使用模板引擎:
模板引擎,我们其实大家听到很多,其实jsp就是一个模板引擎,还有用的比较多的freemarker,包括SpringBoot给我们推荐的Thymeleaf,模板引擎有非常多,但再多的模板引擎,他们的思想都是一样的。
1、Thymeleaf 官网: https://www.thymeleaf.org/
2、Thymeleaf 在Github的主页: https://github.com/thymeleaf/thymeleaf
3、Spring官方文档: "https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/htmlsingle/#using-boot-starter",找到我们对应的版本
pom.xml
<!--thymeleaf模板--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency>
注意:templates包下的html文件只能通过controller层去跳转访问
test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>test页面</h1> </body> </html>
TestController类
package com.dahai.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; //在templates目录下的所有页面,只能通过controller来跳转! //这个需要模板引/整的支持! @Controller public class TestController { @RequestMapping("/test") public String index(){ return "test"; } }
运行结果
结论:只要需要使用thymeleaf,只需要导入对应的依赖就可以了!我们将html放在我们的templates目录下即可
使用thymeleaf接管htmL元素
头文件
<html xmlns:th="http://www.thymeleaf.org">
TestController类
package com.dahai.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; //在templates目录下的所有页面,只能通过controller来跳转! //这个需要模板引/整的支持! @Controller public class TestController { @RequestMapping("/test") public String test(Model model){ model.addAttribute("msg","hello,springboot"); return "test"; } }
test.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--所有的htmL元素都可以被thymeleaf替换接管: th: 元素名--> <div th:text="${msg}"></div> </body> </html>
运行结果
例子:
package com.dahai.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; //在templates目录下的所有页面,只能通过controller来跳转! //这个需要模板引/整的支持! @Controller public class IndexController { @RequestMapping("/test") public String test(Model model){ model.addAttribute("msg","<h1>hello,springboot</h1>"); return "test"; } }
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--所有的htmL元素都可以被thymeleaf替换接管: th: 元素名--> <div th:text="${msg}"></div> <!--不被转义--> <div th:utext="${msg}"></div> </body> </html>
运行结果
IndexController类
package com.dahai.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import java.util.ArrayList; import java.util.Arrays; //在templates目录下的所有页面,只能通过controller来跳转! //这个需要模板引/整的支持! @Controller public class IndexController { @RequestMapping("/test") public String test(Model model){ model.addAttribute("msg","<h1>hello,springboot</h1>"); //存一个集合 model.addAttribute("users", Arrays.asList("Mike","Lisa")); return "test"; } }
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--所有的htmL元素都可以被thymeleaf替换接管: th: 元素名--> <div th:text="${msg}"></div> <!--不被转义--> <div th:utext="${msg}"></div> <hr> <!--遍历list集合--> <h3 th:each="user:${users}" th:text="${user}"></h3> </body> </html>
运行结果
自定义视图解析器
package com.dahai.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.Locale; //扩展 springMvc @Configuration public class MyMvcConfig implements WebMvcConfigurer { //ViewResolver实现了视图解析器接口的类,我们就可以把它看做视图解析器 @Bean public ViewResolver myViewResolver(){ return new MyViewResolver(); } //自定义了一个自的视图解析MyViewResolver public static class MyViewResolver implements ViewResolver { @Override public View resolveViewName(String viewName, Locale locale) throws Exception { return null; } } }
视图跳转
package com.dahai.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; //如果我们要去扩展springmvc, 官方建议我们这样去做! @Configuration //@EnableWebMvc //这玩意就是入了一个类: DelegatingWebMvcConfiguration: 从容器中获取所有的webmvcconfig; public class MyMvcConfig implements WebMvcConfigurer { //视图跳转 @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/mike").setViewName("/test"); } }
访问localhost://8080/mike----(跳转到)---->localhost://8080/test
部门
package com.dahai.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; //部门表 @Data @AllArgsConstructor @NoArgsConstructor public class Department { private Integer id; private String departmentName; }
员工
package com.dahai.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import javax.swing.*; import java.util.Date; //员工表 @Data @NoArgsConstructor public class Employee { private Integer id; private String lastName; private String email; private Integer gender; //0:女 1:男 private Department department; private Date birth; public Employee(Integer id, String lastName, String email, Integer gender, Department department) { this.id = id; this.lastName = lastName; this.email = email; this.gender = gender; this.department = department; //默认的创建日期! this.birth = new Date(); } }
部门dao
package com.dahai.dao; import com.dahai.pojo.Department; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; //部门dao @Repository public class DepartmentDao { //模拟数据库的数据 private static Map<Integer, Department> departments = null; static { departments = new HashMap<Integer, Department>(); //创建一个部门表 departments.put(101,new Department(101,"教学部")); departments.put(102,new Department(102,"市场部")); departments.put(103,new Department(103,"教研部")); departments.put(104,new Department(104,"运营部")); departments.put(105,new Department(105,"后勤部")); } //获得所有部门信息 public Collection<Department> getDepartments(){ return departments.values(); } //通过id得到部门 public Department getDepartmentById(Integer id){ return departments.get(id); } }
员工dao
package com.dahai.dao; import com.dahai.pojo.Department; import com.dahai.pojo.Employee; import org.apache.coyote.OutputBuffer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; //员工dao @Repository public class EmployeeDao { //模拟数据库的数据 private static Map<Integer, Employee> employees = null; //员工有所属的部门 @Autowired private DepartmentDao departmentDao; static { employees = new HashMap<Integer, Employee>(); //创建一个部门表 employees.put(1001,new Employee(1001,"AA","10086@163.com",1,new Department(101,"教学部"))); employees.put(1002,new Employee(1002,"BB","10087@163.com",0,new Department(102,"市场部"))); employees.put(1003,new Employee(1003,"CC","10088@163.com",1,new Department(103,"教研部"))); employees.put(1004,new Employee(1004,"DD","10089@163.com",0,new Department(104,"运营部"))); employees.put(1005,new Employee(1005,"EE","10090@163.com",1,new Department(105,"后勤部"))); } //主键自增 private static Integer ininId = 1006; //增加一个员工 public void addEmployee(Employee employee){ if (employee.getId()==null){ employee.setId(ininId++); } employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId())); employees.put(employee.getId(),employee); } //查询全部员工 public Collection<Employee> getAllEmployee(){ return employees.values(); } //通过id查询员工 public Employee getEmployeeById(Integer id){ return employees.get(id); } //通过id删除员工 public void deleteEmployee(Integer id){ employees.remove(id); } }
自定义视图解析器进行页面跳转
package com.dahai.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index.html").setViewName("index"); } }
因为引入了thymeleaf,所以要把thymeleaf的头文件
加上去
<html xmlns:th="http://www.thymeleaf.org">
然后把引入静态资源的地址修改成thymeleaf的语法格式
然后关闭模板引擎的缓存(防止thymeleaf没有生效)
# 关闭模板引擎的缓存 spring.thymeleaf.cache=false
运行结果
然后继续修改404.html
修改dashboard.html
修改list.html
检查自己的File Encodings字符编码是否为UTF-8
resources目录下新建一个包(i18n)
在i18n目录下新建两个properties
然后右键
点击OK
点击OK
发现多了一个en_US的配置文件
idea的可视化界面。则需要安装一个Resource Bundle Editor的插件。
用可视化编辑
配置完毕
在application.properties绑定配置文件
继续修改thymeleaf语法
运行后变成中文
给a链接添加地址
在config包下新建一个国际化解析器的类
自己写一个国际化解析器类
package com.dahai.config; import org.springframework.util.StringUtils; import org.springframework.web.servlet.LocaleResolver; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Locale; public class MyLocaleResolver implements LocaleResolver { //解析请求 @Override public Locale resolveLocale(HttpServletRequest request) { String language = request.getParameter("l"); Locale locale = Locale.getDefault(); // 如果没有获取到就使用系统默认的 //如果请求链接不为空 // if (!StringUtils.isEmpty(language)){ if (StringUtils.hasLength(language)){ //分割请求参数 String[] split = language.split("_"); //国家,地区 locale = new Locale(split[0],split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } }
将MyLocaleResolver(国际化解析器)放在Bean里面
package com.dahai.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @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"); } //自定义的国际化组件就生效了 @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } }
这样中英文就可以互相切换了
LoginController(登录控制)
package com.dahai.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class LoginController { @RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){ //具体的业务 if(StringUtils.hasLength(username)&&"123456".equals(password)){ return "redirect:/main.html"; }else { //告诉用户你登录失败了 model.addAttribute("msg","用户名或者密码错误!"); return "index"; } } }
index.html
用户必须输入账号密码才能进入main.html,不允许在地址栏输入http://localhost:8080/main.html直接进入主界面,这就需要用拦截器进行拦截操作
解决的办法就是在session里面存登录的用户,session里面有这个用户才让你进入main.html
自定义一个拦截器
package com.dahai.config; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //自定义一个拦截器 public class LoginHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //登陆成功之后,应该有用户的session; Object loginUser = request.getSession().getAttribute("loginUser"); if (loginUser==null){ request.setAttribute("msg","没有权限,请先登录"); request.getRequestDispatcher("/index.html").forward(request,response); return false; }else { return true; } } }
在视图解析器里面配置
package com.dahai.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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; @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"); } //自定义的国际化组件就生效了 @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } //配置拦截器 //addPathPatterns("/**")拦截所有请求 //excludePathPatterns("/index.html","/"......)除了这些请求不拦截 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**"); } }
新建一个commons.html,用来存放公共页部分
<!--提取公共部分--> th:fragment="自定义名字"
<!--插入公共部分--> <div th:replace="~{哪个页面(不用写.html)::提取的自定义的名字}"></div> <!--(active='main.html')表示传入一个参数,后面三元运算符判断用的-->
<!--插入相同的顶部栏th:insert="~{从哪个页面抽取的::抽取的那个侧边栏的名字}"--> <div th:replace="~{commons/commons::topbar}"></div> <div class="container-fluid"> <div class="row"> <!--插入相同的侧边栏th:insert="~{从哪个页面抽取的::抽取的那个侧边栏的名字}"--> <div th:replace="~{commons/commons::sidebar(active='list.html')}"></div> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <h2>员工列表</h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>员工ID</th> <th>名字</th> <th>邮箱</th> <th>性别</th> <th>部门</th> <th>出生日期</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="emp:${emps}"> <td th:text="${emp.getId()}"></td> <!--也可以像下面这样写--> <td>[[${emp.getLastName()}]]</td> <td th:text="${emp.getEmail()}"></td> <td th:text="${emp.getGender()==0?'女':'男'}"></td> <td th:text="${emp.getDepartment.getDepartmentName()}"></td> <!-- <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>--> <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}"></td> <td> <button class="btn btn-sm btn-primary">编辑</button> <button class="btn btn-sm btn-danger">删除</button> </td> </tr> </tbody> </table> </div> </main> </div> </div>
查看结果
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}"> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"> <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path> <circle cx="9" cy="7" r="4"></circle> <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path> <path d="M16 3.13a4 4 0 0 1 0 7.75"></path> </svg> 员工管理 </a>
//查询全部员工 @RequestMapping("/emps") public String list(Model model){ Collection<Employee> employees = employeeDao.getAllEmployee(); model.addAttribute("emps",employees); return "emp/list"; }
<h2>员工列表<a style="margin-left: 50px" class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a></h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>员工ID</th> <th>名字</th> <th>邮箱</th> <th>性别</th> <th>部门</th> <th>出生日期</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="emp:${emps}"> <td th:text="${emp.getId()}"></td> <!--也可以像下面这样写--> <td>[[${emp.getLastName()}]]</td> <td th:text="${emp.getEmail()}"></td> <td th:text="${emp.getGender()==0?'女':'男'}"></td> <td th:text="${emp.getDepartment.getDepartmentName()}"></td> <!-- <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>--> <td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}"></td> <td> <button class="btn btn-sm btn-primary">编辑</button> <button class="btn btn-sm btn-danger">删除</button> </td> </tr> </tbody> </table> </div>
//添加员工 //这里是进入添加员工页面请求,没有提交表单 @GetMapping("/emp") public String toAddpage(Model model){ //查出所有部门的信息 Collection<Department> departments = departmentDao.getDepartments(); model.addAttribute("departments",departments); return "emp/add"; }
<h2 style="text-align: center">添加员工</h2> <form th:action="@{/emp}" method="post"> <div class="form-group"> <label>名字</label> <input type="text" name="lastName" class="form-control" placeholder="员工名字"> </div> <div class="form-group"> <label>邮箱</label> <input type="email" name="email" class="form-control" placeholder="邮箱"> </div> <div class="form-group"> <label>性别</label><br> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="gender" value="1"> <label class="form-check-label">男</label> </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="gender" value="0"> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label>部门</label> <!--我们在controller接收的是一个Employee, 所以我们需要提交的是其中的一个属性!--> <select class="form-control" name="department.id"> <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option> </select> </div> <div class="form-group"> <label>出生日期</label> <input type="text" name="birth" class="form-control" placeholder="出生日期"> </div> <button type="submit" class="btn btn-primary">添加</button> </form>
//这里是提交表单请求 @PostMapping("/emp") public String addEmp(Employee employee){ System.out.println("save=>"+employee); employeeDao.saveEmployee(employee);//调用底层业务方法保存员工信息 System.out.println("添加成功"); return "redirect:/emps"; }
添加提交后会重定向到list.html列表
<a class="btn btn-sm btn-primary" th:href="@{'/toUpdatePage/'+${emp.getId()}}">编辑</a>
//去员工的修改页面的请求 @GetMapping("/toUpdatePage/{id}") public String toUpdatePage(@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"; }
<h2 style="text-align: center">修改员工信息</h2> <form th:action="@{/updateEmp}" method="post"> <input type="hidden" name="id" th:value="${emp.getId()}"> <div class="form-group"> <label>名字</label> <input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="员工名字"> </div> <div class="form-group"> <label>邮箱</label> <input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="邮箱"> </div> <div class="form-group"> <label>性别</label><br> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==1}" class="form-check-input" type="radio" name="gender" value="1"> <label class="form-check-label">男</label> </div> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio" name="gender" value="0"> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label>部门</label> <!--我们在controller接收的是一个Employee, 所以我们需要提交的是其中的一个属性!--> <select class="form-control" name="department.id"> <option th:selected="${emp.getDepartment().getId()==dept.getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option> </select> </div> <div class="form-group"> <label>出生日期</label> <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd')}" type="text" name="birth" class="form-control" placeholder="出生日期"> </div> <button type="submit" class="btn btn-primary">修改</button> </form>
//这里是提交修改员工信息的表单请求 @PostMapping("/updateEmp") public String updateEmp(Employee employee){ employeeDao.saveEmployee(employee); System.out.println("修改后的员工信息"); System.out.println(employee); return "redirect:/emps"; }
修改前
选择修改名字叫“EE”的员工信息
提交修改后
<a class="btn btn-sm btn-danger" th:href="@{'/deleteEmp/'+${emp.getId()}}">删除</a>
//这里是删除员工信息的请求 @GetMapping("/deleteEmp/{id}") public String deleteEmp(@PathVariable("id")Integer id){ employeeDao.deleteEmployeeById(id); return "redirect:/emps"; }
到此增删改查搞定
<ul class="navbar-nav px-3"> <li class="nav-item text-nowrap"> <a class="nav-link" th:href="@{/user/logout}">注销</a> </li> </ul>
controller层编写从session中移除用户
前端模板:别人写好的,我们拿来改成自己需要的
前端框架:组件,自己手动组合拼接!
ElementUI
layui
yaml配置jdbc数据源
spring: datasource: username: root password: 123456 # jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 # 假如报时区问题是因为安装MySQL的时候没有在my.ini里面设置时区 url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.cj.jdbc.Driver
JDBCController类
package com.dahai.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; @RestController public class JDBCController { @Autowired JdbcTemplate jdbcTemplate; //查询数据库所有的数据并且显示到前端 //没有实体类,数据库中的东西,怎么获取? Map @GetMapping("/userList") public List<Map<String,Object>> userList(){ String sql = "select * from user"; List<Map<String, Object>> list_maps = jdbcTemplate.queryForList(sql); return list_maps; } //添加用户 @GetMapping("/addUser") public String addUser(){ String sql = "insert into user(id,name,pwd) values (8,'小明','123456')"; jdbcTemplate.update(sql); return "addUser-ok"; } //修改用户 @GetMapping("/updateUser/{id}") public String updateUser(@PathVariable("id") int id){ String sql = "update user set name=?,pwd=? where id="+id; //封装 Object[] objects = new Object[2]; objects[0] = "小明2"; objects[1] = "123"; jdbcTemplate.update(sql,objects); return "updateUser-ok"; } //删除用户 @GetMapping("/deleteUser/{id}") public String deleteUser(@PathVariable("id")int id){ String sql = "delete from user where id = ?"; jdbcTemplate.update(sql,id); return "deleteUser-ok"; } }
Spring Boot 2.0以上默认使用Hikari 数据源,可以说Hikari 与Driud都是当前Java Web上最优秀的数据源,我们来重点介绍Spring Boot如何集成Druid数据源,如何实现数据库监控。
HikariDataSource号称Java WEB当前速度最快的数据源,相比于传统的C3PO、DBCP、 Tomcat,jdbc等连接池更加优秀;
package com.dahai.config; import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.apache.catalina.filters.WebdavFixFilter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class DruidConfig { @ConfigurationProperties(prefix = "spring.datasource") @Bean public DataSource druidDataSource(){ return new DruidDataSource(); } //后台监控:web.xml 注册ServletRegistrationBean,用@Bean解决 //因为SpringBoot 内置了servlet容器, 所以没有web.xmL ,替代方法: ServletRegistrationBean @Bean public ServletRegistrationBean statViewServlet(){ ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); //后台需要有人登陆,账号密码配置 HashMap<String, String> initParameters = new HashMap<>(); //增加配置 //initParameters.put("key","value"); initParameters.put("loginUsername","admin");//注意这里的参数名字loginUsername是只能这样写,不能写成别的 initParameters.put("loginPassword","123");//loginPassword同上 //允许谁可以访问 initParameters.put("allow","");//如果这里的value为空,表示所有人可以访问,一般value设置为空就可以了 //initParameters.put("allow","localhost");//这里的value为localhost,表示只有本机可以访问 //禁止谁可以访问 //initParameters.put("dahai","192.168.11.123");//表示禁止192.168.11.123这个ip地址访问 bean.setInitParameters(initParameters);//设置初始化参数 return bean; } //springboot注册一个filter(过滤器) @Bean public FilterRegistrationBean webStatFilter(){ FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new WebStatFilter()); //可以过滤哪些请求呢? Map<String,String> initParameters = new HashMap<>(); //这些东西不进行统计~ initParameters.put("exclusions","*.js,*.css,/druid/*"); bean.setInitParameters(initParameters); return bean; } }
运行
输入账号和密码
在另一个页面去访问http://localhost:8080/userList
可以在后台的SQL监控页面看到刚刚执行的sql语句
因为Druid是支持log4j的
如果出现下面这样,是因为没有在resource下面配置log4j.properties
log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/logFile.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
再次运行发现没有错误
整合包
去maven仓库找mybatis-spring-boot-starter
<!--mybatis-spring-boot-starter 整合--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>
这次用application.properties来配置数据源(用yaml的方式也是一样的)
spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
测试类
package com.dahai; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import javax.sql.DataSource; import java.sql.SQLException; @SpringBootTest class Springboot05MybatisApplicationTests { @Autowired DataSource dataSource; @Test void contextLoads() throws SQLException { System.out.println(dataSource.getClass()); System.out.println(dataSource.getConnection()); } }
成功输出
说明连接数据库是没问题了,可以开始后面的工作
User类
package com.dahai.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String pwd; }
UserMapper类
package com.dahai.mapper; import com.dahai.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; import java.util.List; // 这个注解表示了这是一个 mybatis 的 mapper 类 @Mapper @Repository public interface UserMapper { List<User> queryUserList(); User queryUserById(int id); int addUser(User user); int updateUser(User user); int deleteUser(int id); }
spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 整合mybatis mybatis.type-aliases-package=com.dahai.pojo mybatis.mapper-locations=classpath:mybatis/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 namespace="com.dahai.mapper.UserMapper"> <select id="queryUserList" resultType="User"> select * from mybatis.user; </select> <select id="queryUserById" resultType="User"> select * from mybatis.user where id = #{id}; </select> <insert id="addUser" parameterType="User"> insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd}); </insert> <update id="updateUser" parameterType="User"> update mybatis.user set name=#{name},pwd = #{pwd} where id = #{id}; </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id = #{id} </delete> </mapper>
package com.dahai.controller; import com.dahai.mapper.UserMapper; import com.dahai.pojo.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class UserController { @Autowired private UserMapper userMapper; @GetMapping("/queryUserList") public List<User> queryUserList() { List<User> userList = userMapper.queryUserList(); for (User user : userList) { System.out.println(user); } return userList; } //添加一个用户 @GetMapping("/addUser") public String addUser() { userMapper.addUser(new User(7,"阿毛","123456")); return "ok"; } //修改一个用户 @GetMapping("/updateUser") public String updateUser() { userMapper.updateUser(new User(7,"阿毛","123456")); return "ok"; } @GetMapping("/deleteUser") public String deleteUser() { userMapper.deleteUser(7); return "ok"; } }
在web开发中,安全第一位!
功能性需求:否
做网站:安全应该在什么时候考虑?设计之初!
市面上的安全框架
springboot官网:https://spring.io/projects/spring-security
摘要:
Spring Security is a powerful and highly customizable authentication
and access-control
framework.
翻译:Spring Security是一个功能强大且高度可定制的身份验证
和访问控制
框架。
创建一个普通的springboot的空项目
pom.xml文件需要引入的依赖
<?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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.dahai</groupId> <artifactId>springboot-06-security</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-06-security</name> <description>springboot-06-security</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--spring-boot-starter-security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <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> <!--thymeleaf模板,都是基于thymeleaf3.x开发的--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-java8time</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
RouterController(页面跳转)
package com.dahai.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class RouterController { @RequestMapping({"/","/index"}) public String index(){ return "index"; } @RequestMapping("/toLogin") public String toLogin(){ return "views/login"; } @RequestMapping("/level1/{id}") public String level1(@PathVariable("id") int id){ return "views/level1/"+id; } @RequestMapping("/level2/{id}") public String level2(@PathVariable("id") int id){ return "views/level2/"+id; } @RequestMapping("/level3/{id}") public String level3(@PathVariable("id") int id){ return "views/level3/"+id; } }
浏览器访问:http://localhost:8080/
记住几个类:
Spring Security的两个主要目标是“认证"和"授权”(访问控制)
这个概念是通用的,而不是只在Spring Security中存在。
固定的骨架
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); } }
在config包新建SecurityConfig类
package com.dahai.config; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; //:AOP:类似拦截器 @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { //首页所有人可以访问,但是功能页只有对应有权限的人才能访问 //authorizeRequests 认证请求 http.authorizeRequests() .antMatchers("/").permitAll() // vip1用户只能访问/level1/** .antMatchers("/level1/**").hasRole("vip1") // vip2用户只能访问/level2/** .antMatchers("/level2/**").hasRole("vip2") // vip3用户只能访问/level3/** .antMatchers("/level3/**").hasRole("vip3"); // 没有权限默认会到登录页面,需要开启登录的页面 http.formLogin(); } }
再次访问level1页面
报403错误,表示权限不足
给不同的角色分配不同的权限
认证和授权
package com.dahai.config; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; //:AOP:类似拦截器 @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { //授权 @Override protected void configure(HttpSecurity http) throws Exception { //首页所有人可以访问,但是功能页只有对应有权限的人才能访问 //authorizeRequests 认证请求 http.authorizeRequests() .antMatchers("/").permitAll() // vip1用户只能访问/level1/** .antMatchers("/level1/**").hasRole("vip1") // vip2用户只能访问/level2/** .antMatchers("/level2/**").hasRole("vip2") // vip3用户只能访问/level3/** .antMatchers("/level3/**").hasRole("vip3"); // 没有权限默认会到登录页面,需要开启登录的页面 http.formLogin(); //注销 http.logout().logoutSuccessUrl("/"); } //认证,springboot 2.1.x之前 可以直接使用 //2.1.x之后 密码编码:passwordEncoder //在Spring Secutiry 5.0+新增了很多的加密方法~ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // auth.jdbcAuthentication() 从数据库里面拿数据 // auth.inMemoryAuthentication() 从内存里面拿数据 // 这些数据正常应该从数据库中读,这里方便理解就从内存中读 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) .withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3") .and() .withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3") .and() .withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1"); } }
引入一个包
shiro架构
1、导入依赖
2、配置文件
3、HelloWorld
解析官方测试案例
先把环境搭建好
新建一个module
controller层
index页面
运行结果
整合开始
引入一个shiro整合spring的整合包(依赖)
spring托管Bean
运行结果
编写配置文件
pojo类(用户实体类)
编写mapper
编写mapper.xml
编写service层
编写service.impl
测试一下,没问题就说明底层可以连接到数据库
把UserRealm连接到真实的数据库
运行结果
引入shiro-thymeleaf依赖
到此,Shiro就算结束了,当然学习Shiro还未结束,需要后面多加练习。。。