SSM框架(手把手详解)

目录

SSM框架介绍

1、Spring

1 spring概述

1.1 Spring定义

1.2 Spring核心

2 入门案例

2.1 入门案例

使用配置文件内容初始化属性信息

3 相关概念

3.1 IoC

3.2 IoC容器

3.3 依赖注入DI

4 IoC容器实现

5 依赖注入DI

学习反转控制思想 理解Spring的依赖注入(DI)

6 注解管理Bean

6.1 Bean对象定义及获取

6.1.1 Bean对象定义

6.1.2 Bean对象获取

6.1.3 应用分析

6.2 依赖注入DI

6.2.1 @Value注解

6.2.2 @Autowired注解

6.2.2.1 根据类型注入

6.2.2.2 根据接口类型注入

6.2.3 @Qualifier注解

6.2.4 @Resource注解[了解]

6.3 Bean作用域

6.3.1 说明

6.3.2 单实例与多实例

6.3.3 应用分析

6.4 Bean生命周期

6.4.1 说明

6.4.2 完整生命周期

6.4.3 生命周期验证

6.4.4 生命周期扩展

6.5 引用外部属性文件

6.5.1 说明

6.5.2 使用流程

6.5.3 应用分析

6.6 自动扫描配置

6.6.1 说明

6.6.2 使用示例

6.6.3 应用分析

2、MyBatis框架

MyBatis框架-注解管理

1 概述

2 数据初始化

2.1 概述

2.2 数据初始化

3 环境说明

4 整合MyBatis初步分析

5 整合MyBatis完成用户数据操作

5.1 知识点设计

5.2 用户表设计

5.3 Pojo对象设计

5.4 Dao接口设计

5.5 Dao单元测试实现

6 整合MyBatis完成标签业务操作

6.1 业务描述

6.2 知识点设计

6.3 weibo表设计

6.4 Pojo对象设计

6.5 Dao接口设计

6.6 Dao单元测试实现

7 整合MyBatis完成评论业务操作

7.1 评论表设计

7.2 Pojo对象设计

MyBatis框架-xml管理(重点)

1 xml与注解比较

1.1 xml定义

1.2 和SQL注解比较

2 环境初始化

3 使用流程

4 xml配置SQL标签

3、Spring MVC 框架

简介

分层设计

MVC设计思想

Spring中的MVC设计

创建项目

访问项目静态资源

访问网页资源

访问图片资源

访问项目动态资源

基本原理分析


1.SSM框架介绍

SSM框架是一种经典的Java Web 开发框架,它由Spring、SpringMVC和MyBatis三个开源项目整合而成。SSM框架各个组件之间相互配合,提供了一套完整的解决方案,用于开发企业级的Java Web应用程序。

  • Spring:提供了IoC(Inverse of Control,控制反转)和AOP(Aspect-Oriented Programming,面向切面编程)等功能,简化了企业级应用的开发流程。
  • SpringMVC:基于MVC(Model-View-Controller,模型-视图-控制器)设计模式,用于处理用户请求和响应,并提供了灵活的请求映射和视图渲染功能。
  • MyBatis:为数据库访问提供了一个简单而强大的持久层框架,通过SQL映射文件和注解来实现对象关系映射(ORM)。

Spring

1 spring概述

1.1 Spring定义

Spring是一款主流的 Java EE 开源框架,目的是用于简化Java企业级引用的开发难度和开发周期。从简单性、可测试性的角度而言,任何Java应用都可以从Spring中受益。Spring框架提供自己提供功能外,还提供整合其他技术和框架的能力。

自2004年4月,Spring1.0 版正式发布以来,Spring已经步入到了第6个大版本,即 Spring6,本课程采用 Spring5.3.24 正式版本。

Spring官网地址:Spring | Home

1.2 Spring核心

Spring指的是Spring Framework,通常我们称之为Spring框架。

Spring的两个核心模块

  • IoC控制反转

  • Inverse of Control 的简写,为 控制反转,指把创建对象交给 Spring 进行管理。

  • 即:反转资源获取方向,把自己创建资源、向环境索取资源的方式变为环境自动将资源准备好,我们享受资源注入。

  • AOP面向切面编程

  • Aspect Oriented Programming 的简写,为 面向切面编程。AOP 用来封装多个类的公共行为,将那些与业务无关,却为业务模块共同调用的逻辑封装起来,减少系统的重复代码。

2 入门案例

工程环境

  • JDK版本:Java 8-15

  • Spring版本:5.3.24

2.1 入门案例

  • 第1步:聚合工程下构建子工程 _03spring

  • 第2步:pom.xml中添加依赖并刷新 Maven

<dependencies>
    <!-- Maven坐标:https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <!-- 引入spring context依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.24</version>
    </dependency>
</dependencies>

  • 第3步:工程中创建包 cn.tedu.spring.example,并创建类 User.java,添加 @Component 注解;

package demo1;

import org.springframework.stereotype.Component;

/**
 * 凡是希望被Spring容器管理的类,需要添加一个注解:@Component
 *
 * Component:组件
 */
@Component
public class User {
    public void sayHello(){
        System.out.println("User:hello!");
    }
}
package demo1;

import org.springframework.stereotype.Component;

@Component
public class Person {
    public void sayHi(){
        System.out.println("Person:Hi!!");
    }
}

  • 第4步:创建测试类 TestUser.java 进行测试;

package demo1;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 需求:
 * 在demo包下新建一个类:Person,并在类中添加一个方法:sayHi
 *
 * 创建Spring容器,并向容器获取一个Person对象,随后调用sayHi方法
 */
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("demo1");
        Person person = context.getBean(Person.class);
        person.sayHi();

        User user = context.getBean(User.class);
        user.sayHello();
    }
}
package demo1;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo {
    public static void main(String[] args) throws Exception{
        //需求:调用User对象的sayHello()方法

        /*
            1:传统方式-实例化对象,并调用方法
            优点:直观,效率高
            缺点:代码写死了,不利于后期更新维护
                后面出现了新的类或新的方法时,如果想调用则需要修改这里的代码
         */
        User user = new User();
        user.sayHello();


        /*
            2:反射
            缺点:要编写大量的反射代码
            优点:灵活,后期添加了新的类或新的方法,这里无需进行修改
         */
        Class cls = Class.forName("demo1.User");
        User user1 = (User)cls.newInstance();
        user1.sayHello();


        /*
            3:Spring容器
            优点:Spring管理项目中所有的类,可以基于IOC,AOP等特性进行灵活的开发

            创建Spring容器时,需要传入一个参数,该参数是包名
            Spring容器在床架时会扫描这个包,将该包及其子孙包中的所有类扫描一遍,并将
            具有@Component注解的类纳入到容器中进行管理
         */
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("demo1");
        //可以通过Spring容器获取一个类
        User user2 = context.getBean(User.class);
        user2.sayHello();

    }
}
 

使用配置文件内容初始化属性信息

/**
* 使用配置文件内容初始化属性信息
*/

demo2.user.name=rose
demo2.user.age=18

spring.datasource.classname=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/bbs?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
package test;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource("classpath:student.properties")
public class Student {
//    @Value("${student.name}")
    private String name;
//    @Value("${student.age}")
    private int age;
//    @Value("${student.gender}")
    private String gender;

    public String getName() {
        return name;
    }

    @Value("${student.name}")
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    @Value("${student.age}")
    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    @Value("${student.gender}")
    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}
package test;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 练习使用配置文件内容初始化属性信息
 * 在test包下新建一个类:Student
 * 定义三个属性
 * private String name
 * private int age
 * private String gender
 *
 * 提供对应的get,set方法等
 *
 * 在resource包下新建一个配置文件:student.properties
 * 里面定义三个信息
 * student.name
 * student.age
 * student.gender
 * 为Student类中三个属性配置对应的值
 *
 * 使用上述三个值让Spring初始化对象时为三个属性复制
 *
 */
public class Demo {
    public static void main(String[] args) {
        //创建Spring容器,并获取Student对象然后将其输出,观察属性是否正确初始化
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("test");
        Student student = context.getBean(Student.class);
        System.out.println(student);
    }
}

3 相关概念

3.1 IoC

IoC(Inversion of Control,控制反转)是一种编程思想;

IoC 是将对象的创建和管理交由框架来完成,而不是由开发人员手动创建和管理。

3.2 IoC容器

IoC容器是用来实现IoC思想的一个工具或者说技术手段;

它能够自动扫描应用程序中的对象,将它们实例化,并自动注入它们所需要的依赖对象,使应用程序的开发人员能够更加专注于业务逻辑的实现,而不用关心对象的创建和管理。Spring通过IoC容器来管理所有的Java对象的实例化和初始化,控制着对象与对象之间的依赖关系。

我们将由IoC容器管理的Java对象成为 Spring Bean,它与使用关键字 new 创建的Java对象没有任何区别。

3.3 依赖注入DI

DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想,是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

所以 IoC 是一种控制反转的思想,而依赖注入 DI 是对 IoC 的一种具体实现。

Bean管理:指Bean对象的创建,以及Bean对象中属性的赋值

package demo2;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component//用来让Spring管理这个类
@PropertySource("classpath:config.properties")//需要使用某配置文件上的信息
public class DBUtil {
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;

}
package demo2;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource("classpath:config.properties")
public class User {
//    @Value("张三")
//    @Value("${demo2.user.name}")
    private String name;
//    @Value("18")
//    @Value("${demo2.user.age}")
    private int age;

    public String getName() {
        return name;
    }

    @Value("${demo2.user.name}")
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    @Value("${demo2.user.age}")
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package demo2;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("demo2");
        User user = context.getBean(User.class);
        System.out.println(user);
    }
}

4 IoC容器实现

Spring中的IoC容器就是IoC思想的一个落地产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先需要创建IoC容器,Spring提供了IoC容器的两种实现方式

  • BeanFactory

  • 这是IoC容器的基本实现,是Spring内部使用的接口,面向Spring本身,不提供给开发人员使用。

  • ApplicationContext

  • BeanFactory的子接口,提供了更多高级特性,面向Spring的使用者,几乎所有场合都使用 ApplicationContext,而不使用底层的BeanFactory。

源码说明:

  • ApplicationContext的主要实现类

类型说明
AnnotationConfigApplicationContext使用注解方式构建IoC容器
ClassPathXmlApplicationContext使用XML配置文件方式构建Spring IoC容器

5 依赖注入DI

DI (Dependency Injection):依赖注入,依赖注入实现了控制反转的思想;

是指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。

所以 IoC 是一种控制反转的思想,而依赖注入 DI 是对 IoC 的一种具体实现。

学习反转控制思想 理解Spring的依赖注入(DI)

package demo3;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * 学习反转控制思想
 * 理解Spring的依赖注入(DI)
 */
public class Demo {
    public static void main(String[] args) {
        /*
            这里容易一旦初始化,就会扫描demo3包括其子孙包中的所有类,并将所有具有Component注解的
            类纳入其中管理(被管理的类之间存在依赖时,如果需要依赖注入则会自动将这些工作完成)
         */
        AnnotationConfigApplicationContext context
            = new AnnotationConfigApplicationContext("demo3");

        Person person = context.getBean(Person.class);
        person.play();
    }
}
package demo3;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

/**
 * 模拟人需要依赖衣服才可以出门
 *
 * 在Spring的编程模式中,我们的核心思想为"控制反转"
 * 指的是:当我们做某事需要一个类时,原本需要我们自行实例化,并进行
 *        初始化来控制。现在这个动作转交给Spring容器来完成
 *        这种通过编码控制一个对象实例化,初始化的事情反转交给
 *        Spring来完成,被称为"控制反转"
 *
 * Spring的控制反转,将我们依赖的对象进行管理
 * 什么是"依赖"(dependency)
 * 当我们完成某项工作需要用到另一个类,那么这个类就是我们的"依赖"。
 *
 * 我们需要获取依赖对象时,在Spring中可以采取两种方式:
 * 1:主动要:context.getBean(....)
 * 2:可以在当前类上声明一个属性,并使用相应的注解告诉Spring我需要用到某个依赖的对象,那么
 *   Spring在创建当前对象时会自然的将需要依赖的对象通过属性赋值"注入"到当前对象上供我们使用
 *   这个过程称为"依赖注入"
 *   第二种是我们后期最常用的方式
 *
 * 本案例来演示依赖注入
 * 环境:
 * 1:当前类Person应当是一个被Spring管理的类
 * 2:当Person需要用到一个类(Shirt)时,可以在Person类上声明一个属性private Shirt shirt;
 * 3:由于我们希望让Spring将Shirt"注入到当前属性上",就是为当前属性赋值,此时该属性上需要
 *   添加注解:@Autowired(自动装配)
 * 4:如果一旦Spring容器创建Person时,就会发现其内部的属性shirt,并通过@Autowired得知该属性
 *   需要配Spring容器装配,那么如果Shirt也是受Spring容器管理的,那么就会自动设置上,完成
 *   Person需要依赖Shirt的情况。
 *
 */
@Component//首先如果需要利用依赖注入,则当前类也要受Spring容器管理
public class Person {
//    @Autowired//自动装配,那么Spring容器创建Person时就会将Shirt注入进来(Shirt也要受Spring容器管理)
//    private Shirt shirt;//Person需要依赖Shirt,就将其定义为一个属性

//    @Autowired
//    private Sweater sweater;

    /*
        面向接口的编程思想
        依赖在这里定义为属性时,都建议定义为成接口或超类,而不是具体的某一种子类型。
        这样将来业务改变而导致需要更换依赖的类时,这里无需进行任何修改

        类似与:
        人不应当依赖具体的某一种衣服类型,比如毛衣,T恤等。而是依赖"衣服"这个超类。
        这样无论将来提供的是何种衣服,这里都不需要进行修改。
     */
    @Autowired
    /*
        当我们使用@Autowired注解需要Spring容器进行自动装配时,如果这里接口类型有多个实现类
        都被Spring容器管理时,那么容器在装配时就会报错,因为无法选择使用哪一个实现类。
        例如本案例:
        这里定义依赖的类型是Clothes.
        而Spring容器内部同时管理了Shirt,Sweater(它们都实现了Clothes接口),此时
        就会出现异常。

        解决办法:
        1:保证Spring容器内部Clothes的实现类只有一个被管理
        2:可以为每一个被管理的类添加一个"组件名"-在使用@Component注解时用参数定义
          例如:
          @Component("winter")
          public class Sweater implements Clothes {...}

          @Component("summer")
          public class Shirt implements Clothes {...}

          那么在Person这里定义依赖时,除了添加@Autowired注解之外,再添加一个
          注解:@Qualifier,并指名需要将哪个组件注入进来,这里参数指定组件名即可
          例如:
          如果需要使用Shirt,则这里@Qualifier("summer")就可以了,因为Shirt
          类上@Component("summer")

     */
    @Qualifier("summer")
    private Clothes clothes;

    public void play(){

        Object obj = new Person();

        //穿上衣服,出去玩
        /*
            1:最原始方式,自己创建
            Shirt shirt = new Shirt()

            2:主动向Spring容器要
            Shirt shrit = context.getBean(Shirt.class);
            但是上述模式,至少要先有Spring容器,所以如果每次都创建容器会比较麻烦

            3:依赖注入
            将Shirt shirt定义为一个属性,让Spring容器在创建Person的同时将Shirt注入进来
         */
//        shirt.wear();
//        sweater.wear();
        clothes.wear();
        System.out.println("出去玩!");
    }
}
package demo3;

import demo3.Clothes;
import org.springframework.stereotype.Component;

/**
 * T恤
 */
@Component("summer")
public class Shirt implements Clothes {
    public void wear(){
        System.out.println("穿上T恤");
    }
}
package demo3;

import demo3.Clothes;
import org.springframework.stereotype.Component;

/**
 * 毛衣
 */
@Component("winter")
public class Sweater implements Clothes {
    public void wear(){
        System.out.println("穿上毛衣");
    }
}
package demo3;

/**
 * 衣服接口
 */
public interface Clothes {
    void wear();
}

6 注解管理Bean

6.1 Bean对象定义及获取

在Spring框架规范中,所有由spring管理的对象都称之为Bean对象。

6.1.1 Bean对象定义

Spring提供了以下多个注解,这些注解可以直接标注在java类上,将它们定义成Spring Bean。

注解说明
@Component@Component注解,被其标注后,Spring容器扫描其所在包时就会将其纳入到Spring容器中进行管理。该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅标识容器中的一个组件(Bean),并且可以作用在任何层次,例如Service层、Dao层等,使用时只需将该注解标注在相应的类上即可。
@Repository该注解用于数据库(数据持久层)访问层(Dao层)的类标识为Spring中的Bean,功能与@Component相同。
@Service该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
@Controller该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。
package demo5;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

/**
 * @Component注解,被其标注后,Spring容器扫描其所在包时就会将其纳入到Spring容器中进行管理
 * 除了上述注解外,Spring框架还提供了其他注解:
 * @Repository:该注解也可以让当前类被Spring容器管理,这个注解通常标注数据持久层中的类(与数据库打交道的类)
 * @Service:同样受Spring容器管理,这个注解常用于处理业务逻辑的(业务层)类
 * @Controller:这个注解通常用来标注Controller类(与客户端打交道,控制器层,在SpringMVC框架中使用)
 */
@Component
//@Repository
//@Service
//@Controller
public class Person {
    @Autowired
    private Clothes clothes;

    public void play(){
        clothes.wear();
        System.out.println("出去玩!");
    }
}

6.1.2 Bean对象获取

通过 ApplicationContext 对象获取:调用 ApplicationContext 对象的 getBean() 方法,传入对应类的类型即可获取该 Bean 对象,示例:

ApplicationContext context = new AnnotationConfigApplicationContext("包扫描路径");
User user = context.getBean(类名.class);

6.1.3 应用分析

在 cn.tedu.spring 下创建子包 bean ,进行 bean 对象的创建及获取

  • 第1步:在bean包下创建类:UserDao

@Repository
public class UserDao {
    private String databaseUrl;
    private String username;
    private String password;
}

  • 第2步:在bean包下创建测试类:TestUserDao

public class TestUserDao {
    @Test
    public void testBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.bean");
        UserDao userDao = context.getBean(UserDao.class);
        System.out.println("userDao = "userDao);
    }
}

  • 说明:将java类中的 @Repository注解 替换为 @Component注解、@Service注解、@Controller注解 都可以。

6.2 依赖注入DI

6.2.1 @Value注解

@Value 注入是将属性值直接注入到 bean 中,主要用于注入一些简单类型的属性(如字符串、基本类型等);

使用时需要注意属性的类型和格式,否则会导致注入失败。

示例:在UserDao中进行属性值注入

  • 第1步:在UserDao的属性中通过@Value注解注入属性值

@Repository
public class UserDao {
    @Value("jdbc:mysql://localhost:3306/tedu")
    private String databaseUrl;
    @Value("root")
    private String username;
    @Value("root")
    private String password;

    @Override
    public String toString() {
        return "UserDao{" +
                "databaseUrl='"databaseUrl + '\'' +
                ", username='"username + '\'' +
                ", password='"password + '\'' +
                '}';
    }
}

  • 第2步:执行测试方法进行测试

6.2.2 @Autowired注解

@Autowired 注入是将对象注入到 bean 中,并且在注入对象时会根据依赖注入容器中 bean的类型 进行匹配。

如果容器中有多个类型匹配的bean存在,则会抛出异常。

因此,@Autowired注入常用于注入复杂对象、接口类型的属性或其他bean实例。

6.2.2.1 根据类型注入

定义UserService类,并注入UserDao对象

  • 第1步:在包bean下创建 UserService

@Service
public class UserService {
    @Value("注册业务")
    private String sname;
    // Autowired自动装配
    @Autowired
    private UserDao userDao;

    @Override
    public String toString() {
        return "UserService{" +
                "sname='"sname + '\'' +
                ", userDao="userDao +
                '}';
    }
}

  • 第2步:调整测试方法

public class TestUserDao {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.bean");
        UserDao userDao = context.getBean(UserDao.class);
        System.out.println("userDao = "userDao);

        UserService userService = context.getBean(UserService.class);
        System.out.println("userService = "userService);
    }
}

  • 第3步:执行测试方法测试

6.2.2.2 根据接口类型注入

进行依赖注入时,如果指定的是接口 InterfaceSpring 框架会自动找到该接口对应的实现类并创建 bean 对象注入吗?

在 cn.tedu.spring 下创建子包 auto

  • 第1步:创建接口 Cache

package cn.tedu.spring.auto; public interface Cache { }

  • 第2步:创建该接口实现类 CacheImpl1 ,并添加 @Component注解

package cn.tedu.spring.auto;

import org.springframework.stereotype.Component;

@Component
public class CacheImpl1 implements Cache{
} 

  • 第3步:创建类 UserCache ,并注入 CacheImpl1对象

@Component
public class UserCache {
    @Autowired
    private Cache cache;
}

  • 第4步:创建测试类 TestUserCache 进行测试

public class TestUserCache {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.auto");
        Cache bean = context.getBean(UserCache.class);
        System.out.println("bean = "bean);
    }
}

  • 总结: 当一个接口有一个唯一的实现类时,Spring框架会通过接口找到该接口对应的实现类,并进行bean对象的创建以及DI注入操作。

问题:那么如果一个接口有多个实现类,Spring框架会创建对应的 java bean 对象吗?

  • 第1步:创建 Cache 接口的实现类 CacheImpl2

@Component
public class CacheImpl2 implements Cache{
}

  • 第2步:执行测试方法,确认是否成功

总结: 当一个接口有多个实现类时,Spring无法确定注入哪个实现类对象,因此会报错,可以结合 @Qualifier注解 来解决这个问题。

6.2.3 @Qualifier注解

@Qualifier注解是用于限定一个接口有多个实现类时,根据指定的限定条件来选择具体的实现类的注解;

当Spring容器中存在多个实现同一接口的bean时,在注入时,由于不能确定注入哪一个实现类,就需要通过@Qualifier注解来明确指定要注入的bean的名称。

@Qualifier注解演示

  • 第1步:在 UserCache 中添加 @Autowired注解@Qualifier注解

@Component 
public class UserCache { 
    @Autowired 
    @Qualifier("aaa") 
    private Cache cache; 
}

  • 第2步:在 CacheImpl1 实现类中 @Component注解 中添加组件名

@Component("aaa") 
public class CacheImpl1 implements Cache{ 
}

  • 第3步:执行测试方法进行测试

  • 总结:在@Component注解中可以不用指定组件名称,默认为当前类的 类名首字母小写。

6.2.4 @Resource注解[了解]

@Resource 注解是 JavaEE 提供的注解之一,也支持在 Spring Framework 中使用。在 Spring 中,它可以用来注入 Bean 实例,与@Autowired注解的作用类似,但其也有自己的一些特点。

  • @Resource 注解是JDK扩展包中的,也就是说属于JDK的一部分。所以该解释是标准注解,而 @Autowired 注解是 Spring 框架自己的。

  • 装配规则

    • @Resource 注解默认根据名称装配 byName

    • 当未指定 name 时,则使用属性名作为 name 进行装配;

    • 如果通过 name 也未找到,则会自动启动通过类型 byType 装配。

  • 而@Autowired注解默认根据类型装配byType,如果想根据名称匹配,需要配合@Qualifier注解一起使用。

  • 课堂示例

  • 包名:resource

  • 接口:ResMapper

  • 实现类1:ResMapperImpl1

  • 实现类2:ResMapperImpl2

  • DI注入类:WeiboMapper

  • 测试类:TestWeiboMapper

  • 总结

  • 指定 @Resource 中的 name,则根据名称装配;

  • 未指定 name 时,则根据属性名装配;

  • 未指定 name,属性名也不一致,则根据类型装配.

6.3 Bean作用域

6.3.1 说明

在Spring框架中,Bean是按照作用域来创建的,常见的作用域有两种:SingletonPrototype。其中,Singleton (单例)是指整个应用中只有一个实例,并在第一次请求时创建实例。而 Prototype (多例)是指每次请求都会创建一个新的实例并返回,每个实例之间是相互独立的。可以通过 @Scope 注解来指定,默认是单实例。

6.3.2 单实例与多实例
  • 单实例

  • 单实例(Singleton)是指某个类只能创建唯一的一个实例对象,并且该类提供一个全局的访问点(静态方法)来让外界获取这个实例,常常用在那些只需要一个实例来处理所有任务的场景下,例如数据库连接。

  • 多实例

  • 多实例(Multiple Instance)则是指可以在同一个类的定义下,创建多个实例对象。每个对象都是相互独立的,有自己的状态和行为;常常用于需要同时处理多个任务的场景。

在Spring中可以通过 @Scope 注解来指定bean的作用域范围,具体如下

取值含义
@Scope("singleton")(默认)在IoC容器中,这个bean的对象为单实例
@Scope("prototype")这个bean在IoC容器中有多个实例
6.3.3 应用分析
  • 第1步:

package demo6;

/**
 * java有23种经典的设计模式
 *
 * 单例模式
 * 使用该模式设计的类全局仅有一个实例
 */
public class Singleton {
    //2 定义一个私有的静态的当前类型属性,并初始化一次(仅产生一个对象)
    private static Singleton instance = new Singleton();

    //1 私有化构造器,目的:不能让外面随意实例化,否则无法控制实例的个数
    private Singleton(){

    }
    //3 对外提供一个静态的用于返回当前类实例的方法,并且该方法始终返回上述静态属性对应的实例
    //  这样可以保证全局仅有一个实例
    public static Singleton getInstance(){
        return instance;
    }
}
package demo6;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
//@Scope("singleton")//用来指名单例,但是通常不用写,因为默认就是单例的
@Scope("prototype")//用来指名多实例模式
public class Person {
}
  • 第2步:创建测试类进行测试

package demo6;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("demo6");
        /*
            Spring容器默认管理对象都是单例的。
         */
        Person p1 = context.getBean(Person.class);
        Person p2 = context.getBean(Person.class);
        System.out.println(p1);
        System.out.println(p2);

    }
}
package demo6;

import java.lang.reflect.Constructor;

public class Demo {
    public static void main(String[] args) throws Exception {
//        Singleton s1 = new Singleton();
//        Singleton s2 = new Singleton();

        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();

        System.out.println(s1);
        System.out.println(s2);

        //暴力反射可以突破限制,但是在实际开发中应当谨慎
        Class cls = Class.forName("demo6.Singleton");
        Constructor c = cls.getDeclaredConstructor();//获取无参构造器
        c.setAccessible(true);//强行开启访问权限
        Singleton c3 = (Singleton) c.newInstance();
        Singleton c4 = (Singleton) c.newInstance();
        System.out.println(c3);
        System.out.println(c4);
    }
}
 

  • 总结

    • 当为单例模式 singleton 时,多次获取bean实例的地址是相同的

    • 当为多例模式 prototype 时,多次获取bean实例的地址是不同的

单例模式适用于需要共享数据并且需要避免重复创建实例的情况。而多例模式适用于需要动态地创建对象并提供独立实例的情况。

6.4 Bean生命周期

6.4.1 说明

程序中的每个对象都有生命周期,对象的创建、初始化、应用、销毁的整个过程称之为对象的生命周期;

在对象创建以后需要初始化,应用完成以后需要销毁时执行的一些方法,可以称之为是生命周期方法;

在spring中,可以通过 @PostConstruct@PreDestroy 注解实现 bean对象 生命周期的初始化和销毁时的方法。

  • @PostConstruct 注解

  • 生命周期初始化方法,在对象构建以后执行。

  • @PreDestroy 注解

  • 生命周期销毁方法,比如此对象存储到了spring容器,那这个对象在spring容器移除之前会先执行这个生命周期的销毁方法(注:prototype作用域对象不执行此方法)。

6.4.2 完整生命周期
  1. 实例化阶段(bean对象创建)

  2. 在这个阶段中,IoC容器会创建一个Bean的实例,并为其分配空间。这个过程可以通过 构造方法 完成。

  3. 属性赋值阶段

  4. 在实例化完Bean之后,容器会把Bean中的属性值注入到Bean中,这个过程可以通过 set方法 完成。

  5. 初始化阶段(bean对象初始化)

  6. 在属性注入完成后,容器会对Bean进行一些初始化操作;

  7. 使用阶段

  8. 初始化完成后,Bean就可以被容器使用了

  9. 销毁阶段

  10. 容器在关闭时会对所有的Bean进行销毁操作,释放资源。

6.4.3 生命周期验证
  • 第1步:DBConnect 类中打印生命周期过程

package demo7;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * 介绍一个对象在Spring容器中被管理时,生命周期的几个节点方法
 */
@Component
public class Person {
    private String name;

    //1:构造器是一个对象生命周期中第一个被调用的方法
    public Person(){
        System.out.println("1:Person的构造器调用了。");
    }

    public String getName() {
        return name;
    }

    //2:生命周期的第二个方法,当容器实例化对象完毕后,就会调用被@Value注解标注的set方法来进行属性初始化
    @Value("张三")
    public void setName(String name) {
        System.out.println("2:name属性对应的set方法被调用了");
        this.name = name;
    }

    //3:生命周期中第三个操作,当容器完成初始化对象后就会调用被@PostConstruct标注的方法,通知该对象初始化完毕
    @PostConstruct
    public void init(){//方法名叫什么都可以
        System.out.println("3:Person初始化完毕了,name:"+name);
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("4:容器要关闭了,通知我们这件事");
    }
}

  • 第2步:测试类中打印生命周期过程

package demo7;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Demo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext("demo7");
        System.out.println("Spring容器初始化完毕了...");
        Person person = context.getBean(Person.class);
        System.out.println("通过容器获取了Person对象");

        /*
            当我们关闭Spring容器时,容器会陆续调用它内部管理的对象的销毁方法来通知这件事
         */
        context.close();//关闭容器
        System.out.println("Spring容器关闭了!");

    }
}

  • 第3步:生命周期结果验证

创建包 eg3, 并创建类 ShopController ,请自行完成 Spring Bean 生命周期的每步验证。

6.4.4 生命周期扩展

Bean初始化和销毁方法可以在Bean生命周期的特定时机执行自定义逻辑,方便地对Bean进行管理和配置。

  • 初始化常见应用场景

    • 创建数据库连接

    • 加载资源文件

    • 进行数据校验

  • 销毁常见应用场景

    • 断开数据库连接

    • 保存数据

    • 释放占用的资源

6.5 引用外部属性文件

6.5.1 说明

实际开发中,很多情况下我们需要对一些变量或属性进行动态配置,而这些配置可能不应该硬编码到我们的代码中,因为这样会降低代码的可读性和可维护性。

我们可以将这些配置放到外部属性文件中,比如database.properties文件,然后在代码中引用这些属性值,例如jdbc.urljdbc.username等。这样,我们在需要修改这些属性值时,只需要修改属性文件,而不需要修改代码,这样修改起来更加方便和安全。

而且,通过将应用程序特定的属性值放在属性文件中,我们还可以将应用程序的配置和代码逻辑进行分离,这可以使得我们的代码更加通用、灵活。

6.5.2 使用流程
  • 第1步:创建外部属性文件(在 resources 目录下创建文件,命名为:"xxx.properties");

  • 第2步:引入外部属性文件(使用 @PropertySource("classpath:外部属性文件名") 注解);

  • 第3步:获取外部属性文件中的变量值 (使用 ${变量名} 方式);

  • 第4步:进行属性值注入.

6.5.3 应用分析
  • 第1步:在 resources 目录下创建文件 :database.properties

spring.datasource.username=root 
spring.datasource.password=root 
spring.datasource.url=jdbc://mysql://localhost:3306/test

  • 第2步:工程目录下创建子包 file 并创建类 Database

    • 通过 @PropertySource 注解引入外部文件

    • 通过 ${变量名} 获取属性值

    • 通过 @Value() 注解进行属性值注入

@Component
@PropertySource("classpath:database.properties")
public class Database {
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.user}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Override
    public String toString() {
        return "Database{" +
                "url='"url + '\'' +
                ", username='"username + '\'' +
                ", password='"password + '\'' +
                '}';
    }
}

  • 第3步:创建测试类 TestDatabase 进行测试

public class TestDatabase {
    @Test
    public void testFile(){
        ApplicationContext context = new AnnotationConfigApplicationContext("cn.tedu.spring.file");
        Database database = context.getBean(Database.class);
        System.out.println(database);
    }
}

6.6 自动扫描配置

6.6.1 说明

自动扫描配置是 Spring 框架提供的一种基于注解(Annotation)的配置方式,用于自动发现和注册 Spring 容器中的组件。当我们使用自动扫描配置的时候,只需要在需要被 Spring 管理的组件(比如 Service、Controller、Repository 等)上添加对应的注解,Spring 就会自动地将这些组件注册到容器中,从而可以在其它组件中使用它们。

在 Spring 中,通过 @ComponentScan 注解来实现自动扫描配置。

@ComponentScan 注解用于指定要扫描的包或类。

Spring 会在指定的包及其子包下扫描所有添加 @Component(或 @Service@Controller@Repository 等)注解的类,把这些类注册为 Spring Bean,并纳入 Spring 容器进行管理。

6.6.2 使用示例
@Configuration
@ComponentScan("cn.tedu.srping")
public class AppConfig {}

在此示例中,

  • @Configuration 注解

  • 表示将类 AppConfig 标识为一个 Spring 配置类,Spring 会来加载此类,并且读取其中的配置。

  • @ComponentScan 注解

  • 用于指定扫描的包路径 com.example.app

  • Spring 会自动在 cn.tedu.spring 包及其子包下扫描所有被 @Component 等注解标注的类,并将这些类注册为 Spring Bean。

6.6.3 应用分析
  • 第1步: 工程下创建包 config,并在此包下创建类 SpringConfig

package demo8;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * Spring容器配置类
 *
 * @Configuration标注的类就是Spring容器的配置类
 * 配置类上我们将来可以进行各种协助Spring容器进行初始化的操作
 */
@Configuration
@ComponentScan("demo8")//指定容器扫描的包
public class Config {
    /*
        可以在配置类上使用@Bean注解标注一个方法,那么这个方法返回的对象会纳入到Spring容器管理
        容器在初始化时会自动调用该方法。方法名字没有限制,做到见名知意即可。
        这样的操作为了解决那些我们在项目中引入的其他组件(例如之前学习的Druid连接池),那些组件
        没有@Component注解,因此不会被Spring容器主动管理,此时我们可以通过这种方式向容器注册
        某些希望被管理的对象。
     */
    @Bean
    public Student initStudent(){
        //随然Student没有@Component注解,但是我们可通过这种方式将实例放入容器中
        return new Student();
    }

    @Bean
    public DruidDataSource initDataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl("jdbc:mysql://localhost:3306/bbs?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true");
        ds.setUsername("root");
        ds.setPassword("root");
        ds.setMaxActive(20);
        ds.setInitialSize(5);
        return ds;
    }
}
package demo8;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.sql.SQLException;

@Component
public class DBUtil {
    @Autowired
    private DruidDataSource ds;

    public Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
}
package demo8;

import org.springframework.stereotype.Component;

@Component
public class Person {
}
package demo8;

public class Student {
}
 

  • 第2步:创建测试类 TestScan 进行测试

package demo8;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.sql.Connection;
import java.sql.SQLException;

public class Demo {
    public static void main(String[] args) throws SQLException {
//        AnnotationConfigApplicationContext context
//                = new AnnotationConfigApplicationContext("demo8");

        //可以基于给定的配置类信息来初始化容器
        AnnotationConfigApplicationContext context
                = new AnnotationConfigApplicationContext(Config.class);

        Person person = context.getBean(Person.class);
        System.out.println(person);

        Student student = context.getBean(Student.class);
        System.out.println(student);

        DBUtil dbUtil = context.getBean(DBUtil.class);
        System.out.println(dbUtil);
        Connection connection = dbUtil.getConnection();
        System.out.println(connection);
    }
}

MyBatis框架

MyBatis框架-注解管理

1 概述

Mybatis是一个优秀的持久层框架,底层基于JDBC实现与数据库的交互;

使用此框架程序员只需要通过注解或者修改xml配置文件的方式配置好需要执行的SQL语句,MyBatis框架会根据SQL语句生成对应的JDBC代码并对数据库中数据进行增删改查操作。

Mybatis框架的简单应用架构,如图所示:

2 数据初始化

2.1 概述

该项目是一款社交媒体应用,用户可以在平台上发表短文等信息,分享自己的想法、心情和生活。共设计3张表。

2.2 数据初始化
  • 数据表说明

    • 用户表user:存储微博用户信息;

    • 微博表weibo:存储用户所发布的微博信息内容;

    • 评论表comment:存储每条微博的所有评论。

  • 表关系说明

    • 用户表和微博表:一对多,一个用户可以发布多条微博,一条微博只能归属于一个用户;

    • 用户表和评论表:一对多,一个用户可以发布多条评论,一条评论只能归属于一个用户;

    • 微博表和评论表:一对多,一条微博下可以有多条评论,一条评论只能归于与一条微博。

  • 初始化数据表

DROP DATABASE IF EXISTS blog;
CREATE DATABASE blog CHARSET=UTF8;
USE blog;
CREATE TABLE user(
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50),
    password VARCHAR(50),
    nickname VARCHAR(50),
    created TIMESTAMP
)CHARSET=UTF8;

CREATE TABLE weibo(
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
        content VARCHAR(255),
    created TIMESTAMP,
    user_id INT
)CHARSET=UTF8;

CREATE TABLE comment(
        id BIGINT PRIMARY KEY AUTO_INCREMENT,
    content VARCHAR(255),
    created TIMESTAMP,
    user_id INT,
    weibo_id INT
)CHARSET=UTF8;

INSERT INTO user VALUES (100, 'Lucy', '123456', 'lucy', '1987-10-16 00:00:00');
INSERT INTO user VALUES (101, 'Tom', '123456', 'tom', '1987-10-16 00:00:00');
INSERT INTO user VALUES (102, 'Jim', '123456', 'jim', '1987-10-16 00:00:00');

INSERT INTO weibo VALUES (200, 'lucy的第1条微博', '2000-01-01 00:00:00', 100);
INSERT INTO weibo VALUES (201, 'lucy的第2条微博', '2000-01-01 00:00:00', 100);
INSERT INTO weibo VALUES (202, 'tom的第1条微博', '2000-01-01 00:00:00', 101);
INSERT INTO weibo VALUES (203, 'tom的第2条微博', '2000-01-01 00:00:00', 101);
INSERT INTO weibo VALUES (204, 'tom的第3条微博', '2000-01-01 00:00:00', 101);

INSERT INTO comment VALUES (300, 'lucy对第1条微博的评论', '2008-01-01 00:00:00', 100, 200);
INSERT INTO comment VALUES (301, 'tom对第1条微博的评论', '2008-01-01 00:00:00', 101, 200);
INSERT INTO comment VALUES (302, 'lucy对第2条微博的评论', '2008-01-01 00:00:00', 100, 201);
INSERT INTO comment VALUES (303, 'tom对第2条微博的评论', '2008-01-01 00:00:00', 101, 201);
INSERT INTO comment VALUES (304, 'jim对第2条微博的评论', '2008-01-01 00:00:00', 102, 201);

3 环境说明

  • 工程名称:_04MyBatis

  • SpringBoot版本:2.6.13

  • 依赖项:MySQL Driver、MyBatis Framework

4 整合MyBatis初步分析

  • application.properties配置文件中添加连接数据库信息

spring.datasource.url=jdbc:mysql://localhost:3306/blog?serverTimezone=Asia/Shanghai&characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root

5 整合MyBatis完成用户数据操作

5.1 知识点设计

基于本业务实现MyBatis基本操作,掌握MyBatis中xml配置SQL的应用。

5.2 用户表设计

用户表的设计如下(假如库中不存在这个表,需要创建),例如:

create table user(
    id int primary key auto_increment,
    username varchar(30),
    password varchar(30),
    nickname varchar(30),
    created timestamp
)

5.3 Pojo对象设计

在工程目录中创建pojo包,并创建User类,和数据表中的字段一 一对应。

package com.tedu._04mybatis.pojo;

import java.util.Date;

/**
 * 当前类User用于表示blog库中的user表
 * 该类的每一个实例可以表示user表中的一条记录
 */
public class User {
    private Integer id;
    private String username;
    private String password;
    private String nickname;
    private Date created;

生成get,set,tostring方法
}

5.4 Dao接口设计

基于MyBatis规范设计用户数据访问接口,在工程目录下创建包mapper,并创建UserMapper接口

  • @Mapper注解

  • 是由MyBatis框架提供,用于描述数据层接口,告诉系统底层为此接口创建其实现类,在实现类中定义数据访问逻辑,执行与数据库的会话(交互)

  • @Insert注解

  • 使 MyBatis 框架根据接口方法的参数类型自动生成插入数据的代码。

  • 占位符 #{}

  • #{} 是 MyBatis 框架中用来表示占位符的语法。

  • @Insert 注解中,#{}所代表的是一个占位符,它可以接受 Java 对象作为输入参数,并将其转换为预编译的 SQL 语句中的参数。使用 #{}可以帮助我们避免 SQL 注入等问题,同时也让 SQL 写起来更加简单。

package com.tedu._04mybatis.mapper;

import com.tedu._04mybatis.pojo.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
/**
 *  首先当前Mapper接口需要让MyBatis提供对应的实现类,因此需要在当前接口上
 *  使用MyBatis提供的注解:@Mapper
 *
 *  SpringBoot启动时,会调用MyBatis提供的starter,这里MyBatis就会扫描当前项目中
 *  所有被@Mapper标注的接口,并提供实现类(怎么实现的???答:动态代理 CGLIB)
 *  并且根据该接口中的方法上注解来具体实现对应方法的逻辑
 *  
 * 例如:
 *  @Insert:实现方法会执行该注解中的SQL语句完成插入操作。
 *         MyBatis要求的预编译SQL格式不是用"?"表示值,而是用"#{}"
 *         原因是它需要直到原本?对应的是方法传入对象的那个属性的值
 *
 *         好比我们原来自己用JDBC:
 *         标准的预编译SQL,用"?"代表值
 *         String sql = "INSERT INTO user(username,password,nickname,created) " +
 *                      "VALUES(?,?,?,?)";
 *         PreparedStatement ps = connection.preparedStatemen(sql);
 *
 *         我们自己需要硬编码,告知每一个?用的是方法传入的user对象那个属性的值
 *         ps.setString(1,user.getUsername());
 *         ps.setString(2,user.getPassword());
 *         ps.setString(3,user.getNickname());
 *         ps.setDate(4,user.getCreated());
 *
 *
 *         然后使用MyBatis后,预编译SQL写为:
 *         INSERT INTO user(username,password,nickname,created)
 *         VALUE (#{username},#{password},#{nickname},#{created})
 *
 *         其中#{username}就是用于告知MyBatis应当将方法传入的user对象的username
 *         属性的值
 *         因此"#{}"这里"{}"中要填写的是方法传入的对象对应的属性名
 *         本案例:
 *         int insert(User user);
 *
 *         那么#{username}就是告知MyBatis用insert方法传入的user对象的username
 */
@Mapper
public interface UserMapper {

    @Insert("INSERT INTO user(username,password,nickname,created) " +
            "VALUES(#{username},#{password},#{nickname},#{created})")
    int insert(User user);
    
}

5.5 Dao单元测试实现

新建测试方法进行测试

package com.tedu._04mybatis;

import com.tedu._04mybatis.mapper.UserMapper;
import com.tedu._04mybatis.pojo.*;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.List;

@SpringBootTest
class ApplicationTests {

    @Autowired
    private UserMapper userMapper;
    
    @Test
    void contextLoads() {
        Date date = new Date();
        System.out.println(date);
    }
    
    @Test
    void testInsertUser(){
        System.out.println("插入一个用户信息");
        User user = new User();
        user.setUsername("张三");
        user.setPassword("789789");
        user.setNickname("张三");
        user.setCreated(new Date());

        int num = userMapper.insert(user);
        System.out.println(num>0?"插入成功":"插入失败");
    }
}

6 整合MyBatis完成标签业务操作

6.1 业务描述

基于SpringBoot脚手架工程对MyBatis框架的整合,实现对微博内容weibo表进行操作。

6.2 知识点设计

本业务中重点讲解@Select,@Insert,@Update,@Delete注解应用。

6.3 weibo表设计

标签表设计如下(这个表已经存在则无需创建)

CREATE TABLE weibo(
    id INT PRIMARY KEY AUTO_INCREMENT,
        content VARCHAR(255),
    created TIMESTAMP,
    user_id INT
)CHARSET=UTF8;

6.4 Pojo对象设计

在pojo下创建Weibo类,用于和数据库中weibo做映射

import java.util.Date;

public class Weibo {
    private Integer id;
    private String content;
    private Date Created;
    private Integer UserId;

    // setter() getter() toString()
}

6.5 Dao接口设计

在mapper先新建WeiboMapper接口

@Mapper
public interface WeiboMapper {
  /**在微博表中插入数据*/
  @Insert("INSERT INTO weibo VALUES(NULL,#{content},#{created},#{userId})")
  int insert(Weibo weibo);

  /**根据微博id查询数据*/
  @Select("SELECT id,content,created,user_id userId FROM weibo WHERE id=#{id}")
  Weibo selectByWeiboId(int id);

  /**查询所有微博信息*/
  @Select("SELECT id,content,created,user_id userId FROM weibo")
  List<Weibo> selectWeibo();

  /**更新微博表数据*/
  @Update("UPDATE weibo SET content=#{content},created=#{created},user_id=#{userId} WHERE id=#{id}")
  int updateById(Weibo weibo);

  /**删除微博表数据*/
  @Delete("DELETE FROM weibo WHERE id=#{id}")
  int deleteById(int id);
}

6.6 Dao单元测试实现

在测试类中新建测试方法进行测试

/**自动装配*/
@Autowired
private WeiboMapper weiboMapper;

/**在微博表中插入数据-测试方法*/
@Test
void InsertWeibo(){
    Weibo weibo = new Weibo();
    weibo.setContent("今天天气真不错呀");
    weibo.setCreated(new Date());
    weibo.setUserId(1);
    weiboMapper.insert(weibo);
}

/**根据微博id查询数据*/
@Test
void selectByWeiboIdTest(){
    System.out.println(weiboMapper.selectByWeiboId(2));
}

/**查询所有微博信息*/
@Test
void selectWeiboTest(){
    System.out.println(weiboMapper.selectWeibo());
}

/**更新微博表数据-测试*/
@Test
void updateById(){
    Weibo weibo = new Weibo();
    weibo.setId(1);
    weibo.setContent("这是我修改后的微博");
    weibo.setCreated(new Date());
    weibo.setUserId(1);

    System.out.println(weiboMapper.updateById(weibo));
}

/**删除微博表数据-测试*/
@Test
void deleteByIdTest(){
    System.out.println(weiboMapper.deleteById(1));
}

注:insert、update、delete返回值为受影响的数据条数int。

7 整合MyBatis完成评论业务操作

7.1 评论表设计

评论表的设计如下(假如表已经存在则无需创建)

CREATE TABLE comment(
        id INT PRIMARY KEY AUTO_INCREMENT,
    content VARCHAR(255),
    created TIMESTAMP,
    user_id INT,
    weibo_id INT
);

7.2 Pojo对象设计

在pojo下新建Comment类,实现和评论表的映射关系

public class Comment {
    private Integer id;
    private String content;
    private Date created;
    private Integer userId;
    private Integer weiboId;

    // setter() getter() toString()
}

MyBatis框架-xml管理(重点)

1 xml与注解比较

1.1 xml定义

XML是一种可扩展性语言,用户可以自己定义标签,用来描述特定类型的数据;

XML的语法严格,每个标签都必须有一个结束标签,标签的嵌套关系也必须合法;

1.2 和SQL注解比较
  • xml配置SQL,可以将SQL语句和JAVA代码分离开

  • xml配置SQL,支持动态SQL语句

  • xml配置SQL,支持SQL语句的复用

2 环境初始化

依然使用 _04MyBatis工程

  • SpringBoot版本:2.6.13

  • 依赖项

    • MyBatis Framework

    • MySQL Driver

  • 注释掉 UserMapper、WeiboMapper、CommentMapper中的所有 @Insert() @Update() @Select() @Delete 注解

3 使用流程

  1. 在resources目录下创建 mappers目录,用来存放xml配置文件

  2. 在文档服务器中下载映射文件模板

  配置文件下载 - MyBatis Mapper映射文件,下载后解压得到:someMapper.xml

并将该文件拷贝到resources/mappers目录下

  1. application.properties中添加配置:mybatis框架映射配置文件的位置

# 设置MyBatis框架的映射(Mapper)配置文件的位置
mybatis.mapper-locations=classpath:mappers/*.xml

4 xml配置SQL标签

  • 说明

  • 在 Mybatis 的 XML 文件中,SQL 语句都是使用 SQL 标签来定义的。

  • 常用的SQL标签

    • select

    • 用于查询操作,包括多表查询、条件查询等。可以使用 resultType 来指定返回结果的类型。

    • insert

    • 用于插入操作,并将其自动注入实体类中。

    • update

    • 用于更新操作,包括更新一条记录或者批量更新。

    • delete

    • 用于删除操作,包括删除一条记录或者批量删除。

    • if、foreach、set

    • 用于条件控制,可以根据不同的条件进行查询、插入、更新和删除操作。if 标签用于指定可以为空的查询条件,foreach 标签用于循环查询,set 标签用于指定更新操作的字段值。

    • sql:用于定义可重用的 SQL 片段,通常是一些较为复杂的 SQL 片段。可以在其它 SQL 语句中使用 include 标签来引用 SQL 片段。

    • include:用于引入外部的 SQL 片段。可以在 include 标签的 refid 属性中指定外部 SQL 片段的名字,然后在当前 SQL 中使用它。

这些 SQL 标签可以随意组合,可以使 SQL 语句变得很灵活和强大。通常需要根据实际业务场景选择合适的标签来实现相应的 SQL 操作。

Spring MVC 框架

简介

分层设计

在大型软件系统设计时,业务一般会相对复杂,假如所有业务实现的代码都纠缠在一起,会出现逻辑不清晰、可读性差,维护困难,改动一处就牵一发而动全身等问题。为了更好解决这个问题就有了我们现在常说的分层架构设计。

分层设计的本质其实就是将复杂问题简单化,首先基于单一职责原则(SRP-Single responsibility principle)让每个对象各司其职,各尽所能。然后再基于“高内聚,低耦合”的设计思想实现相关层对象之间的交互。这样可以更好提高程序的可维护性和可扩展性,例如生活中的楼宇设计,生日蛋糕设计,企业的组织架构设计等。在计算机通讯领域还有典型的OSI参考模型(Open System Interconnection Reference Model),TCP/IP参考模型等,这些定义了数据的传递过程,如图所示:

其中,TCP/IP参考模型是OSI参考模型的一种网络简化通讯模型,这里不对每一层进行介绍,具体有关OSI和TCP/IP的知识请自行进行查阅。

基于Java的互联网架构,在进行分层设计时,首先要对整体的系统分层架构有一个基本认识,如图-35所示:

而整体应用分层架构中的应用软件层我们还会按照一定的规则继续分层,而在这一层最常用的规则或设计思想就MVC。

MVC设计思想

MVC(Model–view–controller)是软件工程中的一种软件架构模式,基于此模式把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。目的是通过这样的设计使程序结构更加简洁、直观,降低问题的复杂度。其中MVC各个组成部分为:

  • 视图(View) - UI设计人员进行图形界面设计,负责实现与用户交互,例如html、css、js等都属于这一层的技术

  • 控制器(Controller)- 负责获取请求,处理请求,响应结果,在java中充当控制器对象的一般是servlet。

  • 模型(Model) - 实现业务逻辑,数据逻辑实现。(@Service-描述业务层对象,@Repository-描述数据层对象)

Spring中的MVC设计

Spring MVC 是Spring 框架中基于MVC设计思想,实现的一个用于处理Web请求的框架。这个框架封装了对Servlet的技术的应用,简化了程序员对请求和响应过程中数据的处理。

创建项目

创建项目module,并添加Spring Web依赖(提供了spring mvc依赖支持),这个依赖添加时,会关联下载一个tomcat依赖(这个tomcat为一个嵌入式HTTP服务器,这个服务器底层基于ServerSocket实现。)

访问项目静态资源

访问网页资源

接下来,运行启动类,检测项目是否可以启动(服务启动后的默认端口为8080)。项目启动成功后,

直接打开浏览器可以输入http://localhost:8080/index.html进行访问,如图所示:

访问图片资源
  • 第1步:复制任意一张图片到static下

  • 第2步:右键static目录,Rebuild static

  • 第3步:浏览器测试

我们输入地址访问静态资源index.html时,底层有一个处理静态资源请求的Handler对象,这个对象是ResourceHttpRequestHandler,它可以从项目的resources/static目录下去读取静态资源,例如index.html

当然,这个目录下还可以放一些图片等静态资源。

访问项目动态资源

动态请求:基于用户请求获取服务端返回的动态数据。

  • 第一步:编写HelloController类并通过@Controller注解进行描述,将其交给spring管理。@Controller注解描述的对象就是Spring MVC用于处理请求的一个Handler对象。

    package cn.tedu.mvc.controller;
    
    @Controller
    public class HelloController {
    
    }

    第二步:在HelloController中添加方法,代码如下。

  • @ResponseBody
    @RequestMapping("/hello")
    public String doSayHello(){
        return "Hello Spring MVC";
    }

    第三步:启动Web服务器(默认项目嵌入的是tomcat),打开浏览器分别输入http://localhost:8080/hello进行访问测试。

    • 说明

      • Controller注解

            添加在类上;

            表示该类是一个控制器,负责处理用户的请求,并将处理结果生成响应返回给客户端

        • RequestMapping注解

              请求注解,用于定义请求url(例如"/hello")和请求方式(例如Get请求);

          • ResponseBody注解

                用于定义响应特性。这个注解描述方法时,用于告诉Spring MVC,方法的返回值不是view,是数据对象。假如是字符串则直接响应到客户端,假如是pojo对象则转换为json格式字符串响应到客户端。

          1. 基本原理分析

          Spring MVC简易架构如下图所示(具体分析需要结合断点设计,进行流程分析):

          • DispatcherServlet :前端控制器, 处理请求的入口。

          • HandlerMapping:映射器对象, 用于管理url与对应执行链(ExecutionChain)的映射关系。

          • HandlerAdapter:处理器适配器,用于实现不同Handler(Controller)类型之间的适配

          • Controller:后端控制器-handler, 负责处理请求的控制逻辑。

          • ModelAndView: 用于封装数据信息和页面信息的一个对象。

          • ViewResolver:视图解析器,解析对应的视图关系(前缀+viewname+后缀)。

          图中,Spring MVC执行流程

          1. 客户端发送请求至前端控制器DispatcherServlet;

          2. DispatcherServlet收到请求后,调用处理器映射器HandlerMapping;

          3. HandlerMapping根据请求URL找到具体的Controller;

          4. 通过处理器适配器HandlerAdapter适配具体执行该Controller的方式;

          5. Controller处理请求,并返回ModelAndView;

          6. DispatcherServlet通过ViewReslover(视图解析器)确定负责显示数据的具体View;

          7. DispatcherServlet对View进行渲染视图(即将Model填充至视图组件中),并将完整的视图响应到客户端。

          评论
          添加红包

          请填写红包祝福语或标题

          红包个数最小为10个

          红包金额最低5元

          当前余额3.43前往充值 >
          需支付:10.00
          成就一亿技术人!
          领取后你会自动成为博主和红包主的粉丝 规则
          hope_wisdom
          发出的红包
          实付
          使用余额支付
          点击重新获取
          扫码支付
          钱包余额 0

          抵扣说明:

          1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
          2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

          余额充值