Java SSM

zhenxi
2022-07-31 / 0 评论 / 2 阅读 / 正在检测是否收录...

1 Spring概述

Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。

提供了展现层 SpringMVC和持久层 Spring JDBCTemplate以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

Spring优势:

  • 方便解耦,简化开发。
  • AOP 编程的支持。
  • 声明式事务的支持。
  • 方便程序的测试。

2 Spring入门

①导入 Spring 开发的基本包坐标。

<!--导入Spring基本坐标-->
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.16</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

②编写 Dao 接口和实现类。

package com.liu.dao;

public interface UserDao {
    void save();
}
package com.liu.dao.impl;

import com.liu.dao.UserDao;

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("save() running");
    }
}

③创建 Spring 核心配置文件:applicationContext.xml。

④在 Spring 配置文件中配置 UserDaoImpl。

<?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">

    <bean id="userDao" class="com.liu.dao.impl.UserDaoImpl"></bean>
</beans>

⑤使用 Spring 的 API 获得 Bean 实例。

@Test
public void test1() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) app.getBean("userDao");
    System.out.println(userDao);
}

2.1 Bean标签基本配置

Bean标签用于配置对象交由Spring来创建。

默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。

基本属性:

  • id:Bean实例在Spring容器中的唯一标识。
  • class:Bean的全限定名称。
  • scope:指对象的作用范围。
  • init-method:指定类中的初始化方法名称。
  • destroy-method:指定类中销毁方法名称。

scope属性范围:

取值范围说明
singleton默认值,单例的
prototype多例的
requestWEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中
sessionWEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中
global sessionWEB 项目中,应用在 Portlet 环境,如果没有 Portlet 环境那么globalSession 相当于 session

当scope的取值为singleton

  • Bean的实例化个数:1个
  • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
  • Bean的生命周期:

    对象创建:当应用加载,创建容器时,对象就被创建了;

    对象运行:只要容器在,对象一直活着;

    对象销毁:当应用卸载,销毁容器时,对象就被销毁了

①在 Spring 配置文件中配置bean标签的scope属性为singleton。

<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl" scope="singleton"></bean>

②使用 Spring 的 API 获得 Bean 实例。

@Test
public void test1() {
    //配置scope为:singleton
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) app.getBean("userDao");
    UserDao userDao1 = (UserDao) app.getBean("userDao");
    System.out.println(userDao);
    System.out.println(userDao1);
    /**
         * com.liu.dao.impl.UserDaoImpl@564fabc8
         * com.liu.dao.impl.UserDaoImpl@564fabc8
         * */
}

③测试结果。两个对象的地址是一样的!

image-20220813190835318

当scope的取值为prototype

  • Bean的实例化个数:多个。
  • Bean的实例化时机:当调用getBean()方法时实例化Bean。
  • Bean的生命周期:

    对象创建:当使用对象时,创建新的对象实例;

    对象运行:只要对象在使用中,就一直活着;

    对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了。

①在 Spring 配置文件中配置bean标签的scope属性为prototype。

<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl" scope="prototype"></bean>

②使用 Spring 的 API 获得 Bean 实例。

@Test
public void test2() {
    //配置scope为:prototype
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) app.getBean("userDao");
    UserDao userDao1 = (UserDao) app.get
        Bean("userDao");
    System.out.println(userDao);
    System.out.println(userDao1);
    /**
* com.liu.dao.impl.UserDaoImpl@399f45b1
* com.liu.dao.impl.UserDaoImpl@38c6f217
*/
}

③测试结果。两个对象的地址不同!

image-20220813190911152

  • init-method:指定类中的初始化方法名称。
  • destroy-method:指定类中销毁方法名称。

①在 Spring 配置文件中配置bean标签的init-method、destroy-method属性为该类的初始化和销毁方法。

<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destroy"></bean>

②使用 Spring 的 API 获得 Bean 实例。

@Test
public void test3(){
    //配置init,destroy方法
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) app.getBean("userDao");
    System.out.println(userDao);
    app.close();
}

③测试结果。

image-20220813192445245

2.2 Bean实例化的三种方式

<1>使用无参构造方法实例化

它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败。

<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl"/>

<2>工厂静态方法实例化

工厂的静态方法返回Bean实例。

①创建工厂类,并创建静态方法,使用静态方法创建UserDao对象。

package com.liu.factory;

import com.liu.dao.UserDao;
import com.liu.dao.impl.UserDaoImpl;

public class StaticFactory {
    public static UserDao getUserDao() {
        return new UserDaoImpl(); //使用工厂创建UserDao
    }
}

②在 Spring 配置文件中配置工厂类对象,并配置工厂方法。

<bean id="userDao" class="com.liu.factory.StaticFactory" factory-method="getUserDao"/>

③使用 Spring 的 API 获得 Bean 实例。

@Test
public void test4(){
    //通过静态工厂获取对象
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) app.getBean("userDao");
    System.out.println(userDao);
}

④测试结果。成功创建对象。

image-20220813193508086

<3> 工厂实例方法实例化

工厂的非静态方法返回Bean实例。

①创建工厂类,并创建实例方法,使用实例方法创建UserDao对象。

package com.liu.factory;

import com.liu.dao.UserDao;
import com.liu.dao.impl.UserDaoImpl;

public class StaticFactory {
    public UserDao getUserDao() {
        return new UserDaoImpl(); //使用工厂创建UserDao
    }
}

②在 Spring 配置文件中配置工厂类对象,然后配置UserDao对象,选择工厂类的工厂方法。

<bean id="factory" class="com.liu.factory.DynamicFactory"/>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao"/>

③使用 Spring 的 API 获得 Bean 实例。

@Test
public void test5(){
    //通过实例工厂获取对象
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) app.getBean("userDao");
    System.out.println(userDao);
}

④测试结果。成功创建对象。

image-20220813194110188

2.3 Bean的依赖注入

依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。

在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

<1>依赖注入方式一:set方法

①创建UserService接口以及UserServiceImpl实现类,并生成set方法。

package com.liu.service;

public interface UserService {
    void save();
}
package com.liu.service.impl;

import com.liu.dao.UserDao;
import com.liu.service.UserService;

public class UserServiceImpl implements UserService {
    //set依赖注入
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.save();
    }
}

②在 Spring 配置文件中配置Spring容器调用set方法进行注入。

注意:ref属性代表引用类型,对于普通基本类型采用value属性。

<!--依赖注入,set方法-->
<bean id="userService" class="com.liu.service.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
</bean>

简写:添加p命名空间:

xmlns:p="http://www.springframework.org/schema/p"
<bean id="userService" class="com.liu.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>

③使用 Spring 的 API 获得 UserService 实例。

public class UserServiceTest {
    @Test
    public void test() {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) app.getBean("userService");
        userService.save();
    }
}

④测试结果:成功在UserService中依赖注入了UserDao对象。

image-20220813195623426

<2>依赖注入方式二:构造方法

①创建UserService接口以及UserServiceImpl实现类,并创建有参构造方法。

package com.liu.service.impl;

import com.liu.dao.UserDao;
import com.liu.service.UserService;

public class UserServiceImpl implements UserService {
    //构造方法 依赖注入
    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void save() {
        userDao.save();
    }
}

②在 Spring 配置文件中配置Spring容器调用有参构造方法进行注入。

<!--依赖注入:构造方法-->
<bean id="userService" class="com.liu.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"/>
</bean>

③使用 Spring 的 API 获得 UserService 实例。

@Test
public void test() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = (UserService) app.getBean("userService");
    userService.save();
}

④测试结果:成功在UserService中依赖注入了UserDao对象。

image-20220813200548549

<3>基本数据类型的依赖注入

通过set方法注入:

①创建基本数据类型,并创建set方法。

public class UserDaoImpl implements UserDao {
    private String username;
    private int age;

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public UserDaoImpl() {
        System.out.println("new  UserDaoImpl~~~");
    }
    @Override
    public void save() {
        System.out.println("Username: " + username+",age: " +age);
        System.out.println("UserDao save() running");
    }
}

②在 Spring 配置文件中配置Spring容器调用set方法进行注入。

<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl">
    <property name="username" value="liuChang"/>
    <property name="age" value="23"/>
</bean>

③使用 Spring 的 API 获得 UserService 实例。

@Test
public void test() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = (UserService) app.getBean("userService");
    userService.save();
}

④测试结果:成功在UserService中依赖注入了基本的数据类型对象。

image-20220813203228840

<4>集合类型的依赖注入

通过set方法注入:

①创建集合数据类型,并创建set方法。

private List<String> stringList;
private Map<String, User> userMap;
private Properties properties;

public void setStringList(List<String> stringList) {
    this.stringList = stringList;
}

public void setUserMap(Map<String, User> userMap) {
    this.userMap = userMap;
}

public void setProperties(Properties properties) {
    this.properties = properties;
}
@Override
public void save() {
    System.out.println(stringList);
    System.out.println(userMap);
    System.out.println(properties);
    System.out.println("UserDao save() running");
}
package com.liu.pojo;

public class User {
    private Integer id;
    private String username;
    private String password;

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

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

②在 Spring 配置文件中配置Spring容器调用set方法进行注入。

<bean id="user1" class="com.liu.pojo.User">
    <property name="id" value="1"/>
    <property name="username" value="LiuChang"/>
    <property name="password" value="131411"/>
</bean>
<bean id="user2" class="com.liu.pojo.User">
    <property name="id" value="2"/>
    <property name="username" value="See"/>
    <property name="password" value="111111"/>
</bean>

<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl">
    <property name="username" value="liuChang"/>
    <property name="age" value="23"/>
    <property name="stringList">
        <list>
            <value>aaa</value>
            <value>ccc</value>
            <value>bbb</value>
        </list>
    </property>
    <property name="userMap">
        <map>
            <entry key="user1" value-ref="user1"/>
            <entry key="user2" value-ref="user2"/>
        </map>
    </property>
    <property name="properties">
        <props>
            <prop key="p1">JDBC</prop>
            <prop key="p2">com.mysql.cj.jdbc.Driver</prop>
        </props>
    </property>
</bean>

③使用 Spring 的 API 获得 UserService 实例。

@Test
public void test() {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = (UserService) app.getBean("userService");
    userService.save();
}

④测试结果:成功在UserService中依赖注入了集合数据对象。

image-20220813204442127

3 Spring的API

ApplicationContext的实现类

1)ClassPathXmlApplicationContext

它是从类的根路径下加载配置文件 推荐使用这种

2)FileSystemXmlApplicationContext

它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

3)AnnotationConfigApplicationContext

当使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

4 Spring配置数据源

4.1 手动创建数据源

①导入数据源的坐标和数据库驱动坐标

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>
    <dependency>
        <groupId>c3p0</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.1.2</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.24</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.16</version>
    </dependency>
</dependencies>

②创建数据源对象

③设置数据源的基本连接数据

④使用数据源获取连接资源和归还连接资源

package com.liu;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;

import java.beans.PropertyVetoException;
import java.sql.Connection;

public class DataSourceTest {

    @Test
    //测试手动创建c3p0数据源
    public void test() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUser("root");
        dataSource.setPassword("131411");
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
    //手动创建druid数据源
    @Test
    public void testDruid() throws Exception {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setUsername("root");
        dataSource.setPassword("131411");
        DruidPooledConnection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
}

⑤测试数据源是否手动配置成功。

image-20220813213729547

4.2 抽取jdbc配置文件

①properties配置文件的创建。

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=131411

②读取配置文件,并创建数据源对象,获取连接。

@Test
public void test3() throws Exception {
    //读取配置文件
    ResourceBundle rb = ResourceBundle.getBundle("jdbc");
    String driver = rb.getString("jdbc.driver");
    String url = rb.getString("jdbc.url");
    String username = rb.getString("jdbc.username");
    String password = rb.getString("jdbc.password");
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass(driver);
    dataSource.setJdbcUrl(url);
    dataSource.setUser(username);
    dataSource.setPassword(password);
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

③测试是否成功。

image-20220813223049817

4.3 Spring配置数据源

①在Spring核心配置文件:Sping创建c3p0数据源。

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
    <property name="user" value="root" />
    <property name="password" value="131411"/>
</bean>

②使用 Spring 的 API 获得 DataSource 实例。

//测试使用Spring容器产生数据源
@Test
public void test4() throws Exception {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    DataSource dataSource = (DataSource) app.getBean(DataSource.class);
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

③测试是否获取连接。

image-20220813222851194

优化:Spring抽取properties文件,创建数据源

①在Spring核心配置文件:抽取properties文件,并在Sping中创建druid数据源。

<!--添加context-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">


<!--加载properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
    
</beans>

②使用 Spring 的 API 获得 DataSource 实例。

//测试使用Spring加载properties产生数据源
@Test
public void test5() throws Exception {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    DataSource dataSource = (DataSource) app.getBean("druidDataSource");
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

③测试是否获取连接。

image-20220813224504006

5 Spring注解开发

Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以简化配置,提高开发效率。

5.1 Spring原始注解开发

Spring原始注解主要是替代Bean的配置。

注解说明
@Component使用在类上用于实例化Bean
@Controller使用在web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用用于根据名称进行依赖注入
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

①使用注解标注UserDao对象。

package com.liu.dao.impl;

import com.liu.dao.UserDao;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

//<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl"/>
//@Component("userDao")
//使用Dao层注解
@Repository("userDao")
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("save Ruing");
    }
}

②使用注解标注UserService类,并使用注解进行依赖注入。

package com.liu.service.impl;

import com.liu.dao.UserDao;
import com.liu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

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

//<bean id="userService" class="com.liu.service.impl.UserServiceImpl"/>
//@Component("userService")
//使用service层注解
@Service("userService")
//bean标签的scope属性:单 双
@Scope("singleton")
public class UserServiceImpl implements UserService {
    //<property name="userDao" ref="userDao"/>
    //如果使用注解,则set方法可以不写
    //@Autowired //按照数据类型从Spring容器中进行匹配的
    //@Qualifier("userDao")  //是按照id值从容器中进行匹配的,此处需要结合@Autowired
    @Resource(name="userDao") //相当于@Autowired加@Qualifier
    private UserDao userDao;
    //<property name="id" value="1"/>
    @Value("${jdbc.driver}")
    private String driver;

    @Override
    public void save() {
        System.out.println(driver);
        userDao.save();
    }
    //<bean id="userDao" class="com.liu.dao.impl.UserDaoImpl" scope="singleton" init-method="init" destroy-method="destroy"/>
    @PostConstruct
    public void init() {
        System.out.println("UserService.init()");
    }
    @PreDestroy
    public void destroy() {
        System.out.println("UserService.destroy()");
    }
}

③注意!必须要配置扫描!

<!--告诉Spring在哪个包下的bean需要扫描-->
<context:component-scan base-package="com.liu"/>

④使用Spring API 创建UserService类。

//注解开发测试
@Test
public void test() {
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = (UserService)app.getBean("userService");
    userService.save();
    app.close();
}

⑤测试结果:成功创建。

image-20220814093341488

5.2 Spring新注解开发

使用上面的注解还不能全部替代xml配置文件,还需要使用注解替代的配置如下:

非自定义的Bean的配置:

加载properties文件的配置:<context:property-placeholder>

组件扫描的配置:<context:component-scan>

引入其他文件:

注解说明
@Configuration用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包。 作用和在 Spring 的 xml 配置文件中的 <context:component-scan base-package="com.itheima"/>一样
@Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource用于加载.properties 文件中的配置
@Import用于导入其他配置类

①使用新注解加载properties文件

package com.liu.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;

//<!--加载properties文件-->
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;


    @Bean("dataSource")
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

②使用新注解配置核心配置类。

package com.liu.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

//标志该类是Spring的核心配置类
@Configuration
//<context:component-scan base-package="com.liu"/>
@ComponentScan("com.liu")
//<import resource=""/>
@Import(DataSourceConfiguration.class)
public class SpringConfiguration {

}

③使用Spring API创建DataSouce类。

//新注解开发
@Test
public void test2() throws SQLException {
    ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    UserService userService = (UserService)app.getBean("userService");
    System.out.println(userService);
    DataSource dataSource = (DataSource) app.getBean("dataSource");
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    connection.close();
}

④进行测试。

image-20220814100555503

6 Spring集成Junit

①导入spring集成Junit的坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.16</version>
</dependency>

②使用@Runwith注解替换原来的运行期

③使用@ContextConfiguration指定配置文件或配置类

④使用@Autowired注入需要测试的对象

⑤创建测试方法进行测试

package com.liu;

import com.liu.config.SpringConfiguration;
import com.liu.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@RunWith(SpringJUnit4ClassRunner.class)
//配置配置文件
//@ContextConfiguration("classpath:applicationContext.xml")
//配置核心类
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
    @Autowired
    private UserService userService;

    @Autowired
    private DataSource dataSource;

    @Test
    public void test() {
        userService.save();
    }

    @Test
    public void test1() throws SQLException {
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
}

⑥测试结果。

image-20220814101500056

7 Sping AOP

7.1 什么是 AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

7.2 AOP 的作用及其优势

作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

7.3 AOP 的底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

7.4 AOP 的动态代理技术

常用的动态代理技术

JDK 代理 : 基于接口的动态代理技术

cglib 代理:基于父类的动态代理技术

7.5 AOP 相关概念

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

理解 AOP 的相关术语,常用的术语如下:

  • Target(目标对象):代理的目标对象。
  • Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类。
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
  • Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
  • Advice(通知/ 增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。
  • Aspect(切面):是切入点和通知(引介)的结合。
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

7.6 AOP 开发明确的事项

①需要编写的内容

  • 编写核心业务代码(目标类的目标方法)
  • 编写切面类,切面类中有通知(增强功能方法)
  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

②AOP 技术实现的内容

Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

③AOP 底层使用哪种代理方式

在 spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

7.7 基于XML的AOP开发

(1) 切点表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号* 代表任意
  • 包名与类名之间一个点 . 代表当前包下的类,两个点 .. 表示当前包及其子包下的类
  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

例如:

execution(public void com.itheima.aop.Target.method())    
execution(void com.itheima.aop.Target.*(..))
execution(* com.itheima.aop.*.*(..))
execution(* com.itheima.aop..*.*(..))
execution(* *..*.*(..))
(2) 通知的类型

通知的配置语法:

<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>

(3) AOP入门

①导入 AOP 相关坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.16</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.16</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.16</version>
</dependency>

②创建目标接口和目标类(内部有切点)

package com.liu.aop;

public interface TargetInterface {
    void save();
}
package com.liu.aop;

public class Target implements TargetInterface{
    @Override
    public void save() {
        System.out.println("save running~~~");
    }
}

③创建切面类(内部有增强方法)

package com.liu.aop;

public class MyAspect {

    public void before(){
        System.out.println("前置增强......");
    }

}

④将目标类和切面类的对象创建权交给 spring

⑤在 applicationContext.xml 中配置织入关系

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--目标对象-->
    <bean id="target" class="com.liu.aop.Target"/>

    <!--切面对象-->
    <bean id="myAspect" class="com.liu.aop.MyAspect"/>

    <!--配置织入:告诉Spring框架 哪些方法(切点)需要进行哪些增强-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--切面:切点加通知-->
            <aop:before method="before" pointcut="execution(public void com.liu.aop.Target.save())"/>
        </aop:aspect>
    </aop:config>
</beans>

⑥测试代码

package com.liu;

import com.liu.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
    private TargetInterface targetInterface;

    @Test
    public void test() {
        targetInterface.save();
    }
}

⑦测试结果

image-20220814210539317

(4) 各种通知类型

①创建切面类的各种通知方法。

package com.liu.aop;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspect {

    public void before(){
        System.out.println("前置增强......");
    }
    public void after(){
        System.out.println("后置增强.....");
    }
    //Proceeding Join Point:进行连接点 -->切点
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强。。。");
        //切点方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后增强。。。。");
        return proceed;
    }

    public void afterThrowing(){
        System.out.println("异常抛出增强....");
    }

    public void afterEnding(){
        System.out.println("最终增强....");
    }
}

②配置织入。

<!--配置织入:告诉Spring框架 哪些方法(切点)需要进行哪些增强-->
<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
        <!--切面:切点加通知-->
        <aop:before method="before" pointcut="execution(* com.liu.aop.*.*(..))"/>
        <aop:after-returning method="after" pointcut="execution(* com.liu.aop.*.*(..))"/>
        <aop:around method="around" pointcut="execution(* com.liu.aop.*.*(..))"/>
        <aop:after-throwing method="afterThrowing" pointcut="execution(* com.liu.aop.*.*(..))"/>
        <aop:after method="afterEnding" pointcut="execution(* com.liu.aop.*.*(..))"/>
    </aop:aspect>
</aop:config>

③进行测试。

image-20220814213827183

(5) 抽取切点表达式

当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性代替 pointcut 属性来引用抽取后的切点表达式。

<!--配置织入:告诉Spring框架 哪些方法(切点)需要进行哪些增强-->
<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
        <!--抽取切点表达式-->
        <aop:pointcut id="pointcut"  expression="execution(* com.liu.aop.*.*(..))"/>
        <!--切面:切点加通知-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after-returning method="after" pointcut-ref="pointcut"/>
        <aop:around method="around" pointcut-ref="pointcut"/>
        <aop:after method="afterEnding" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

image-20220814214453705

7.8 基于注解的AOP开发

通知的配置语法:@通知注解(“切点表达式")

①创建目标接口和目标类(内部有切点)

package com.liu.annoAop;

import org.springframework.stereotype.Component;

@Component("target")
public class Target implements TargetInterface {
    @Override
    public void save() {
        System.out.println("save running~~~");
    }
}

②创建切面类(内部有增强方法)

③将目标类和切面类的对象创建权交给 spring

④在切面类中使用注解配置织入关系

package com.liu.annoAop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component("myAspect")
@Aspect //标注当前类是一个切面类
public class MyAspect {
    @Before("pointcut()")//配置前置通知
    public void before(){
        System.out.println("前置增强......");
    }

    @AfterReturning("pointcut()")//配置后置通知
    public void after(){
        System.out.println("后置增强.....");
    }
    //Proceeding Join Point:进行连接点 -->切点
    @Around("pointcut()")//配置环绕通知
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前增强。。。");
        //切点方法
        Object proceed = pjp.proceed();
        System.out.println("环绕后增强。。。。");
        return proceed;
    }

    @AfterThrowing("pointcut()") //配置异常抛出通知
    public void afterThrowing(){
        System.out.println("异常抛出增强....");
    }

    @After("pointcut()")//配置最终通知
    public void afterEnding(){
        System.out.println("最终增强....");
    }

    //定义切点表达式
    @Pointcut("execution(* com.liu.annoAop.*.*(..))")
    public void pointcut(){}
}

⑤在配置文件中开启组件扫描和 AOP 的自动代理

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context.xsd">

    <!--添加组件扫描-->
    <context:component-scan base-package="com.liu.annoAop"/>
    <!--配置Aop自动代理-->
    <aop:aspectj-autoproxy/>
</beans>

⑥测试

package com.liu;

import com.liu.annoAop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class AnnoAopTest {
    @Autowired
    private TargetInterface targetInterface;

    @Test
    public void test() {
        targetInterface.save();
    }
}

image-20220814220636148

8 Spring JdbcTemplate

JdbcTemplate是spring框架中提供的一个对象,是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如:操作关系型数据的JdbcTemplate和HibernateTemplate,操作nosql数据库的RedisTemplate,操作消息队列的JmsTemplate等等。

①导入spring-jdbc和spring-tx坐标

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.16</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.3.16</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.24</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.16</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.16</version>
    </dependency>
</dependencies>

②创建实体对象

package com.liu.pojo;

public class User {
    private int id;
    private String username;
    private String password;
    private String gender;
    private String addr;

    //省略了 setter 和 getter

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }
}

③使用Spring读取properties文件,并且创建JdbcTemplate对象

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=131411
<!--加载properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<!--JdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="druidDataSource"/>
</bean>

④执行数据库操作

package com.liu.JdbcTemplate;

import com.liu.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.sql.ResultSet;
import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringJdbcTemplate {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    public void test() {
        int update = jdbcTemplate.update("insert into tb_user(username, password,gender,addr) values(?, ?, ?, ?)",
                "畅", "131411", "男", "河南");
        System.out.println(update);
    }
    @Test
    public void test1() {
        int update = jdbcTemplate.update("update tb_user set password = ? where username =?", "131411", "流");
        System.out.println(update);
    }
    @Test
    public void test2() {
        jdbcTemplate.update("delete from tb_user where username = ?","流");
    }
    //查询操作
    @Test
    public void test3() {
        List<User> userList = jdbcTemplate.query("select * from tb_user", new BeanPropertyRowMapper<User>(User.class));
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    public void test4() {
        User user = jdbcTemplate.queryForObject("select * from tb_user where username = ?", new BeanPropertyRowMapper<User>(User.class), "畅");
        System.out.println(user);
    }
    @Test
    public void test5() {
        Long count = jdbcTemplate.queryForObject("select count(*) from tb_user", Long.class);
        System.out.println(count);
    }
}

9 Spring事务控制

9.1 事务控制对象

PlatformTransactionManager

PlatformTransactionManager 接口是 spring 的事务管理器,它里面提供了我们常用的操作事务的方法。

1554628587457

注意:

PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager

Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

TransactionDefinition

TransactionDefinition 是事务的定义信息对象,里面有如下方法:

1554628676150

事务隔离级别

设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。

  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED
  • ISOLATION_READ_COMMITTED
  • ISOLATION_REPEATABLE_READ
  • ISOLATION_SERIALIZABLE

事务传播行为

  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  • NEVER:以非事务方式运行,如果当前存在事务,抛出异常
  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
  • 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
  • 是否只读:建议查询时设置为只读

TransactionStatus

TransactionStatus 接口提供的是事务具体的运行状态,方法介绍如下。

1554628857200

9.2 基于 XML 的声明式事务控制

①引入tx命名空间

②配置事务增强

③配置事务 AOP 织入

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>


    <bean id="accountDao" class="com.liu.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>
    <bean id="accountService" class="com.liu.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>
    <!--平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--事务增强配置-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="save" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
            <tx:method name="findAll" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
            <tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true"/>
            <tx:method name="*"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!--事务的aop增强-->
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* com.liu.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>
    <!--告诉Spring在哪个包下的bean需要扫描-->
    <context:component-scan base-package="com.liu"/>
</beans>

④测试事务控制转账业务代码

package com.liu;

import com.liu.service.AccountService;
import com.liu.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TransactionManagerTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void test() {
        accountService.transfer();
    }
}

⑤测试结果:

image-20220815184916721

image-20220815184926436

9.3 基于注解声明式事务控制

①使用注解标识accountDao,以及注入JdbcTemplate

package com.liu.dao.impl;

import com.liu.dao.AccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void out() {
        jdbcTemplate.update("update account set money = ? where id = ?","500",1);
    }

    @Override
    public void in() {
        jdbcTemplate.update("update account set money = ? where id = ?","1500",2);
    }
}

②使用注解标识accountService类,以及使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同 xml 配置方式,例如隔离级别、传播行为等。

package com.liu.service.impl;

import com.liu.dao.impl.AccountDaoImpl;
import com.liu.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

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

    @Autowired
    private AccountDaoImpl accountDao;


    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    @Override
    public void transfer() {
        accountDao.out();
        int i = 1/0;
        accountDao.in();
    }
}

③Xml配置文件中要开启事务的注解驱动<tx:annotation-driven />,以及包扫描。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>


    <!--平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--告诉Spring在哪个包下的bean需要扫描-->
    <context:component-scan base-package="com.liu"/>
    <!--事务的注解驱动-->
    <tx:annotation-driven/>
</beans>

④测试。

package com.liu;


import com.liu.service.AccountService;
import com.liu.service.impl.AccountServiceImpl;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TransactionManagerTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void test() {
        accountService.transfer();
    }
}

10 学习总结

在这三天中,比较系统的再次复习了Spring的使用,并且使用Git提交了自己学习Spring的每一个过程代码:总共提交了19次代码,完成了Spring的基本学习。通过这次学习,对Spring的使用更加的熟悉了,但是对于注解的一些单词还是需要多练习才能记住,以及对于Spring的一些原理性东西,现在还是一知半解,以后需要学习掌握Spring框架的原理,并且多加练习使用Spring。

image-20220815190448920


1 Mybatis基本增删改查

①添加MyBatis的坐标

<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.9</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
    </dependency>
    <!--mysql 驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.29</version>
    </dependency>
    <!-- 添加slf4j日志api -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>
    <!-- 添加logback-classic依赖 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
    </dependency>
    <!-- 添加logback-core依赖 -->
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.24</version>
    </dependency>
</dependencies>

②编写User实体类

package com.liu.pojo;

public class User {
    private int id;
    private String username;
    private String password;
    private String gender;
    private String addr;

    //省略了 setter 和 getter

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }
}

③创建UserMapper接口

package com.liu.mapper;

import com.liu.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> findAll();
    void addUser(User user);
    int updateUser(User user);
    void deleteUser(int id);
}

④编写映射文件UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.mapper.UserMapper">
    <update id="updateUser">
        update tb_user set username = #{username},password = #{password},gender = #{gender},addr = #{addr} where id = #{id}
    </update>
    
    <delete id="deleteUser">
        delete from tb_user where id = #{id}
    </delete>
    
    <select id="findAll" resultType="com.liu.pojo.User">
        select * from tb_user
    </select>
    
    <insert id="addUser" parameterType="com.liu.pojo.User">
        insert into tb_user(username,password,gender,addr) values(#{username},#{password},#{gender},#{addr})
    </insert>

</mapper>

⑤编写核心文件SqlMapConfig.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:///test?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="131411"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/liu/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

⑥编写测试类

public class MybatisTest {
    //获取sqlSessionFactory
    String resource = "sqlMapConfig.xml";
    InputStream inputStream;

    {
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //获取userMapper接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    @Test
    public void test() throws IOException {
        List<User> users = userMapper.findAll();
        for (User user : users) {
            System.out.println(user);
        }
        sqlSession.close();
    }
    @Test
    public void test1() throws IOException {
        User user = new User();
        user.setUsername("帅博");
        user.setPassword("123456");
        user.setGender("男");
        user.setAddr("河南");
        userMapper.addUser(user);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void test2(){
        User user = new User();
        user.setId(8);
        user.setUsername("帅博11");
        user.setPassword("12345611");
        user.setGender("男");
        user.setAddr("河南");
        int i = userMapper.updateUser(user);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void test3() {
        userMapper.deleteUser(8);
        //提交事务
        sqlSession.commit();
        sqlSession.close();
    }
}

2 MyBatis常用配置解析

2.1 Mybatis配置文档结构

image-20220817112138216

需要注意配置的结构:比如,在第二次复习时,使用properties标签时,由于没有写在configuration标签内部发生报错。

image-20220817112330765

正确写法:

image-20220817112356567

以及对于设置别名,也需要按照上述结构,比如,将类型别名写在映射器之下,程序将会发生ClassNotFound错误。

正确写法:

image-20220817112557055

2.2 Mybatis常用标签

①environments标签

数据库环境的配置,支持多环境配置。

详细可以进官网查看。

②mapper标签

该标签的作用是加载映射的,加载方式有如下几种:

  • 使用相对于类路径的资源引用,例如:
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  • 使用完全限定资源定位符(URL),例如:
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
  • 使用映射器接口实现类的完全限定类名,例如:
<mapper class="org.mybatis.builder.AuthorMapper"/>
  • 将包内的映射器接口实现全部注册为映射器,例如:
<package name="org.mybatis.builder"/>

③properties标签

实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件。

<properties resource="properties.properties"/>
<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="${jdbc.driver}"/>
            <property name="url" value="${jdbc.url}"/>
            <property name="username" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>

④typeAliases标签

类型别名是为Java 类型设置一个短的名字。

类型别名可为 Java 类型设置一个缩写名字。

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。推荐使用这种。

<!--取别名-->
<typeAliases>
    <package name="com.liu.pojo"/>
</typeAliases>

下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

2.3 Mybatis常用API

SqlSession工厂构建器SqlSessionFactoryBuilder

常用API:

SqlSessionFactory  build(InputStream inputStream)

通过加载mybatis的核心文件的输入流的形式构建一个SqlSessionFactory对象

String resource = "org/mybatis/builder/mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 
SqlSessionFactory factory = builder.build(inputStream);

其中, Resources 工具类,这个类在 org.apache.ibatis.io 包中。Resources 类帮助你从类路径下、文件系统或一个 web URL 中加载资源文件。

SqlSession工厂对象SqlSessionFactory

SqlSessionFactory 有多个个方法创建SqlSession 实例。常用的有如下两个:

1552653723002

SqlSession会话对象

SqlSession 实例在 MyBatis 中是非常强大的一个类。在这里你会看到所有执行语句、提交或回滚事务和获取映射器实例的方法。

执行语句的方法主要有:

<T> T selectOne(String statement, Object parameter) 
<E> List<E> selectList(String statement, Object parameter) 
int insert(String statement, Object parameter) 
int update(String statement, Object parameter) 
int delete(String statement, Object parameter)

操作事务的方法主要有:

void commit()  
void rollback() 

更为详细的Mybatis API可以查看官网:https://mybatis.net.cn/java-api.html

3 Mybatis高级配置

3.1 Mybatis动态SQL

if 标签:

  • if 标签:条件判断

    • test 属性:逻辑表达式

where 标签:

  • 作用:

    • 替换where关键字
    • 会动态的去掉第一个条件前的 and
    • 如果所有的参数没有值则不加where关键字

choose标签:

  • choose 标签类似于Java 中的switch语句。
  • when 标签类似于Java 中的case语句。

foreach标签:

<foreach>标签用于遍历集合,它的属性:

  • collection:代表要遍历的集合元素,注意编写时不要写#{}
  • open:代表语句的开始部分
  • close:代表结束部分
  • item:代表遍历集合的每个元素,生成的变量名
  • sperator:代表分隔符

①在UserDao中写查询方法

//动态SQL
List<User> findBySome(User user);

List<User> findByIds(int[] ids);

②使用标签编写sql语句

<!--Sql语句抽取-->
    <sql id="selectUser">select *from tb_user</sql>
<!--动态SQL-->
<select id="findBySome" parameterType="user" resultType="User">
    <include refid="selectUser"/>
    <where>
        <if test="id!=null">
           and id = #{id}
        </if>
        <if test="username!=null">
           and username = #{username}
        </if>
        <if test="password!=null">
           and password = #{password}
        </if>
        <if test="gender!=null">
           and gender = #{gender}
        </if>
        <if test="addr!=null">
           and addr = #{addr}
       </if>
    </where>
</select>
<select id="findByIds" resultType="user">
    <include refid="selectUser"/>
    <where>
        <foreach collection="array" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>

③进行测试

@Test
public void test4(){
    User user = new User();
    user.setId(9);
    user.setUsername("帅博");
    //user.setPassword("123456");
    user.setGender("男");
    List<User> users = userMapper.findBySome(user);
    for (User user1 : users) {
        System.out.println(user1);
    }
}
@Test
public void test5(){
    int[] ids = {1,2,3,9};
    List<User> userList = userMapper.findByIds(ids);
    for (User user : userList) {
        System.out.println(user);
    }
}

3.2 实现分页查询

MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。

①导入通用PageHelper的坐标

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.1.3</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>1.0</version>
</dependency>

②在mybatis核心配置文件中配置PageHelper插件

<plugins>
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>

③测试分页数据获取

@Test
public void test6(){
    //设置分页相关参数
    PageHelper.startPage(1,3);
    List<User> userList = userMapper.findAll();
    for (User user : userList) {
        System.out.println(user);
    }
    //获得与分页相关参数
    PageInfo<User> pageInfo = new PageInfo<>(userList);
    System.out.println("当前页"+pageInfo.getPageNum());
    System.out.println("每页显示条数"+pageInfo.getPageSize());
    System.out.println("总页数"+pageInfo.getPages());
    System.out.println("是否第一页"+pageInfo.isIsFirstPage());
    System.out.println("是否最后一页"+pageInfo.isIsLastPage());
}

④测试结果

image-20220817155823231

发现报错问题,经过查询发现,是pagehelper插件版本与mybatis版本相差过大。解决办法:选择低版本的pagehelper。

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.2.1</version>
</dependency>

image-20220817155933669

3.3 Mybatis多表查询

MyBatis多表配置方式:

一对一配置:使用<resultMap>做配置

一对多配置:使用<resultMap>+<collection>做配置

多对多配置:使用<resultMap>+<collection>做配置

①实体类:User、Order、Role

public class User {
    private Integer id;
    private String username;
    private String password;
    private Integer orderId;
    //当前用户存在哪些设备
    private List<Order> orderList;

    public List<Order> getOrderList() {
        return orderList;
    }

    public void setOrderList(List<Order> orderList) {
        this.orderList = orderList;
    }

    //描述当前用户属于什么角色
    private List<Role> roles;

    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public User() {
    }

    public User(Integer id, String username, String password, Integer orderId) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.orderId = orderId;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getOrderId() {
        return orderId;
    }

    public void setOrderId(Integer orderId) {
        this.orderId = orderId;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", roles=" + roles +
                '}';
    }
}
package com.liu.pojo;

public class Order {
    private Integer orderId;
    private String ordername;
    private User user;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Order() {
    }

    public Order(Integer orderId, String ordername) {
        this.orderId = orderId;
        this.ordername = ordername;
    }

    public Integer getOrderId() {
        return orderId;
    }

    public void setOrderId(Integer orderId) {
        this.orderId = orderId;
    }

    public String getOrdername() {
        return ordername;
    }

    public void setOrdername(String ordername) {
        this.ordername = ordername;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderId=" + orderId +
                ", ordername='" + ordername + '\'' +
                ", user=" + user +
                '}';
    }
}
public class Role {
    private Integer id;
    private String rolename;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }

    @Override
    public String toString() {
        return "Role{" +
                "id=" + id +
                ", rolename='" + rolename + '\'' +
                '}';
    }
}

②Mapper接口:

public interface OrderMapper {
    List<Order> findAll();
    //一对一
    List<Order> findByUser();
}
public interface UserMapper {
    List<User> findAll();
    //一对多
    List<User> findByOrder();
    //多对多
    List<User> findByRole();
}

③UserMapper.xml、OrderMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.mapper.UserMapper">

    <select id="findAll" resultType="user">
        select * from user
    </select>
    <resultMap id="userMap" type="user">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="oid" property="orderId"/>
        <!--配置集合-->
        <collection property="orderList" ofType="order">
            <id column="oid" property="orderId"/>
            <result column="ordername" property="ordername"/>

        </collection>
    </resultMap>

    <select id="findByOrder" resultMap="userMap">
        select *,o.orderId oid
        from `order` o,`user` u
        where o.orderId = u.orderId
    </select>

    <resultMap id="userRoleMap" type="user">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="orderId" property="orderId"/>
        <collection property="roles" ofType="role">
            <id column="roleId" property="id"/>
            <result column="rolename" property="rolename"/>
        </collection>
    </resultMap>

    <select id="findByRole" resultMap="userRoleMap">
        select * from user u,role r ,user_role ur
        where u.id=ur.userId and r.id=ur.roleId
    </select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.mapper.OrderMapper">

    <select id="findAll" resultType="order">
        select * from `order`
    </select>
    <resultMap id="orderMap" type="order">
        <id column="oid" property="orderId"/>
        <result column="ordername" property="ordername"/>
        <!--<result column="username" property="user.username"/>
        <result column="password" property="user.password"/>
        <result column="uid" property="user.id"/>
        <result column="oid" property="user.orderId"/>-->
        <association property="user" javaType="user">
            <id column="uid" property="id"/>
            <result column="username" property="username"/>
            <result column="password" property="password"/>
            <result column="oid" property="orderId"/>
        </association>

    </resultMap>

    <select id="findByUser" resultMap="orderMap">
        select o.orderId oid,u.username,u.id uid,u.`password`,o.ordername
        from `order` o,user u
        where o.orderId = u.id
    </select>

</mapper>

④测试:

//一对一查询
@Test
public void test1() {
    List<Order> orders = orderMapper.findByUser();
    for (Order order : orders) {
        System.out.println(order);
    }
}
//一对多查询
@Test
public void test2() {
    List<User> userList = userMapper.findByOrder();
    for (User user : userList) {
        System.out.println(user);
    }
}
//多对多查询
@Test
public void test3() {
    List<User> userList = userMapper.findByRole();
    for (User user : userList) {
        System.out.println(user);
    }
}

4 Mybatis注解开发

4.1 简单查询

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

①设置mapper加载映射

<mappers>
    <package name="com.liu.mapper"/>
</mappers>

②编写注解SQL

public interface UserMapper {
    @Select("select * from tb_user")
    List<User> findAll();
    @Insert("insert into tb_user(username, password, gender, addr) values (#{username},#{password},#{gender},#{addr})")
    void insert(User user);
    @Update("update tb_user set username = #{username} where id=#{id}")
    int update(@Param("username") String username, @Param("id") int id);
    @Delete("delete from tb_user where id=#{id}")
    void delete(int id);
}

③进行测试

@Test
public void test() {
    List<User> userList = userMapper.findAll();
    for (User user : userList) {
        System.out.println(user);
    }
}

@Test
public void Test2(){
    User user = new User();
    user.setUsername("刘畅");
    user.setPassword("1314");
    user.setGender("男");
    user.setAddr("河南");
    userMapper.insert(user);
    sqlSession.commit();
}
@Test
public void Test3(){
    int update = userMapper.update("六珍惜", 1);
    sqlSession.commit();
}
@Test
public void Test4(){
    userMapper.delete(5);
    sqlSession.commit();
}

4.2 复杂映射开发

@Result:实现结果集封装

@Results:可以与@Result 一起使用,封装多个结果集

@One:实现一对一结果集封装

@Many:实现一对多结果集封装

实现复杂关系映射之前我们可以在映射文件中通过配置来实现,使用注解开发后,我们可以使用@Results注解,@Result注解,@One注解,@Many注解组合完成复杂关系的配置

①编写复杂注释

public interface OrderMapper {
    //一对一
    @Select("select o.orderId oid,u.username,u.id uid,u.`password`,o.ordername\n" +
            "        from `order` o,user u\n" +
            "        where o.orderId = u.id")
    @Results({
            @Result(column="oid",property="orderId"),
            @Result(column="ordername",property="ordername"),
            @Result(column="username",property="user.username"),
            @Result(column="password",property="user.password"),
            @Result(column="uid",property="user.id"),
            @Result(column="oid",property="user.orderId")
    }
    )
    List<Order> findByUser();
    //一对一常用写法
    @Select(("select *from `order`"))
    @Results({
            @Result(column="orderId",property="orderId"),
            @Result(column="ordername",property="ordername"),
            @Result(
                    property = "user", //要封装的属性名称
                    column = "orderId",//根据哪个字段取查询user表
                    javaType = User.class, //要封装的实体类型
                    one = @One(select = "com.liu.mapper.UserMapper.findById")
            )
    })
    List<Order> findByUser2();

    //用来进行一对多查询
    @Select("select * from `order` where orderId = #{orderId}")
    List<Order> findById(int orderId);
}
public interface UserMapper {

    //一对多
    @Select("select * from user")
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column="username",property="username"),
            @Result(column="password",property="password"),
            @Result(column="orderId",property="orderId"),
            @Result(
                    property = "orderList",
                    column = "orderId",
                    javaType = List.class,
                    many = @Many(select = "com.liu.mapper.OrderMapper.findById")

            )
    })
    List<User> findByOrder();

    //多对多
    @Select("select *from user")
    @Results({
            @Result(column = "id",property = "id"),
            @Result(column="username",property="username"),
            @Result(column="password",property="password"),
            @Result(column="orderId",property="orderId"),
            @Result(
                    property = "roles",
                    column = "orderId",
                    javaType = List.class,
                    many = @Many(select = "com.liu.mapper.RoleMapper.findByUid")
            )
    })
    List<User> findByRole();
    //用来进行一对一查询
    @Select("select * from user where id = #{id}")
    User findById(int id);
}
public interface RoleMapper {
    //用来多对多查询
    @Select("select *from user_role,role where user_role.roleId = role.id and user_role.userId = #{id}")
    List<Role> findByUid(int id);
}

②进行测试

@Test
public void test() {
    List<Order> orderList = orderMapper.findByUser();
    for (Order order : orderList) {
        System.out.println(order);
    }
}
@Test
public void test1() {
    List<Order> orderList = orderMapper.findByUser2();
    for (Order order : orderList) {
        System.out.println(order);
    }
}
@Test
public void test2() {
    List<User> userList = userMapper.findByOrder();
    for (User user : userList) {
        System.out.println(user);
    }
}

@Test
public void test3(){
    List<User> userList = userMapper.findByRole();
    for (User user : userList) {
        System.out.println(user);
    }
}

5 Spring集成web环境

1.1 ApplicationContext应用上下文获取方式

应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从容器中获得Bean时都要编写new ClasspathXmlApplicationContext(spring配置文件) ,这样的弊端是配置文件加载多次,应用上下文对象创建多次。

在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

1.2 Spring提供获取应用上下文的工具

上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。

所以我们需要做的只有两件事:

① 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.16</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!--全局初始化参数-->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!--配置监听器-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

② 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

@WebServlet("/UserServlet")
public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        WebApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        UserService userService = app.getBean(UserService.class);
        userService.save();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

③运行结果

image-20220821095258254

6 Spring MVC

2.1 SpringMVC概述

SpringMVC 是一种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级 Web 框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,从而在使用 Spring 进行 WEB 开发时,可以选择使用 Spring的 Spring MVC 框架或集成其他 MVC 开发框架,如 Struts1(现在一般不用),Struts2 等。

SpringMVC 已经成为目前最主流的 MVC 框架之一,并且随着 Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。

它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful 编程风格的请求。

2.2 SpingMVC的主要改变

SpringMVC的执行流程是:当客户端发起一个请求的时候,由tomcat接收到了客户端的请求封装代表请求的request对象以及代表响应的response对象。然后向web应用调用请求资源,web的组件servlet用来接收请求资源。servlet中存在着共有行为(接收数据,封装实体,指派视图)以及特有行为(表单校验、cookie)。共有行为是一样的,可以进行抽取出来。而SpringMVC就是把Servlet的共有行为和特有行为分隔开,当请求到来时,先找Servlet的共有行为后再找Servlet的特有行为。而此时,servlet的特有行为可以为一个pojo,即一个javaBean。springMVC把servlet的共有行为充当前端控制器。

20201010171524

2.3 SpringMVC的快速入门

需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。

开发步骤:

①导入SpringMVC相关坐标

在pom.xml中配置SpringMVC的坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.16</version>
</dependency>

②配置SpringMVC核心控制器DispathcerServlet

在web-INF下配置web.xml中配置SpringMVC核心控制器DispathcerServlet

<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

③创建Controller类和视图页面

在controller层创建Controller类:

package com.liu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

public class UserController {
    public String save(){
        System.out.println("UserController save running....");
        return "success.jsp";
    }
}

在webapp文件夹中创建一个success.jsp文件为视图页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>Success成功~~~</h1>
</body>
</html>

④使用注解配置Controller类中业务方法的映射地址

在Controller类上写上Spring的@Controller注解,把该类配置到Spring容器之中。在业务方法上配置@RequestMapping用来建立请求URL和处理请求方法的之间的联系

package com.liu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class UserController {
    @RequestMapping("/quick")//用来建立请求URL和处理请求方法的之间的联系
    public String save(){
        System.out.println("UserController save running....");
        return "success.jsp";
    }
}

⑤配置SpringMVC核心文件 spring-mvc.xml

配置spring-mvc.xml主要配置组件扫描。扫描到注解!

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--组件扫描-->
    <context:component-scan base-package="com.liu.controller"/>
</beans>

⑥客户端发起请求测试

配置tomcat:

image-20220821112044693

运行访问/quick:

image-20220821112009673

2.4 SpringMVC流程图示

20201010202125

2.5 SpringMVC的组件

1. 前端控制器:DispatcherServlet

用户请求到达前端控制器,它就相当于 MVC 模式中的 C,DispatcherServlet 是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet 的存在降低了组件之间的耦合性。

2. 处理器映射器:HandlerMapping

HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3. 处理器适配器:HandlerAdapter

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4. 处理器:Handler

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由Handler 对具体的用户请求进行处理。

5. 视图解析器:View Resolver

View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名,即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

6. 视图:View

SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。最常用的视图就是 jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面

2.6 SpringMVC的注解开发

@RequestMapping

作用:用于建立请求 URL 和处理请求方法之间的对应关系

位置:

  • 类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
  • 方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/quick")//用来建立请求URL和处理请求方法的之间的联系
    public String save(){}

属性:

  • value:用于指定请求的URL。它和path属性的作用是一样的
  • method:用于指定请求的方式
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样

例如:

  • params = {"accountName"},表示请求参数必须有accountName
  • params = {"moeny!100"},表示请求参数中money不能是100

1.mvc命名空间引入

命名空间:xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
约束地址:http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc.xsd

2.组件扫描

SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用<context:component-scan base-package=“com.itheima.controller"/>进行组件扫描。

2.7 Spring MVC的XML配置解析

视图解析器

SpringMVC有默认组件配置,默认组件都是DispatcherServlet.properties配置文件中配置的,该配置文件地址org/springframework/web/servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

REDIRECT_URL_PREFIX = "redirect:" --重定向前缀
FORWARD_URL_PREFIX = "forward:" --转发前缀(默认值)
prefix = ""; --视图名称前缀
suffix = ""; --视图名称后缀

我们可以通过属性注入的方式修改视图的的前后缀

<!--配置内部资源视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- /jsp/success.jsp -->
    <property name="prefix" value="/jsp/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

2.8 SpringMVC的执行流程

20201010203236

①用户发送请求至前端控制器DispatcherServlet。

②DispatcherServlet收到请求调用HandlerMapping处理器映射器。

③处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

④DispatcherServlet调用HandlerAdapter处理器适配器。

⑤HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

⑥Controller执行完成返回ModelAndView。

⑦HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

⑧DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

⑨ViewReslover解析后返回具体View。

⑩DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

2.9 SpringMVC的数据响应

①页面跳转

(1)返回字符串形式

直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。

@RequestMapping("/quick")//用来建立请求URL和处理请求方法的之间的联系
public String save(){
    System.out.println("UserController save running....");
    return "success";
}

返回带有前缀的字符串:
转发:forward:/WEB-INF/views/index.jsp
重定向:redirect:/index.jsp(因为webinfo文件夹[即WEB-INF]是受保护的,所以要把重定向的jsp放到可以直接访问的地方)

(2)返回ModelAndView对象

@RequestMapping("/quick2")
public ModelAndView save2(){
    /*
    * Model:模型 作用封装数据
    View:视图 作用展示数据*/
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("user","liuChang");
    //设置视图
    modelAndView.setViewName("success");
    return modelAndView;
}

通过Spring框架为你自动注入ModelAndView类:

//MVC框架调用时通过注入给你创造实参
@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView){
    modelAndView.setViewName("success");
    return modelAndView;
}

(3)存储数据

在进行转发时,往往要向request域中存储数据,在jsp页面中显示,那么Controller中怎样向request域中存储数据呢?

1.通过SpringMVC框架注入的request对象setAttribute()方法设置

@RequestMapping("/quick5")
public String save5(HttpServletRequest request){
    request.setAttribute("user","通过MVC框架注入使用request对象");
    return "success";
}

2.通过ModelAndView的addObject()方法设置

//MVC框架调用时通过注入给你创造实参
@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView){
    modelAndView.addObject("user","你好");
    modelAndView.setViewName("success");
    return modelAndView;
}

3.使用Model类对象来存储数据

@RequestMapping("/quick4")
public String save4(Model model){
    model.addAttribute("user","Model添加数据");
    return "success";
}

②回写数据

(1)直接返回字符串

Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print(“hello world”) 即可,那么在Controller中想直接回写字符串该怎样呢?

① 通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为void。

//通过HttpServletResponse回写数据
@RequestMapping("/quick6")
public void save6(HttpServletResponse response) throws IOException {
    response.setCharacterEncoding("UTF-8");
    response.getWriter().println("通过HttpServletResponse回写数据");
}

② 将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。

@RequestMapping("/quick7")
@ResponseBody
public String save7() {
   return "通过ResponseBody注解,告知SpringMvc框架,该方法不进行页面跳转,直接回写";
}

③在异步项目中,客户端与服务器端往往要进行json格式字符串交互,此时我们可以手动拼接json字符串返回。

1.导入jackson坐标。

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.13.1</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.13.1</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.13.1</version>
</dependency>

2.通过jackson转换json格式字符串,回写字符串。

@RequestMapping("/quick8")
@ResponseBody
public String save8() throws JsonProcessingException {
    User user = new User();
    user.setName("John");
    user.setEmail("john.doe@example.com");
    user.setPassword("123456");
    user.setId(1);
    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(user);
    return json;
}

image-20220821224242406

(2)返回对象或集合

通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:

<!--配置处理映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
        </list>
    </property>
</bean>
@RequestMapping("/quick9")
@ResponseBody
public User save9() throws JsonProcessingException {
    User user = new User();
    user.setName("Mac");
    user.setEmail("MAC.doe@example.com");
    user.setPassword("123456");
    user.setId(1);
    return user;
}

image-20220821225137205

在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用mvc的注解驱动代替上述配置。

xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context  https://www.springframework.org/schema/context/spring-context.xsd
">

<!--mvc注解驱动-->
    <mvc:annotation-driven/>

在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使用mvc:annotation-driven自动加载 RequestMappingHandlerMapping(处理映射器)和
RequestMappingHandlerAdapter( 处 理 适 配 器 ),可用在Spring-xml.xml配置文件中使用mvc:annotation-driven替代注解处理器和适配器的配置。同时使用mvc:annotation-driven默认底层就会集成jackson进行对象或集合的json格式字符串的转换。

2.10 SpringMVC 获得请求数据

(1)获得基本类型参数

Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。

@RequestMapping("/quick10")
@ResponseBody
public void save10(String username,int id){
    System.out.println(username);
    System.out.println(id);
}

image-20220821231315088

(2)获得POJO类型参数

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。

@RequestMapping("/quick11")
@ResponseBody
public void save11(User user){
    System.out.println(user);
}

image-20220821232339724

(3)获得数组类型参数

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。

@RequestMapping("/quick12")
@ResponseBody
public void save12(String[] strings){
    System.out.println(Arrays.asList(strings));
}

image-20220822094340417

(4)获得集合类型参数

获得集合参数时,要将集合参数包装到一个POJO中才可以。

public class Vo {
    private List<User> userList;

    public Vo() {
    }

    public Vo(List<User> userList) {
        this.userList = userList;
    }

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }

    @Override
    public String toString() {
        return "Vo{" +
                "userList=" + userList +
                '}';
    }
}
@RequestMapping("/quick13")
@ResponseBody
public void save13(Vo vo){
    System.out.println(vo);
}
<form action="${pageContext.request.contextPath}/user/quick13" method="post">
    <%--表明是第几个User对象的username age--%>
    <input type="text" name ="userList[0].name"><br/>
    <input type="text" name ="userList[0].id"><br/>
    <input type="text" name ="userList[1].name"><br/>
    <input type="text" name ="userList[1].id"><br/>
    <input type="submit" value="提交">
</form>

image-20220822100129153

image-20220822100158720

(5)解决全局乱码

在webapp下的WEB-INF的web.xml文件中配置全局的filter:

<!--配置全局过滤的filter-->
<!--为了解决中文乱码的问题-->
<filter>
    <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(6)参数绑定注解@requestParam

当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。

注解@RequestParam还有如下参数可以使用:
● value:与请求参数名称
● required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
● defaultValue:当没有指定请求参数时,则使用指定的默认值赋值

@RequestMapping("/quick14")
@ResponseBody
//@RequestParam注解是进行参数绑定
public void save14(@RequestParam(value = "user") String username){
    System.out.println(username);
}

image-20220822200222402

(7)获得Restful风格的参数

Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。

Restful风格的请求是使用“url+请求方式”表示一次请求目的的,HTTP 协议里面四个表示操作方式的动词如下:

● GET:用于获取资源
● POST:用于新建资源
● PUT:用于更新资源
● DELETE:用于删除资源

例如:
● /user/1 GET : 得到 id = 1 的 user
● /user/1 DELETE: 删除 id = 1 的 user
● /user/1 PUT: 更新 id = 1 的 user
● /user POST: 新增 user

上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。

@RequestMapping("/quick15/{name}")
@ResponseBody
public void save15(@PathVariable(value = "name") String username){
    System.out.println(username);
}

image-20220822200125137

(8)自定义类型转换器

  • SpringMVC 默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
  • 但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。

自定义类型转换器的开发步骤:

① 定义转换器类实现Converter接口

public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        //将日期字符串转日期对象
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = dateFormat.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

② 在配置文件中声明转换器

<!--mvc注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>

③ 在<annotation-driven>中引用转换器

<!--声明转换器-->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.liu.Converter.DateConverter"/>
        </list>
    </property>
</bean>

④获得请求数据

@RequestMapping("/quick16")
@ResponseBody
public void save16(@RequestParam(value = "date") Date date){
    System.out.println(date);
}

image-20220822202028589

(9)获得Servlet相关API

SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
@RequestMapping("/quick17")
@ResponseBody
public void save17(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    System.out.println(request);
    System.out.println(response);
    System.out.println(session);
}

image-20220822202359189

(10)获得请求头

使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:

  • value:请求头的名称
  • required:是否必须携带此请求头
@RequestMapping(value = "/quick18")
@ResponseBody
public void save18(@RequestHeader(value = "User-Agent",required = false) String user_agent) {
    System.out.println(user_agent);
}

image-20220822203101062

使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:

  • value:指定cookie的名称
  • required:是否必须携带此cookie
@RequestMapping(value = "/quick19")
@ResponseBody
public void save19(@CookieValue(value = "JSESSIONID",required = false) String jsessionId) {
    System.out.println(jsessionId);
}

image-20220822203134709

(11)文件上传

文件上传客户端三要素

  • 表单项type=“file”
  • 表单的提交方式是post
  • 表单的enctype属性是多部分表单形式,及enctype=“multipart/form-data”

单文件上传步骤:

① 导入fileupload和io坐标

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>

② 配置文件上传解析器

<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--上传文件的编码类型-->
    <property name="defaultEncoding" value="UTF-8"/>
    <!--上传文件的总大小-->
    <property name="maxUploadSize" value="5242800"/>
    <!--上传单个文件的大小-->
    <property name="maxUploadSizePerFile" value="5242800"/>
</bean>

③ 编写文件上传代码

@RequestMapping("/quick20")
@ResponseBody
public void save20(String username, MultipartFile uploadFile) throws IOException {
    System.out.println(username);
    //获得上传文件的名称
    String originalFilename = uploadFile.getOriginalFilename();
    uploadFile.transferTo(new File("D:\\Java\\java练习项目\\Spring" + originalFilename));
}

多文件上传实现:

<form action="${pageContext.request.contextPath}/user/quick23" method="post" enctype="multipart/form-data">
    名称:<input type="text" name="username"><br/>
    文件1:<input type="file" name="uploadFile"><br/>
    文件2:<input type="file" name="uploadFile"><br/>
    文件3:<input type="file" name="uploadFile"><br/>
    <input type="submit" value="提交">
</form>
@RequestMapping(value = "/quick21")
@ResponseBody
public void save21(String username, MultipartFile[] uploadFile) throws IOException {
    System.out.println(username);
    for (MultipartFile multipartFile : uploadFile) {
        String originalFilename = multipartFile.getOriginalFilename();
        multipartFile.transferTo(new File("D:\\Java\\java练习项目\\Spring" + originalFilename));
    }
}

2.11 学习Spring MVC遇到的问题

①JSP页面取不到ModelAndView中的值

当学习SpringMVC的请求和响应时遇到:SpringMVC在JSP页面取不到ModelAndView中的值的问题

image-20220821221252925

原因:创建maven工程时,idea自动添加的web.xml中的jsp页面头约束版本太低。

解决方法:把这个约束版本换成下面那个约束版本即可。

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
</web-app>

image-20220821221312342

7 SSM整合

3.1 导入相关坐标

<dependencies>
  <!--spring相关-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
  </dependency>

  <!--servlet和jsp-->
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
  </dependency>
  <dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.0</version>
  </dependency>

  <!--mybatis相关-->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
  </dependency>
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.1</version>
  </dependency>
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.29</version>
  </dependency>
  <dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
  </dependency>

  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
  </dependency>
  <dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
  </dependency>

</dependencies>

3.2 实现mybatis

①编写properties文件:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=131411

②编写mybatis核心配置文件:sqlMapConfig.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>
    <properties resource="properties.properties"/>
    <!--取别名-->
    <typeAliases>
        <package name="com.liu.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.liu.mapper"/>
    </mappers>
</configuration>

③编写实体类与mapper接口方法

package com.liu.pojo;

public class User {
    private int id;
    private String username;
    private String password;
    private String gender;
    private String addr;

    //省略了 setter 和 getter

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", addr='" + addr + '\'' +
                '}';
    }
}
public interface UserMapper {

    @Insert("insert into tb_user(username, password, gender, addr) values (#{username},#{password},#{gender},#{addr})")
    void save(User user);

    @Select("select *from tb_user")
    List<User> findAll();

}

直接采用注解的方式,不需要再配置mapper映射文件。

④测试Dao层是否成功。

public class UserTest {
    //获取sqlSessionFactory
    String resource = "sqlMapConfig.xml";
    InputStream inputStream = null;

    {
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    //获取sqlSession
    SqlSession sqlSession = sqlSessionFactory.openSession();

    //获取userMapper接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    @Test
    public void test() {
        List<User> userList = userMapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    public void Test2(){
        User user = new User();
        user.setUsername("辰乐");
        user.setPassword("1314");
        user.setGender("男");
        user.setAddr("河南");
        userMapper.save(user);
        sqlSession.commit();
    }
}

3.3 Spring配置

①核心文件的配置

Spring核心配置文件:applicationContext.xml

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <!--告诉Spring在哪个包下的bean需要扫描-->
    <context:component-scan base-package="com.liu">
        <!--排除controller扫描-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>

Sping-MVC核心文件:spring-mvc.xml

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

    <!--主要扫描controller-->
    <context:component-scan base-package="com.liu.controller"/>

    <!--配置mvc注解驱动-->
    <mvc:annotation-driven/>

    <!--内部资源视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- /jsp/success.jsp -->
        <property name="prefix" value="/WEB-INF/page/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--开发静态资源访问权限-->
    <mvc:default-servlet-handler/>
</beans>

WEB配置文件:web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

  <!--Spring 监听器-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--springmvc的前端控制器-->
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!--配置全局过滤的filter-->
  <!--为了解决中文乱码的问题-->
  <filter>
    <filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

②Mapper层

public interface UserMapper {

    @Insert("insert into tb_user(username, password, gender, addr) values (#{username},#{password},#{gender},#{addr})")
    void save(User user);

    @Select("select *from tb_user")
    List<User> findAll();

}

③Service层

public interface UserService {
    void save(User user);

    List<User> findAll();
}
@Service("userService")
public class UserServiceImpl implements UserService {

    @Override
    public void save(User user) {
        UserMapper userMapper = UserMapperUtils.getUserMapper();
        userMapper.save(user);
    }

    @Override
    public List<User> findAll() {
        UserMapper userMapper = UserMapperUtils.getUserMapper();
        return userMapper.findAll();
    }
}

④Controller层

@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/save",produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String save(User user){
        userService.save(user);
        return "保存成功";
    }

    @RequestMapping("/findAll")
    public ModelAndView findAll(ModelAndView modelAndView){
        List<User> userList = userService.findAll();
        modelAndView.addObject("userList",userList);
        modelAndView.setViewName("userList");
        return modelAndView;
    }
}

⑤jsp文件

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>添加账户信息表单</h1>
    <form name="userForm" action="${pageContext.request.contextPath}/user/save" method="post">
       用户名称: <input type="text" name="username" value="${username}">
       用户密码: <input type="text" name="password" value="${password}">
       用户性别: <input type="text" name="gender" value="${gender}">
       用户地址: <input type="text" name="addr" value="${addr}">
        <input type="submit" value="Save"/>
 </form>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>展示用户列表</h1>
<table border="1">
    <tr>
        <th>用户id</th>
        <th>用户名称</th>
        <th>用户密码</th>
        <th>用户性别</th>
        <th>用户地址</th>
    </tr>
    <c:forEach items="${userList}" var="user">
        <tr>
            <td>${user.id}</td>
            <td>${user.username}</td>
            <td>${user.password}</td>
            <td>${user.gender}</td>
            <td>${user.addr}</td>
        </tr>
    </c:forEach>
</table>

</body>
</html>

3.4 运行测试

8 Mybatis二次复习遇到的问题

问题一:发现问题:mybatis的映射文件中的mapper空间名不能自定义名字,自定义后爆红。

image-20220817094400431

第一次编辑的时候没出现任何问题,第二次复习的时候报错,疯狂爆红。查询各种原因,最后发现是在后面的学习中下载了MybatisX的插件,导致映射文件只能进行傻瓜式操作,自定义不了了。

解决办法:

配置全包路径。

image-20220817094945626

问题二:查询order表时,显示sql错误。

image-20220817190210646

经过查阅,了解到order是mysql的关键字,需要加:

image-20220817190404938

问题三:练习多表查询时,代码爆红。

image-20220817203558161

尝试将mybatis-x插件关闭,成功不爆红,代码运行正常,结果正确。

image-20220817204023680

问题四:依旧是Mybatis-X插件问题...

当使用association标签添加user属性时,问题三成功解决,但是,在填写user对象的最后一个属性时又爆红了...

当我用ctrl键,发现所该标签加载的property属性是加载的本包下的User类,而javaType属性加载的是其它包下的User类?

当我尝试关闭Mybatis-X插件时,发现又不爆红了...

运行测试:成功...

image-20220817210855842

为什么开启Mybatis-X插件开发时老是爆红呢?当关闭之后,一切正常,而且当代码运行成功后,再次开启mybatis-x插件又不报错了?很奇怪~~通过查询后发现,有很多人学习时也遇到了这样的问题,当启用Mybatis-X插件开发时报错。当关闭之后,开发便不再报错。是不是开发没有遵守该插件的规范呢?

学习总结

由于本科阶段有Java学习基础,于是该周进度快了一些。本周主要学习了Git、Spring框架、Mybatis框架,在学习期间也遇到了两个小问题,比如:使用Mybatis-X插件爆红、Spring的课程缺少了关于SpringMVC的课程。问题一,已经通过查询解决。问题二,当我在学习mybatis时,最后的课程是SSM整合,但是我突然发现关于SpringMVC的课程在百度网盘中并没有。不过已经通过哔哩哔哩查找到了相关的SpringMVC课程:https://www.bilibili.com/video/BV1WZ4y1P7Bp?p=37&vd_source=105d7c6f614459406c1e6948579b6dc1

0

评论

博主关闭了所有页面的评论