SpringBoot学习

简介: 【10月更文挑战第7天】Spring学习

创建第一个springboot项目

创建一个项目两种方式:

第一种方式:

去springboot官网)选中配置

第二种方式:

SpringBoot运行原理

1. pom.xml

父依赖

其中它主要是以来一个父项目,主要是管理项目的过滤及插件。

点进去,发现还有一个父依赖。

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

这里管理了SpringBoot应用中所有依赖版本的地方,SpringBoot版本控制中心。

所以之后在导入依赖的时候,我们就不需要写版本了,但是如果导入的包没有在依赖管理中就需要手动配置版本。

依赖管理中的名称都叫spring-boot-starter-xxx

2. 主启动类

2.1 默认的启动类

package com.study.springboot1;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication 来标注一个主程序
//说明这是一个Spring Boot应用。
@SpringBootApplication
public class Springboot1Application {
       
    public static void main(String[] args) {
   
        //run()看着是一个方法,实际是启动一个服务
        SpringApplication.run(Springboot1Application.class, args);
    }
}

下面介绍一个这个启动类都做了什么

2.2 @SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用。

进入这个注解可以看到上面还有很多其他注解。

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {
   @Filter(
    type = FilterType.CUSTOM,
    classes = {
   TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {
   AutoConfigurationExcludeFilter.class}
)}
)

2.3 @ComponentScan

这个注解我们之前见过,就是扫描包。

作用:自动扫描并加载符合条件的组件或者Bean,讲这个Bean定义加载到IOC容器中。

2.4 @SpringBootConfiguration

作用:SpringBoot的配置类注解,标注在某个类上,表示这是一个SpringBoot的配置类;

继续看这个注释里面有什么:

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed

这里@Configuration说明这是一个配置类,配置类就是对应的Spring的xml配置文件。

打开@Configuration注释

@Component

这里@Component,说明启动类本身也是Spring的一个组件,负责启动应用

@EnableAutoConfigur

作用:开启自动配置功能

打开这个注解:

@Target({
   ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({
   AutoConfigurationImportSelector.class})

首先看到@Import({AutoConfigurationImportSelector.class})给容器导入组件,

@AutoConfigurationPackage自动配置包

Spring Boot-YAML配置注入

1.配置文件:

//丢给spring管理,注册bean容器中
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
/*@ConfigurationProperties作用:
将配置我呢见中配置的每一个属性的值,映射到这个组件中。
告诉SpringBoot江本类中的所有属性和配置文件中相关的配置进行绑定。
参数prefix="dog",将配置文件中的person下面的所有属性一一对应
 */
@ConfigurationProperties(prefix = "dog")
public class Dog {
   
    private String name;
    private int age;
}
@Component//注册bean交给spring托管
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@ConfigurationProperties(prefix = "person")
public class Person {
   
    private String name;
    private Integer age;
    private Boolean happy;
    private Date birthday;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}
server:
  port: 8081
dog:
  name: 鸡蛋壳
  age: 3
person:
  age: 5
  name: 苏卿
  happy: true
  birthday: 2020/1/4
  maps: {
   1: a,2: b}
  lists: [q,b,c]
  dog: {
    name: d, age: 1 }
@SpringBootTest
class Springboot1ApplicationTests {
   

    @Autowired
    Person person;

    @Test
    void contextLoads() {
   
        System.out.println(person.toString());
    }
}

Spring Boot: Web开发静态资源处理

  1. 使用SpringBoot的步骤

员工管理系统

1. 准备工作

1.1 导入资源

将静态资源导入到resources文件夹里。

1.2 编写pojo实体类

员工表:

@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();
    }
}

部门表:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Department {
   
    private int id;
    private String departmentName;
}

1.3 编写dao层

这里使用模拟数据库,并没有使用mysql数据库。

部门dao

@Repository 用于标注访问层(Dao层)的类。

与@Component注解相同,@Service和@Controller都予以理解为@Component注解的拓展,朱永都是在类上实例化bean,并把这个类交给spring容器进行管理。

@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> findAll() {
   
        return departments.values();
    }
    public Department findById(Integer id) {
   
        return departments.get(id);
    }
}

员工dao

@Repository
public class EmployeeDao {
   
    private static Map<Integer, Employee> employees = null;
    private final DepartmentDao departmentDao;
    static {
   
        employees = new HashMap<Integer,Employee>(); //创建一个部门表

        employees.put(1001,new Employee(  1001,"AA","1622840727@qq.com",1,new Department(101,"教学部")));
        employees.put(1002,new Employee(  1002,"BB","2622840727@qq.com",0,new Department(102,"市场部")));
        employees.put(1003,new Employee(  1003,"CC","4622840727@qq.com",1,new Department(103,"教研部")));
        employees.put(1004,new Employee(  1004,"DD","5628440727@qq.com",0,new Department(104,"运营部")));
        employees.put(1005,new Employee(  1005,"FF","6022840727@qq.com",1,new Department(105,"后勤部")));
    }

    //逐渐自增
    private static Integer initId=1006;

    public EmployeeDao(DepartmentDao departmentDao) {
   
        this.departmentDao = departmentDao;
    }

    //添加一个新员工:
    public void addEmployee(Employee employee) {
   
        if (employee.getId() == null){
   
            employee.setId(initId++);
        }
        employee.setDepartment(departmentDao.findById(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
    }
    //查询全部员工
    public Collection<Employee> getAllEmployees() {
   
        return employees.values();
    }
    //通过ID查询员工
    public Employee getEmployeeById(Integer id) {
   
        return employees.get(id);
    }
    //删除员工
    public void deleteEmployee(Integer id) {
   
        employees.remove(id);
    }
}

2. 首页实现:

2.1 引入Thymeleaf依赖

<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>

2.2 编写MyMVCConfig

@Configuration作用取代bean.xml配置文件注册bean对象

@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");
        registry.addViewController("/main").setViewName("dashboard");    
    }
}

这个可以对应理解为,在javaweb+jap中可以理解为servler-mapping映射成"/"

<!-- web.xml -->
<servlet>
    <servlet-name>indexServlet</servlet-name>
    <servlet-class>com.example.IndexServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>indexServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
// IndexServlet.java
@WebServlet("/")
public class IndexServlet extends HttpServlet {
   
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        request.getRequestDispatcher("/index.jsp").forward(request, response);
    }
}

在javamvc中可以理解成:使用@Controller注解定义一个控制器,并使用@RequestMapping注解来映射URL。

// IndexController.java
@Controller
public class IndexController {
   
    @RequestMapping("/")
    public String index() {
   
        return "index"; // 返回视图名称,对应于 /WEB-INF/views/index.jsp
    }
}

更改静态资源路径:

将所有静态资源都交给thymeleaf接管。@{}

例如:链接css文件时:<link th:href="@{/css/bootstrap.min.css}" rel="style,用@{}括起来。并在href前面加上th:

<head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="description" content="">
        <meta name="author" content="">
        <title>Signin Template for Bootstrap</title>
        <!-- Bootstrap core CSS -->
        <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
        <!-- Custom styles for this template -->
        <link th:href="@{/css/signin.css}" rel="stylesheet">
    </head>

3. 页面国际化

首先需要将IDEA中的配置全部改成UTF-8

  1. resources资源文件下新建一个i18n目录,用于存放国际化配置文件。

  2. 创建login.properties文件,login_zh_CN.properties中文文件,login_en_US.rpoperties英文文件。然后会发现文件夹变了。

  3. 然后需要安装Resource Budle Editor这个插件,才能更快更清晰的配置。当然不安装也可以。安装后是下面这个样子。点击资源包,就可以同时配三个,默认-英语-中文。

    当然不安装也可以直接设置:三个文件中都这样对应着设置即可。

配置生效:

我们需要在springboot配置文件中关联到这个文件,

application.properties文件中加入:

spring.messages.basename=i18n.login

然后我们还需要对Spring,中的l在来自于前端点击配置的

<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>

然后就是解析请求:

public class MyLocaleResolver implements LocaleResolver {
   
    //解析请求
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
   
        // 获取请求中的语言参数
        String language = request.getParameter("l");
        Locale locale = Locale.getDefault();//如果没有就用默认的
        //如果请求的链接携带了国际化的参数
        if (!StringUtil.isNullOrEmpty(language)) {
   
            String[] s = language.split("_");//分割
            locale = new Locale(s[0], s[1]);
        }
        return locale;
    }
    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
   
    }
}

然后不要忘了将其配置到spring文件(MyMVCConfig.java)中:

//自定义的国际化
@Bean
public LocaleResolver localeResolver() {
   
    return new MyLocaleResolver();
}

4. 登录+拦截器

4.1 登录

禁用模板缓存

#禁用模板缓存 
spring.thymeleaf.cache=false
  1. 首先肯定要先将表单中添加一个提交事件

    <form class="form-signin" th:action="@{/user/login}">
       ……
    </form>
    
  2. 编写对应的Controller

    这里面的username和password对应着下面中的name标签提交都是携带name标签

    <input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
    <input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
    
    @Controller
    public class LoginController {
         
    
     @RequestMapping("/user/login")
     public String login(@RequestParam("username") String username,
                         @RequestParam("password") String password,
                         Model model, HttpSession session) {
         
         //具体的业务:
         if (!StringUtil.isNullOrEmpty(username)&&"123456".equals(password)) {
         
             session.setAttribute("loginUser", username);
             return "redirect:/main";
         }else {
         
             model.addAttribute("msg", "用户名错误或者密码错误");
             return "index";
        }
     }
    }
    
  3. 登录失败,将信息传送给前端。

    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>优化
    
  4. 优化,登陆后链接变化,使其重定向到首页

    添加一个视图控制映射,加在自己的MyMVCConfig里面。

  5. 将Controller的代码改为重定向,就是这里,已经改过了。

    if (!StringUtil.isNullOrEmpty(username)&&"123456".equals(password)) {
         
             session.setAttribute("loginUser", username);
             return "redirect:/main";
         }else {
         
             model.addAttribute("msg", "用户名错误或者密码错误");
             return "index";
        }
    

4.2 登录拦截器

设置完上面,会发现一个问题:我们不登录,也可以直接访问到后台主页面,这是不允许的。所以我们添加拦截器。对没有登录的进行拦截。

  1. 在LoginController中添加Session.上面也已经写了。

    session.setAttribute("loginUser", username);
    
  2. 自定义个一个拦截器:

    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;
         }
     }
    }
    
  3. 然后将拦截器配置到我们自己的SpringMVC配置类中(MyMVCConfig).

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
         
     registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/", "/user/login","/css/**","/js/**","/img/**");
    }
    
  4. 然后我们可以在后台获取用户登录的信息,这里我们选择只传递用户名

    [[${session.loginUser}]]
    

5. 展示员工列表。

设置跳转:编写前端,

<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">员工管理</a>

编写Controller层对应的th:href="@{/emps}"请求。

th:class="${active=='list.html'?'nav-link active':'nav-link'}"这个是在判断是否点击这个,如果点击这个显示蓝色,负责就正常颜色。为了凸显出我们在那个界面

@Controller
public class EmployeeController {
   
    @Autowired
    EmployeeDao employeeDao;
    @Autowired
    DepartmentDao departmentDao;
    @RequestMapping("/emps")
    public String lise(Model model) {
   
        Collection<Employee> employees = employeeDao.getAllEmployees();
        model.addAttribute("emps", employees);
        return "emp/list";
    }
}

公共元素抽离

我们看,每一个界面都有左边和上面这些公共元素,那么我们可以将其提取出来,封装一下,再拿来用时,直接插入或者替换即可。

例如我们将头部标签拿出来。

<!--顶部导航栏-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
    <a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a> <!--$取EL表达式-->
    <input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
    <ul class="navbar-nav px-3">
        <li class="nav-item text-nowrap">
            <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">注销</a>
        </li>
    </ul>
</nav>

然后我们在list页面引入,引入后就可以删除之前的了

<!--引入抽取的topbar--> 
<!--模板名 : 会使用thymeleaf的前后缀配置规则进行解析 使用~{模板::标签名}-->
<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>

th:insert="~{commons/commons::topbar}"
首先th:insert表示要插入:

然后"~{}"这个是插入的表达式。
里面commons/commons,这个是公共元素在那个页面

topbar这个是要插入那个块。
插入块那边用th:fragment="topbar"来标记。

5.3 员工信息页面展示:

<thead>
    <tr>
        <th>id</th>
        <th>lastName</th>
        <th>email</th>
        <th>gender</th>
        <th>department</th>
        <th>birth</th>
        <th>操作</th>
    </tr>
</thead>
<tbody>
    <tr th:each="emp:${emps}">
        <td th:text="${emp.getEmail()}"></td>
        <td th:text="${emp.getId()}"></td>
        <td th:text="${emp.getLastName()}"></td>
        <td th:text="${emp.getGender()==0?'':''}"></td>
        <td th:text="${emp.department.getDepartmentName()}"></td>
        <td th:text="${#dates.format(emp.getBirth(), 'yyyy-MM-dd HH:mm:ss')}"></td>
        <td>
            <a class="btn btn-sm btn-primary" th:href="@{/emp/} + ${emp.getId()}">编辑</a>
            <a class="btn btn-sm btn-danger" th:href="@{/removeEmp/} + ${emp.getId()}">删除</a>
        </td>
    </tr>
</tbody>

6. 添加员工

前端表单

<h2><a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a></h2>

编写对应的Controller

@GetMapping("/emp")
    public String add(Model model) {
   
        //查出所有部门的信息
        Collection<Department> departments = departmentDao.findAll();
        model.addAttribute("departments", departments);
        return "emp/add";
    }

添加前端页面

<div class="container-fluid">
    <div class="row">
    <!-- 侧边栏-->
    <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">
        <form th:action="@{/emp}" method="post">
            <div class="form-group">
                <label>姓名</label>
                <input name="lastName" type="text" class="form-control"  placeholder="姓名">
            </div>
            <div class="form-group">
                <label>Email</label>
                <input name="email" type="email" class="form-control" placeholder="Email">
            </div>
            <div class="form-group">
                <label>性别</label><br/>
                <div class="form-check form-check-inline">
                    <input name="gander" type="radio" class="form-check-input" value="1">
                    <label class="form-check-label"></label>
                </div>
                <div class="form-check form-check-inline">
                    <input name="gander" type="radio" class="form-check-input" value="0">
                    <label class="form-check-label"></label>
                </div>
            </div>
            <div class="form-group">
                <label>所属部门</label>
                <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 name="birth" type="text" class="form-control" placeholder="1975/01/01">
            </div>
            <button type="submit" class="btn btn-default">添加</button>
        </form>
    </main>
    </div>
</div>

编写Controller层

注意这里都是"/emp"但是一个是get,一个是post请求,是不同的。然后下面就是获取前端传过来的数据然后对模拟数据库进行操作,操作完,重定向到查询全部网站。

@PostMapping("/emp")
    public String addEmp(Employee employee,Model model) {
   
        employeeDao.addEmployee(employee);
        return "redirect:/emps";
    }

这是在前端中显示部门名称,而不是id,1,2,3,4,5这样用户不知道具体指代什么。

<select class="form-control" name="department.id">
    <option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>

修改时间格式:在application.properies中配置。从之前默认的的yyyy/MM/dd

spring.mvc.date-format=yyyy-MM-dd

修改员工信息

逻辑分析:

  • 点击修改按钮应该先跳转到修改页面,

  • 进入修改页面,原数据要显示在上面。修改完毕要返回列表页面。

  1. 前端设置跳转位置:
<a class="btn btn-sm btn-primary" th:href="@{/emp/} + ${emp.getId()}">编辑</a>
  1. 写Controller,在这里面,我们要通过id查到原数据,然后将原数据返回到修改页面,查部门消息是因为我们之后编辑部门时,能够显示出具体是什么部门,而不是单纯的id/

    // 去员工修改页面
     @GetMapping("/emp/{id}")
     public String toUpData(@PathVariable("id") Integer id, Model model){
         
         //查出原本数据
         Employee employee = employeeDao.getEmployeeById(id);
         model.addAttribute("emp", employee);
         //查所有的部门信息
         Collection<Department> departments = departmentDao.findAll();
         model.addAttribute("departments", departments);
         return "emp/update";
     }
    
  2. 编写修改页面。

    <div class="container-fluid">
             <div class="row">
                 <!-- 侧边栏-->
                 <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">
                     <form th:action="@{/emp/update}" method="post">
                         <input type="hidden" name="id" th:value="${emp.getId()}">
                         <div class="form-group">
                             <label>姓名</label>
                             <input th:value="${emp.getLastName()}" name="lastName" type="text" class="form-control"  placeholder="姓名">
                         </div>
                         <div class="form-group">
                             <label>Email</label>
                             <input th:value="${emp.getEmail()}" name="email" type="email" class="form-control" placeholder="Email">
                         </div>
                         <div class="form-group">
                             <label>性别</label><br/>
                             <div class="form-check form-check-inline">
                                 <input th:checked="${emp.getGender()==1}" name="gander" type="radio" class="form-check-input" value="1">
                                 <label class="form-check-label"></label>
                             </div>
                             <div class="form-check form-check-inline">
                                 <input th:checked="${emp.getGender()==0}" name="gander" type="radio" class="form-check-input" value="0">
                                 <label class="form-check-label"></label>
                             </div>
                         </div>
                         <div class="form-group">
                             <label>所属部门</label>
                             <select class="form-control" name="department.id">
                                 <option th:selected="${dept.getId()==emp.getDepartment().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 HH:mm')}" name="birth" type="text" class="form-control" placeholder="1975-01-01">
                         </div>
                         <button type="submit" class="btn btn-default">修改</button>
                     </form>
                 </main>
             </div>
         </div>
    
  3. 编写修改的Controller

    @PostMapping("/emp/update")
     public String updateEmp(Employee employee,Model model){
         
         employeeDao.addEmployee(employee);
         return "redirect:/emps";
     }
    

删除员工

  1. list页面,提交地址:

    <a class="btn btn-sm btn-danger" th:href="@{/removeEmp/} + ${emp.getId()}">删除</a>
    
  2. 编写Controller

    //删除
     @RequestMapping("/removeEmp/{id}")
     public String delete(@PathVariable("id") Integer id, Model model) {
         
         employeeDao.deleteEmployee(id);
         return "redirect:/emps";
     }
    

404及注销

我们只需要命名为404.htmlspringboot就会自动帮我们使用了。

注销

  1. 注销请求地址

    <a class="nav-link" th:href="@{/user/logout}">注销</a>
    
  2. 对应的Controller,这里去除session也行,直接invalidate也行

    @RequestMapping("/user/logout")
     public String logout(HttpSession session) {
         
         session.removeAttribute("loginUser");
         //session.invalidate();
         return "redirect:/index.html";
     }
    

整合JDBC

目录
相关文章
|
7天前
|
编解码 Java 程序员
写代码还有专业的编程显示器?
写代码已经十个年头了, 一直都是习惯直接用一台Mac电脑写代码 偶尔接一个显示器, 但是可能因为公司配的显示器不怎么样, 还要接转接头 搞得桌面杂乱无章,分辨率也低,感觉屏幕还是Mac自带的看着舒服
|
9天前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1568 10
|
1月前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
12天前
|
人工智能 Rust Java
10月更文挑战赛火热启动,坚持热爱坚持创作!
开发者社区10月更文挑战,寻找热爱技术内容创作的你,欢迎来创作!
782 27
|
2天前
|
移动开发 JavaScript 前端开发
💻揭秘!如何用 Vue 3 实现酷炫的色彩魔方游戏✨
本文分享了开发基于Canvas技术的小游戏"色彩魔方挑战"的完整过程。游戏旨在考验玩家的观察力和耐心,通过随机生成的颜色矩阵和一个变化点,玩家需在两幅画布中找出不同的颜色点。文章详细讲解了游戏的核心功能,包括随机颜色矩阵生成、点的闪烁提示、自定义配色方案等。此外,作者展示了使用Vue 3和TypeScript开发的代码实现,带领读者一步步深入了解游戏的逻辑与细节。
103 68
|
2天前
|
存储 前端开发 JavaScript
🚀前端轻松实现网页内容转换:一键复制、保存图片及生成 Markdown
在现代前端开发中,提升用户的交互体验至关重要。本文将详细介绍如何使用 HTML2Canvas 和 Turndown 两个强大的 JavaScript 库,实现将网页选中文本转化为图片并保存或复制到剪贴板,或将内容转换为 Markdown 格式。文章包含核心代码实现、技术细节和功能拓展方向,为开发者提供了一个轻量级的解决方案,提升用户体验。
100 68
|
16天前
|
Linux 虚拟化 开发者
一键将CentOs的yum源更换为国内阿里yum源
一键将CentOs的yum源更换为国内阿里yum源
849 5
|
9天前
|
存储 SQL 关系型数据库
彻底搞懂InnoDB的MVCC多版本并发控制
本文详细介绍了InnoDB存储引擎中的两种并发控制方法:MVCC(多版本并发控制)和LBCC(基于锁的并发控制)。MVCC通过记录版本信息和使用快照读取机制,实现了高并发下的读写操作,而LBCC则通过加锁机制控制并发访问。文章深入探讨了MVCC的工作原理,包括插入、删除、修改流程及查询过程中的快照读取机制。通过多个案例演示了不同隔离级别下MVCC的具体表现,并解释了事务ID的分配和管理方式。最后,对比了四种隔离级别的性能特点,帮助读者理解如何根据具体需求选择合适的隔离级别以优化数据库性能。
232 4
|
2天前
|
人工智能
云端问道12期-构建基于Elasticsearch的企业级AI搜索应用陪跑班获奖名单公布啦!
云端问道12期-构建基于Elasticsearch的企业级AI搜索应用陪跑班获奖名单公布啦!
121 1
|
6天前
|
并行计算 PyTorch TensorFlow
Ubuntu安装笔记(一):安装显卡驱动、cuda/cudnn、Anaconda、Pytorch、Tensorflow、Opencv、Visdom、FFMPEG、卸载一些不必要的预装软件
这篇文章是关于如何在Ubuntu操作系统上安装显卡驱动、CUDA、CUDNN、Anaconda、PyTorch、TensorFlow、OpenCV、FFMPEG以及卸载不必要的预装软件的详细指南。
472 2