SSM框架

SSH和SSM的区别

spring简介

  1. Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。
  2. Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象(模
    块和模块)之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模
    块)的关系。
  3. Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互
    调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring
    容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且
    不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成
    “织入”。

mvc设计模式

是用一种业务逻辑、数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件里面,在需要改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑,达到减少编码的时间,提高代码复用性。

springmvc简介

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。

ORM程序技术

ORM 是 Object Relational Mapping 的缩写,译为“对象关系映射”框架。 所谓的 ORM 框架就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述 Java 对象与数据库表之间的映射关系,自动将 Java 应用程序中的对象持久化到关系型数据库的表中。

mybatis简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

spring工程搭建

搭建配置spring

1.maven 创建普通 java 工程并调整整体工程环境
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.坐标 依赖添加 spring 框架核心坐标添加

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>

在这里插入图片描述
3.编写bean
在这里插入图片描述

package com.shsxt.service;

public class HelloService {
    public void hello(){
        System.out.println("hello spring");
    }
}

4.spring 配置文件的编写

在 src 下新建 xml 文件,并拷贝官网文档提供的模板内容到 xml 中,配置bean 到 xml 中,把对应 bean 纳入到 spring 容器来管理
在这里插入图片描述
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
xmlns 即 xml namespace xml 使用的命名空间
xmlns:xsi 即 xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
-->
<bean id="helloService" class="com.shsxt.service.HelloService"></bean>
</beans>

编写代码测试

验证 spring 框架环境是否搭建成功
在这里插入图片描述

package com.shsxt.service;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class HelloServiceTest {
    @Test
    public void test1() throws Exception {
        /**
         * 1.加载Spring的配置文件
         * 2.取出Bean容器中的实例
         * 3.调用bean方法
         */
        // 1.加载Spring的配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        // 2.取出Bean容器中的实例
        HelloService helloService = (HelloService) context.getBean("helloService");
        // 3.调用bean方法
        helloService.hello();
    }

}

执行过程分析

BeanFactory

BeanFactory是基础类型的IOC容器,是管理bean容器的根接口,并提供了完整的IOC服务支持

简单来说BeanFactory就是一个管理Bean的工厂,它主要负责初始化各种Bean、调用生命周期等方法

ApplicationContext

ApplicationContext被称为应用上下文,是BeanFactory接口的子接口,在其基础上提供了其他的附加功能,扩展了BeanFactory接口

ClassPathXmlApplicationContext

ClassPathXmlApplicationContext是ApplicationContext的实现类,也在其基础上加了许多附加功能

该类从类路径ClassPath中寻找指定的XML配置文件,找到并完成对象实例化工作

spring IOC&DI

IOC(Inversion of Control 控制反转)是面向对象编程中的一种设计模式

其最常见的方式叫做DI(Dependency Injection 依赖注入)

通过控制反转,将实例化对象的控制权,由手动的new变成了Spring框架通过反射机制实例化

需要使用的时候,依赖通过配置文件以及注解的方式注入到对象中

项目常用后端代码结构 如下图所示:
在这里插入图片描述

我们新建Maven项目名为“spring-ioc“,设置好Maven版本、配置文件以及Maven仓库

以查询User数据为例对比IOC的引入前后程序耦合性

引入IOC之前

引入IOC之前

代码实现

User模块实体类:User.java

package entity;

public class User {
    private Integer id;
    private String name;
    private Integer gender;
    // 省略getter&setter方法
}

User模块视图类:UserVo.java

package vo;

public class UserVo {
    private Integer id;
    private String name;
    private Integer gender;
    private String genderName;
    // 省略getter&setter方法
    public UserVo() {
    }
    public UserVo(User user) {
        this.id = user.getId();
        this.name = user.getName();
        this.gender = user.getGender();
    }
    // 省略toString方法
}

User模块Dao层:UserDao.java

package dao;

public interface UserDao {
    User getEntity(Integer id);
}
User模块Dao层实现类:UserDaoImpl.java

package dao.impl;

public class UserDaoImpl implements UserDao {
    public User getEntity(Integer id) {
        // 此处应该从数据库查询值 方便起见直接返回一个固定对象
        User user = new User();
        user.setId(1);
        user.setName("Anne");
        user.setGender(0);
        return user;
    }
}

User模块Service层:UserService.java

package services;

public interface UserService {
    UserVo getVo(Integer id);
}

User模块Service层实现类:UserServiceImpl.java

package services.impl;

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserVo getVo(Integer id) {
        // 手动实例化Dao
        userDao = new UserDaoImpl();
        // 执行Dao层方法
        User user = userDao.getEntity(id);
        // 省略业务逻辑处理。。。
        UserVo userVo = new UserVo(user);
        userVo.setGenderName(userVo.getGender() == 0 ? "female" : "male");
        return userVo;
    }
}

引入IOC(XML)

要想使用SpringIOC首先需要导入Spring框架基础包并且添加Spring核心配置文件

将依赖交给Spring的beanFactory管理

<bean id="userDao" class="dao.impl.UserDaoImpl"/>
<bean id="userService" class="services.impl.UserServiceImpl"/>
<bean id="userController" class="controller.UserController"/>

User模块测试类:UserTest.java

读取配置文件刷新Spring容器
Controller由手动实例化改为从Spring容器拿取
把ApplicationContext传到Controller层继续使用

public class UserTest {
    public static void main(String[] args) {
        // 读取配置文件刷新Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        // 从Spring容器拿Controller
        UserController userController = (UserController) context.getBean("userController");
        // 执行Controller层方法,因为之后还需要用到context对象,故下传
        UserVo userVo = userController.getVo(1, context);
        System.out.println(userVo);
    }
}

User模块Controller层:UserController.java

Service由手动实例化改为从Spring容器拿取
把ApplicationContext传到Service层继续使用

package controller;

public class UserController {
    private UserService userService;

    public UserVo getVo(Integer id, ApplicationContext context) {
        // 从Spring容器拿Service
        userService = (UserService) context.getBean("userService");
        // 执行Service层方法,因为之后还需要用到context对象,故下传
        return userService.getVo(id, context);
    }
}

User模块Service层实现类:UserServiceImpl.java

Dao由手动实例化改为从Spring容器拿取

package services.impl;

public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserVo getVo(Integer id, ApplicationContext context) {
        // 从Spring容器拿Dao
        userDao = (UserDao) context.getBean("userDao");
        // 执行Dao层方法
        User user = userDao.getEntity(id);
        // 省略业务逻辑处理。。。
        UserVo userVo = new UserVo(user);
        userVo.setGenderName(userVo.getGender() == 0 ? "female" : "male");
        return userVo;
    }
}

测试结果

测试结果如下图所示:
在这里插入图片描述

表示已经将所有的依赖由手动实例化改为从Spring容器拿取

缺点分析

因为每一个类的实例化都需要一个bean标签,一个大型工程有很多类,配置文件的内容未免过于臃肿,维护成本高

解决方式

使用注解形式实现SpringIOC

XML改注解(IOC)

核心配置文件修改

context-component-scan标签Spring框架自定义的xml标签,通过base-package属性指明需要被自动扫描实例化的类所在位置

如下代码所示,我们在dao、services、controller下的类是需要扫描自动注入容器的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
       ">

    <!-- bean definitions here -->
    <context:component-scan base-package="dao"/>
    <context:component-scan base-package="services"/>
    <context:component-scan base-package="controller"/>

</beans>

修改好后运行项目发现context.getBean()代码报错
在这里插入图片描述
说明不是在base-package下的所有类都会自动注入到容器,而是要搭配注解使用

常用注解介绍

@Component:一般用于通用组件类上使用的注解
@Service:一般用于业务逻辑层上使用的注解
@Controller:一般用于流程控制层上使用的注解
@Repository:一般用于数据持久层上使用的注解
依次添加注解,添加之后运行再次报错找不到bean
在这里插入图片描述
其实我们在添加注解后,Spring会默认给每个bean设置id,值为类名首字母改为小写

这次报错原因就是找不到名为”userService“的bean

解决办法就是在注解时设置bean的id,保证可以找到bean
在这里插入图片描述
测试结果如下
在这里插入图片描述

引入DI

上面所有的内容都是将对象放入Spring容器中

那么放入之后的使用呢,目前都是使用ApplicationContext拿取容器中的对象

接下来讲解如何使用注解实现依赖注入

常用注解介绍

@Autowired注解自动按照类型注入

会从容器中寻找符合依赖类型的实例,但是也有缺点:

因为时按照类型匹配,如果找不到匹配的实例也会抛出异常

如果容器中有多个匹配的类型也会抛出异常,需要指定引入的实例id

@Qualifier注解作用是在按照类型注入的基础之上,再按照Bean的id注入。所以如果是使用了@Autowire注解自动注入,但是容器中却有多个匹配的实例,可以搭配此注解,指定需要注入的实例id

@Resource注解作用是指定依赖按照id注入,还是按照类型注入。当只使用注解但是不指定注入方式的时候,默认按照id注入,找不到再按照类型注入。

代码实现

User模块Controller层:UserController.java

package controller;

@Controller
public class UserController {
    // 改为自动注入
    @Autowired
    private UserService userService;

    public UserVo getVo(Integer id, ApplicationContext context) {
        // 执行Service层方法,因为之后还需要用到context对象,故下传
        return userService.getVo(id, context);
    }
}

User模块Dao层实现类:UserDaoImpl.java

去除指定bean id,改为默认bean id(userDaoImpl)

package dao.impl;

// 改为默认bean id“userDaoImpl”
@Repository
public class UserDaoImpl implements UserDao {
    public User getEntity(Integer id) {
        // 此处应该从数据库查询值 方便起见直接返回一个固定对象
        User user = new User();
        user.setId(1);
        user.setName("Anne");
        user.setGender(0);
        return user;
    }
}

User模块Service层实现类:UserServiceImpl.java

改为自动注入并指定需要注入的实例id

package services.impl;

@Service("userService")
public class UserServiceImpl implements UserService {
    // 改为自动注入并指定需要注入的实例id
    @Autowired
    @Qualifier("userDaoImpl")
    private UserDao userDao;

    public UserVo getVo(Integer id) {
        // 执行Dao层方法
        User user = userDao.getEntity(id);
        // 省略业务逻辑处理。。。
        UserVo userVo = new UserVo(user);
        userVo.setGenderName(userVo.getGender() == 0 ? "female" : "male");
        return userVo;
    }
}

测试结果

测试结果如下图所示:
在这里插入图片描述
表示

@Autowired注解已将UserService依赖自动注入UserController
@Qualifier注解已指定UserDao依赖的bean id,并使用@Autowired注解自动注入UserServiceImpl

springAOP

代理模式

Spring的核心特性就是IOC和AOP,之前整理了SpringIOC,这篇文章就来写一下SpringAOP(Aspect Oriented Programming),即:面向切面编程

面向切面编程是指通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态统一添加功能的一种技术

在学习SpringAOP之前我们需要了解什么是代理模式

文中提到的基于接口的JDK动态代理与基于子类的CGLib动态代理两种动态代理的方式都是实现SpringAOP的基础

在spring中,虽然引入了AspectJ的语法,但是他本质上使用的是动态代理的方式,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式

如果目标对象有接口,优先使用JDK 动态代理,如果目标对象没有接口,则使用CGLib动态代理

简单转账功能

新建Maven项目名为“spring-aop“,设置好Maven版本、配置文件以及Maven仓库

准备数据

# 删除spring_aop数据库
drop database if exists spring_aop;

# 创建spring_aop数据库
create database spring_aop;

# 使用spring_aop数据库
use spring_aop;

# 创建account表
create table account (
    id int(11) auto_increment primary key,
    accountNum varchar(20) default NULL,
    money int(8) default 0
);

# 新增数据
insert into account (accountNum, money) values
("622200001",1000),("622200002",1000);

导包

导入Spring基础包

导入操作数据库、连接数据库、测试需要的包

<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.13.RELEASE</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

核心配置文件

配置自动扫包

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
       ">

    <!-- bean definitions here -->
    <context:component-scan base-package="dao"/>
    <context:component-scan base-package="services"/>
    <context:component-scan base-package="utils"/>

</beans>

配置数据源

<!--配置QueryRunner-->
<bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!--连接数据库的必备信息-->
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_aop"></property>
    <property name="user" value="root"></property>
    <property name="password" value="root"></property>
</bean>

代码编写

数据库连接工具类:ConnectionUtils.java

package utils;

@Component
public class ConnectionUtils {
    private ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
    @Autowired
    private ComboPooledDataSource dataSource;

    /**
     * 获得当前线程绑定的连接
     *
     * @return
     */
    public Connection getThreadConnection() {
        try {
            // 看线程是否绑了连接
            Connection conn = tl.get();
            if (conn == null) {
                // 从数据源获取一个连接
                conn = dataSource.getConnection();
                // 和线程局部变量  绑定
                tl.set(conn);
            }
            // 返回线程连接
            return tl.get();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 把连接和当前线程进行解绑
     */
    public void remove() {
        tl.remove();
    }
}
Account模块实体类:Account.java
package dao;

public interface AccountDao {
    /**
     * 更新
     *
     * @param account
     */
    void updateAccount(Account account);

    /**
     * 根据编号查询账户
     *
     * @param accountNum
     * @return 如果没有结果就返回null,如果结果集超过一个就抛异常,如果有唯一的一个结果就返回
     */
    Account findAccountByNum(String accountNum);
}

Account模块Dao层实现类:AccountDaoImpl.java

package dao.impl;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    // 数据库查询工具类
    @Autowired
    private QueryRunner runner;
    // 数据库连接工具类
    @Autowired
    private ConnectionUtils connectionUtils;

    /**
     * 更新
     *
     * @param account
     */
    public void updateAccount(Account account) {
        try {
            runner.update(connectionUtils.getThreadConnection(),
                    "update account set accountNum=?,money=? where id=?",
                    account.getAccountNum(), account.getMoney(), account.getId());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据编号查询账户
     *
     * @param accountNum
     * @return 如果没有结果就返回null,如果结果集超过一个就抛异常,如果有唯一的一个结果就返回
     */
    public Account findAccountByNum(String accountNum) {
        List<Account> accounts = null;
        try {
            accounts = runner.query(connectionUtils.getThreadConnection(),
                    "select * from account where accountNum = ? ",
                    new BeanListHandler<Account>(Account.class),
                    accountNum);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        if (accounts == null || accounts.size() == 0) {
            // 如果没有结果就返回null
            return null;
        } else if (accounts.size() > 1) {
            // 如果结果集超过一个就抛异常
            throw new RuntimeException("结果集不唯一,数据有问题");
        } else {
            // 如果有唯一的一个结果就返回
            return accounts.get(0);
        }
    }
}

Account模块Service层:AccountService.java

package services;

public interface AccountService {
    /**
     * 转账
     *
     * @param sourceAccount 转出账户
     * @param targetAccount 转入账户
     * @param money         转账金额
     */
    void transfer(String sourceAccount, String targetAccount, Integer money);
}

Account模块Service层实现类:AccountServiceImpl.java

package services.impl;

@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    /**
     * 转账
     *
     * @param sourceAccount 转出账户
     * @param targetAccount 转入账户
     * @param money         转账金额
     */
    public void transfer(String sourceAccount, String targetAccount, Integer money) {
        // 查询原始账户
        Account source = accountDao.findAccountByNum(sourceAccount);
        // 查询目标账户
        Account target = accountDao.findAccountByNum(targetAccount);
        // 原始账号减钱
        source.setMoney(source.getMoney() - money);
        // 目标账号加钱
        target.setMoney(target.getMoney() + money);
        // 更新原始账号
        accountDao.updateAccount(source);
        // 更新目标账号
        accountDao.updateAccount(target);
        System.out.println("转账完毕");
    }
}

Account模块测试类:AccountTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class AccountTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer() {
        accountService.transfer("622200001", "622200002", 100);
    }
}

执行结果

控制台打印结果
-

引入代理模式解决事务

实现思路介绍

创建一个工具类,目的是用于管理数据库的事务,提供事务的开启,提交,回滚等操作;
创建一个代理处理器类,目的是生成转账实现类的代理对象,对转账的业务方法提供增强,主要是在数据操作之前,和操作之后干点事,嘿嘿嘿;
在 Spring 的配置文件中,通过 xml 文件的标签实例化管理事务的工具类和生成代理对象的处理器类。
代码实现

事务管理器:TransactionManager.java

此工具类主要作用是对数据库连接实现事务的开启,提交以及回滚

至于何时开启、提交、回滚事务,根据业务场景需要调用该类的方法即可

package transaction;

@Component
public class TransactionManager {
    // 数据库连接工具类
    @Autowired
    private ConnectionUtils connectionUtils;

    /**
     * 开启事务
     */
    public void beginTransaction() {
        try {
            System.out.println("开启事务");
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    public void commit() {
        try {
            System.out.println("提交事务");
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    public void rollback() {
        try {
            System.out.println("回滚事务");
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    public void release() {
        try {
            System.out.println("释放连接");
            connectionUtils.getThreadConnection().close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        connectionUtils.removeConnection();
    }
}

事务代理工具类:TransactionProxyUtils

此类的核心代码是getAccountService方法,该方法返回代理业务类示例

在代理对象的invoke方法内部,实现对原始被代理对象的增强

package utils;

@Component
public class TransactionProxyUtils {
    //被代理的业务类接口
    @Autowired
    private AccountService accountService;
    //提供事务管理的工具类
    @Autowired
    private TransactionManager transactionManager;

    /**
     * 获取AccountService代理对象
     *
     * @return
     */
    public AccountService getAccountService() {
        return (AccountService) Proxy.newProxyInstance(
                accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 添加事务的支持
                     *
                     * @param proxy     被代理的对象实例本身
                     * @param method    被代理对象正在执行的方法对象
                     * @param args      正在访问的方法参数对象
                     * @return
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        //
                        Object rtValue = null;
                        try {
                            // 执行操作前开启事务
                            transactionManager.beginTransaction();
                            // 执行操作
                            rtValue = method.invoke(accountService, args);
                            // 执行操作后提交事务
                            transactionManager.commit();
                            // 返回结果
                            return rtValue;
                        } catch (Exception e) {
                            // 捕捉到异常执行回滚操作
                            transactionManager.rollback();
                            throw new RuntimeException(e);
                        } finally {
                            // 最终释放连接
                            transactionManager.release();
                        }
                    }
                });

    }
}

核心配置文件:applicationContext.xml

添加事务管理bean
<context:component-scan base-package=“transaction”/>
配置代理Service

<!--配置代理的service-->
<bean id="transactionProxyAccountService" factory-bean="transactionProxyUtils" factory-method="getAccountService"/>

Account模块测试类:AccountTest.java

将原本引入的AccountService实例改为AccountService的事务代理对象

@Qualifier(“transactionProxyAccountService”)
执行结果

首先将数据库中两账户余额都改为1000

update account set money = 1000;

在这里插入图片描述

引入AOP(XML)

相关概念

使用Spring的AOP替代代理类。先回顾下AOP的概念

AOP是一种编程设计模式,是一种编程技术,使用AOP后通过修改配置即可实现增加或者去除某些附加功能

学习AOP中的常用术语:

Join point(连接点)
所谓连接点是指那些可以被拦截到的点

在Spring中这些点指的是方法,可以看作正在访问的,或者等待访问的那些需要被增强功能的方法

Spring只支持方法类型的连接点

Pointcut(切入点)
切入点是一个规则,定义了我们要对哪些Joinpoint进行拦截

因为在一个程序中会存在很多的类,每个类又存在很多的方法,Pointcut来标记哪些方法会应用AOP对该方法做功能增强

Advice(通知)
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。也就是对方法做的增强功能。通知分为如下几类:

前置通知:在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为

后置通知:在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知

最终通知:无论连接点执行后的结果如何,正常还是异常,都会执行的通知

异常通知:如果连接点执行因抛出异常而退出,则执行此通知

环绕通知:环绕通知可以在方法调用之前和之后执行自定义行为

Target(目标)
Target指的是代理的目标对象,更通俗的解释就是:AOP对连接点方法做增强,底层是代理模式生成连接点所在类的代理对象,那么连接点所在的类,就是被代理的类称呼为Target

Aspect(切面)
切面本质是一个类,只不过是个功能类,作为整合AOP的切入点和通知。

一般来讲,需要在Spring的配置文件中配置,或者通过注解来配置

Weaving(织入)
织入是一种动作的描述,在程序运行时将增强的功能代码也就是通知,根据通知的类型(前缀后缀等…)放到对应的位置,生成代理对象

Proxy(代理)
一个类被AOP织入增强后,产生的结果就是代理类

代码实现

在执行原始业务类前对方法增强也就是SpringAOP中所谓的前置通知,对原始业务类中的方法执行之后的增强行为就是后置通知

而一旦出现异常,那么所做的动作就是异常通知。本案例使用几种通知,来实现事务的控制。

删除事务代理工具类:TransactionProxyUtils.java

导入aspectjweaver包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.3</version> </dependency>配置文件中添加 AOP 的相关配置

<!-- aop相关的节点配置 -->
<aop:config>
    <!-- 切入点  表示哪些类的哪些方法在执行的时候会应用Spring配置的通知进行增强 -->
    <aop:pointcut expression="execution ( * services.*.*(..))" id="pc"/>
    <!-- 配置切面类的节点  作用主要就是整合通知和切入点 -->
    <aop:aspect ref="transactionManager">
        <aop:before method="beginTransaction" pointcut-ref="pc"/>
        <aop:after-returning method="commit" pointcut-ref="pc"/>
        <aop:after method="release" pointcut-ref="pc"/>
        <aop:after-throwing method="rollback" pointcut-ref="pc"/>
    </aop:aspect>
</aop:config>

修改测试类代码
在这里插入图片描述

XML改注解(AOP)

使用注解介绍

@Aspect
此注解用于表明某个类为切面类,而切面类的作用我们之前也解释过,用于整合切入点和通知

@Pointcut
此注解用于声明一个切入点,表明哪些类的哪些方法需要被增强

@Before 前置通知
在连接点之前运行的通知类型,它不会阻止流程进行到连接点,只是在到达连接点之前运行该通知内的行为

@AfterReturning 后置通知
在连接点正常完成后要运行的通知,正常的连接点逻辑执行完,会运行该通知

@After 最终通知
无论连接点执行后的结果如何,正常还是异常,都会执行的通知

@AfterThrowing 异常通知
如果连接点执行因抛出异常而退出,则执行此通知

代码实现

删除XML中的AOPXML配置并注解代理模式
<!-- 注解 开启代理模式 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>注释事务管理器类:TransactionManager.java

package transaction;

@Component
@Aspect
public class TransactionManager {
    // 数据库连接工具类
    @Autowired
    private ConnectionUtils connectionUtils;

    @Pointcut("execution(* services.*.*(..))")
    private void transactionPointcut() {
    }

    /**
     * 开启事务
     */
    @Before("transactionPointcut()")
    public void beginTransaction() {
        try {
            System.out.println("开启事务");
            connectionUtils.getThreadConnection().setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 提交事务
     */
    @AfterReturning("transactionPointcut()")
    public void commit() {
        try {
            System.out.println("提交事务");
            connectionUtils.getThreadConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 回滚事务
     */
    @AfterThrowing("transactionPointcut()")
    public void rollback() {
        try {
            System.out.println("回滚事务");
            connectionUtils.getThreadConnection().rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 释放连接
     */
    @After("transactionPointcut()")
    public void release() {
        try {
            System.out.println("释放连接");
            connectionUtils.getThreadConnection().close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        connectionUtils.removeConnection();
    }
}

执行结果

控制台打印结果
在这里插入图片描述

mybatis工程搭建

JDBC连接及操作数据库

连接数据库步骤

第一步:注册驱动

第二步:获取连接

第三步:获取statement对象

第四步:执行SQL语句返回结果集

第五步:遍历结果集

第六步:关闭连接释放资源

传统方式连接数据库


public class JDBCDemo {
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		//1.注册驱动
		DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		//2.获取连接
		Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm", "root", "root");
		//3.获取Statement对象
		PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user");
		//4.执行SQL语句返回结果集
		ResultSet resultSet = preparedStatement.executeQuery();
		//5.遍历结果集
		while (resultSet.next()) {
			System.out.println(resultSet.getString("username"));
		}
		//6.释放资源
		resultSet.close();
		preparedStatement.close();
		connection.close();
	}
}

用这种方式连接数据库存在一个重要的问题:

注册驱动时,当前类和MySQL的驱动类有很强的依赖关系。

当我们没有驱动类的时候,连编译都不能通过。

这种调用者与被调用者之间的依赖关系,就叫做程序的耦合,耦合分为高耦合(紧密联系)和低耦合(松散联系)

我们在开发中,理想的状态应该是编译时不依赖,运行时才依赖。

要做到编译时不依赖,就需要使用反射来创建类对象。

即将注册驱动部分的代码稍作修改如下:

编译时不依赖的数据库连接

public class JDBCDemo {
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		//1.注册驱动
		//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
		Class.forName("com.mysql.jdbc.Driver");
		//2.获取连接
		Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssm", "root", "root");
		//3.获取Statement对象
		PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user");
		//4.执行SQL语句返回结果集
		ResultSet resultSet = preparedStatement.executeQuery();
		//5.遍历结果集
		while (resultSet.next()) {
			System.out.println(resultSet.getString("username"));
		}
		//6.释放资源
		resultSet.close();
		preparedStatement.close();
		connection.close();
	}
}

这样做的好处是,我们的类中不再依赖具体的驱动类,此时就算删除mysql的驱动jar包依然可以通过编译,只不过因为没有驱动类所以不能运行罢了。

不过,此处还有一个问题,就是我们反射类对象的全限定类名称是在java类中写死的,数据库的端口号、用户名密码也是写死的,一旦要修改就等于是要修改源码。
自己小打小闹写的代码改源码什么的还好说,但如果是上线项目,改源码势必要停服务器重新编运行。

这么看来这个问题造成的后果很严重,其实它的解决方法也很简单,使用配置文件配置数据库连接信息就可以啦。

使用配置文件连接数据库

配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=root

代码实现

public class JDBCDemoPro {
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		//读取配置文件db.properties
		Properties prop = new Properties();
		prop.load(new FileInputStream("db.properties"));
		 
		//获取配置文件中的相关参数值
		String driver = prop.getProperty("jdbc.driver");
		String url = prop.getProperty("jdbc.url");
		String user = prop.getProperty("jdbc.username");
		String password = prop.getProperty("jdbc.password");
		
		//1.注册驱动
		Class.forName(driver);
		//2.获取连接
		Connection connection = DriverManager.getConnection(url, user, password);
		//3.获取Statement对象
		PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user");
		//4.执行SQL语句返回结果集
		ResultSet resultSet = preparedStatement.executeQuery();
		//5.遍历结果集
		while (resultSet.next()) {
			System.out.println(resultSet.getString("username"));
		}
		//6.释放资源
		resultSet.close();
		preparedStatement.close();
		connection.close();
	}
}

引入mybatis依赖

maven仓库查询网址:MavenRepository

mybatis:MyBatis基础包
logback-classic:日志依赖

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.3.0-alpha5</version>
    <scope>test</scope>
</dependency>

刷新maven等待自动下载

libraries中有了所有导入的包表示依赖引入完成

编程式配置方法

MyBatis官方文档中并未详细的介绍如何编程式使用MyBatis,因为编程式配置方法代码有点复杂

但是大家没必要被代码吓退,因为在实际的开发中几乎没有机会去写这段代码,一般都是通过配置文件来拿到配置然后开启会话的

我们之所以讲解编程式配置方法,是因为使用配置文件配置时屏蔽了太多的细节

为了层层递进的介绍MyBatis的基础用法,使大家熟悉MyBatis整体结构,我们需要讲解编程式配置方法

代码实现

删除JDBC连接及操作数据库:JDBCDemo.java

新建编程式配置文件:StartNoXml.java

@SuppressWarnings({"SqlResolve", "SqlNoDataSourceInspection", "Duplicates"})
public class StartNoXml {
    public static void main(String[] args) throws SQLException {
        // 准备jdbc事务类
        JdbcTransactionFactory jdbcTransactionFactory = new JdbcTransactionFactory();
        // 配置数据源
        PooledDataSource dataSource = new PooledDataSource(
                "com.mysql.cj.jdbc.Driver",
                "jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false",
                "root",
                "root");
        // 配置环境,向环境中指定环境id、事务和数据源
        Environment environment = new Environment.Builder("development")
                .transactionFactory(jdbcTransactionFactory)
                .dataSource(dataSource).build();
        // 新建 MyBatis 配置类
        Configuration configuration = new Configuration(environment);
        // 得到 SqlSessionFactory 核心类
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        // 开始一个 sql 会话
        SqlSession session = sqlSessionFactory.openSession();
        // 得到 sql 连接并运行 sql 语句
        PreparedStatement preStatement = session
                .getConnection()
                .prepareStatement("SELECT * FROM user WHERE id = ?");
        preStatement.setInt(1, 1);
        ResultSet result = preStatement.executeQuery();
        // 验证结果
        while (result.next()) {
            System.out.println("username: " + result.getString("username"));
            System.out.println("age: " + result.getString("age"));
        }
        // 关闭会话
        session.close();
    }
}

执行结果

控制台打印结果如下图所示,表示我们已经成功连接数据库并查出了需要的值
在这里插入图片描述

配置文件配置mybatis

代码实现

在resources文件夹下新建配置文件:mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 环境变量 -->
    <environments default="development">
        <environment id="development">
            <!-- 事务管理器 -->
            <transactionManager type="JDBC"/>
            <!-- 数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

有了上面编程式 API 的使用经验,那么你一定可以轻松的看懂配置项:

configuration 标签对应 Configuration 类
environment 标签对应 Environment 类
transactionManager 标签对应 JdbcTransactionFactory 类
dataSource 标签对应 PooledDataSource 类
启动类:StartWithXml.java

修改类名为StartWithXml
读取配置文件

InputStream configuration = Resources.getResourceAsStream("mybatis-config.xml");

执行结果

控制台打印结果如下图所示,表示我们已经成功连接数据库并查出了需要的值

mybatis实现mapper配置并查询数据

什么是mapper注解方式使用mapper

在MyBatis工程搭建 中我们主要讲解的是 MyBatis 如何连接数据库,具体执行 SQL 语句使用的是 JDBC 方式

但在实际应用中是不会选择 JDBC 来执行 SQL 的,MyBatis 提供了 Mapper 作为 Java 方法和 SQL 语句之间的桥梁,来帮助我们更好地去使用 SQL

Java 接口方法与 SQL 语句以及 mapper 之间的关系如下图所示:
在这里插入图片描述

XML方式使用mapper

因为MyBatis是一个持久层框架,所以我们在使用之前需要执行如下SQL语句备好数据源

#删除mybatis_demo数据库
drop database if exists mybatis_demo;

#创建mybatis_demo数据库
create database mybatis_demo;

#使用mybatis_demo数据库
use mybatis_demo;

#创建account表
create table user (
    id int auto_increment primary key,
    username varchar(20),
    age int,
    score int
);

#新增数据
insert into user (id, username, age, score) values
(1,'peter', 18, 100), (2,'pedro', 24, 200),
(3,'jerry', 28, 500), (4,'mike', 12, 300),
(5,'tom', 27, 1000);

我们以查询User数据为例感受Mapper引入后和JDBC执行SQL有什么区别

MyBatis 提供了注解和XML两种方式来连接Java方法和SQL语句,首先学习注解方式使用Mapper

select-resultType

代码实现

在UserMapper.xml文件中,我们新增 selectUserById 标签,该 select 标签的作用是:通过id查询用户

<select id="selectUserById" resultType="">
    SELECT * FROM user WHERE id = #{id}
</select>

上面的内容返回的是所有字段值,我们需要自己创建实体类来存储查出来的值

在entity包下创建User实体类:User.java

package entity;

public class User {
    private Integer id;
    private String username;
    private Integer age;
    private Integer score;
    // 省略getter&setter方法
    // 省略toString方法
}

把UserMapper.xml文件中selectUserById标签的返回类型改为上面创建的实体类

resultType="entity.User"

User模块Mapper层:UserMapper.java

/**
 * 通过用户id查询用户信息
 *
 * @param id
 * @return
 */
User selectUserById(Integer id);

User模块测试类:UserTest.java

// 调用通过用户id查询用户信息的方法
User user = mapper.selectUserById(1);
System.out.println(user);

测试结果
在这里插入图片描述

select-resultMap

MyBatis自动帮助我们映射数据库数据和Java对象,其实这是MyBatis在幕后帮我们创建了resultMap对象,我们也可手动定义

代码实现

User模块Mapper层配置文件:UserMapper.xml

<resultMap id="userMap" type="entity.User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="age" column="age"/>
    <result property="score" column="score"/>
</resultMap>

上标签中有和两个子标签

其中标签是主键,其它字段均使用 result 标签来映射

标签有property和column两个属性

其中 property 是 Java 对象中的属性名称,column 是数据表与之对应的字段名称

把UserMapper.xml文件中selectUserById标签的返回类型改为上面创建的resultMap

resultMap="userMap"

测试结果

mybatis操作数据

select单条数据查询

要想使用MyBatis首先需要导入MySQL驱动包、MyBatis框架基础包并且添加MyBatis核心配置文件

首先复习下上个章节的内容

代码实现

在entity包下创建User实体类:User.java

package entity;

public class User {
    private Integer id;
    private String username;
    private Integer age;
    private Integer score;
    // 省略getter&setter方法
    // 省略toString方法
}

在dao包下创建User模块Dao层:UserDao.java

package dao;

public interface UserDao {
    /**
     * 通过用户id查询用户信息
     *
     * @param id
     * @return
     */
    User selectUserById(Integer id);
}

在mybatis-config.xml配置文件中添加上对应的mapper配置

<!-- mapper配置 -->
<mappers>
    <mapper class="dao.UserDao"/>
</mappers>

在resources文件夹下新建dao包,并在其下新建User模块Dao层配置文件:UserDao.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="dao.UserDao">
    <select id="selectUserById" resultType="entity.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>

新建User模块测试类:UserTest.java

@SuppressWarnings({"Duplicates"})
public class UserTest {
    public static void main(String[] args) throws IOException, SQLException {
        // 读取配置文件
        InputStream configuration = Resources.getResourceAsStream("mybatis-config.xml");
        // 得到 SqlSessionFactory 核心类
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        // 开始一个 sql 会话
        SqlSession session = sqlSessionFactory.openSession();
        // 得到 Dao
        UserDao dao = session.getMapper(UserDao.class);
        // 调用通过用户id查询用户信息的方法
        User user = dao.selectUserById(1);
        System.out.println(user);
        // 关闭会话
        session.close();
    }
}

执行结果
在这里插入图片描述

select多条数据查询

代码实现

但是在实际使用时我们常常需要一次性查询多条数据,例如:SELECT * FROM user

User模块Dao层配置文件:UserDao.xml

<select id="selectUserList" resultType="entity.User">
    SELECT * FROM user
</select>

这时要用到List集合装对象来完成多条数据的查询工作,User模块Dao层:UserDao.java

/**
 * 查询所有用户信息
 *
 * @return
 */
List<User> selectUserList();

User模块测试类:UserTest.java

// 调用查询所有用户信息的方法
List<User> userList = dao.selectUserList();
userList.forEach(u -> System.out.println(u));
TIPS: 对于userList.forEach(u -> System.out.println(u));

不理解的需要复习一下for forEach 循环及Lambda表达式使用
执行结果
在这里插入图片描述

insert数据

代码实现

User模块Dao层:UserDao.java

/**
 * 新增用户信息
 *
 * @param user
 * @return
 */
int insertUser(User user);

User模块Dao层配置文件:UserDao.xml

<insert id="insertUser" parameterType="entity.User">
    INSERT INTO user VALUES (#{id},#{username},#{age},#{score})
</insert>

User模块测试类:UserTest.java

// 调用查询所有用户信息的方法
List<User> userListBefore = dao.selectUserList();
userListBefore.forEach(u -> System.out.println(u));
// 创建一个要新增的对象并赋值
User insertUser = new User();
insertUser.setId(6);
insertUser.setUsername("anne");
insertUser.setAge(18);
insertUser.setScore(600);
// 调用新增用户信息的方法
int count = dao.insertUser(insertUser);
// 调用查询所有用户信息的方法
List<User> userListAfter = dao.selectUserList();
userListAfter.forEach(u -> System.out.println(u));

执行结果
在这里插入图片描述

update数据

代码实现

User模块Dao层:UserDao.java

/**
 * 修改用户信息
 *
 * @param user
 * @return
 */
int updateUserById(User user);

User模块Dao层配置文件:UserDao.xml

<update id="updateUserById" parameterType="entity.User">
    UPDATE user SET
    age = #{age},
    username = #{username},
    score = #{score}
    WHERE id = #{id}
</update>

User模块测试类:UserTest.java

// 调用通过用户id查询用户信息的方法
User userBefore = dao.selectUserById(6);
System.out.println(userBefore);
// 把成绩改为900
userBefore.setScore(900);
// 调用修改用户信息的方法
dao.updateUserById(userBefore);
// 调用通过用户id查询用户信息的方法
User userAfter = dao.selectUserById(6);
System.out.println(userAfter);

执行结果

控制台打印结果如下图所示
在这里插入图片描述

delete数据

代码实现

User模块Dao层:UserDao.java

/**
 * 删除用户信息
 *
 * @param id
 * @return
 */
int deleteUserById(Integer id);

User模块Dao层配置文件:UserDao.xml

<delete id="deleteUserById">
    DELETE FROM user WHERE id = #{id}
</delete>

User模块测试类:UserTest.java

// 调用查询所有用户信息的方法
List<User> userListBefore = dao.selectUserList();
userListBefore.forEach(u -> System.out.println(u));
// 调用删除用户信息的方法
int count = dao.deleteUserById(6);
// 调用查询所有用户信息的方法
List<User> userListAfter = dao.selectUserList();
userListAfter.forEach(u -> System.out.println(u));

执行结果

控制台打印结果如下图所示
在这里插入图片描述

springmvc工程搭建

springmvc请求映射注解

Spring MVC 提供了以下这些请求映射注解:

请求映射注解 说明 类/方法
@RequestMapping 通用的请求处理 类
@GetMapping 处理 HTTP GET 请求 查询方法
@PostMapping 处理 HTTP POST 请求 新增修改方法
@PutMapping 处理 HTTP PUT 请求 新增修改方法
@PatchMapping 处理 HTTP PATCH 请求 新增修改方法
@DeleteMapping 处理 HTTP DELETE 请求 删除方法
除了 @RequestMapping ,其它类型的注解本质上是 @RequestMapping 的简写形式

比如 @GetMapping 其实可以写为@RequestMapping( method = RequestMethod.GET)

建议在类级别上只使用 @RequestMapping ,用于指定基本路径

而在每个方法上根据方法功能不同使用更具体的请求映射注解

搭建配置文件springmvc

创建项目

使用IDEA创建Maven工程

IDEA配置Maven

我们新建Maven项目名为“springmvc-demo“,设置好Maven版本、配置文件以及Maven仓库

由于SpringMVC在 MVC设计模式 中是作为控制器负责前后端代码的连接,我们首先需要添加WEB文件夹用来存放前端代码

添加web

右键我们的项目名 -> 选择“Add Framework Support”
在这里插入图片描述

编写代码测试

编写Controller层

controller包下新建HelloController类

package controller;

@Controller
public class HelloController {

    @RequestMapping("/hello")
    public String hello(Model model){
        // Model 封装数据
        model.addAttribute("msg","HELLO MY FIRST SPRING MVC PROJECT");

        // 返回的字符串就是视图的名字 会被视图解析器处理
        return "hello";
    }
}

配置Spring容器自动扫描包

将Controller对象放进Spring容器

Spring核心配置文件:applicationContext.xml

<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="controller"/>

编写jsp

WEB-INF包下新建jsp包,jsp包下新建hello.jsp文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    ${msg}
</body>
</html>

编写web.xml

配置前端控制器

<!-- 配置前端控制器 -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

配置初始化参数
在服务器启动时 加载spring的核心配置文件applicationContext.xml

配置初始化参数的代码写在前端控制器内

<!-- 配置初始化参数 -->
<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</init-param>

设置启动级别
设置启动级别的代码也写在前端控制器内,数字越小启动越早

1
设置SpringMVC拦截请求

<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

用url-pattern中的内容来标识请求拦截:

/ 匹配所有的请求;(不包括.jsp)
/* 匹配所有的请求;(包括.jsp)
为空时所有请求都会被SpringMVC拦截
配置中文乱码过滤器

<!--  乱码过滤 -->
<filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

运行web项目

打包

file -> Project Structure 打开项目构建管理框

首先删除默认打好的包
在这里插入图片描述

运行web项目

运行TomCat

点击绿色的小三角运行TomCat,出现如下内容表示运行成功
在这里插入图片描述

在浏览器输入 http://localhost:8080/hello 可以看到页面打印出了我们设置好的值
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值