Spring

一、Spring的IoC和DI

1、Spring简介

1. 什么是Spring

  • Spring是分层的 Java SE/EE应用 full-stack(各层都有对应的解决方案) 轻量级开源框架,以 IOC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核。
  • 提供了展现层 SpringMVC持久层 Spring JDBCTemplate 以及 业务层事务管理 等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。

2. Spring的优势

  • 方便解耦,简化开发
    • 通过 Spring 提供的 IoC容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度耦合。
    • 用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • AOP 编程的支持
    • 通过 Spring的 AOP 功能,方便进行面向切面编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松实现。
  • 声明式事务的支持
    • 可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务管理,提高开发效率和质量。
  • 方便程序的测试
    • 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
  • 方便集成各种优秀框架
    • Spring对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的支持。
  • 降低 JavaEE API 的使用难度
    • Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
  • Java 源码是经典学习范例
    • Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。
  • 它的源代码无意是 Java 技术的最佳实践的范例。

3. Spring的体系结构

在这里插入图片描述

2、Spring程序开发步骤

1. 引入项目依赖

<!--导入spring的context坐标,context依赖core、beans、expression-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>

2. 编写接口和实现类

  • 接口
package erer.spring.introduction.dao;
public interface UserDao {
    void save();
}
  • 实现类
package erer.spring.introduction.dao.impl;
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDao save running...");
    }
}

3. 添加配置信息

resources 类路径下创建 applicationContext.xml 配置文件,并 配置 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="erer.spring.introduction.dao.impl.UserDaoImpl"/>
</beans>

4. 使用API 获得 Bean 实例

public class _使用API获得Bean实例{
	@Test
    public void test() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = app.getBean("userDao", UserDao.class);
        userDao.save();		//UserDao save running...
        app.close();
    }
}

3、Spring配置文件

1. 基本配置

  • 用于配置对象交由Spring 来创建。

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

  • 基本属性:

    • id:Bean实例在Spring容器中的唯一标识
    • class:Bean的全限定名称

2. 范围配置

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

(1)当scope的取值为singleton

  • Bean的实例化个数:1个
  • Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
  • Bean的生命周期:
    1. 对象创建:当应用加载,创建容器时,对象就被创建了
    2. 对象运行:只要容器在,对象一直活着
    3. 对象销毁:当应用卸载,销毁容器时,对象就被销毁了

(2)当scope的取值为prototype

  • Bean的实例化个数:多个
  • Bean的实例化时机:当调用 getBean() 方法时实例化Bean
  • Bean的生命周期:
    1. 对象创建:当使用对象时,创建新的对象实例
    2. 对象运行:只要对象在使用中,就一直活着
    3. 对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
<?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="userDao01" class="erer.spring.introduction.dao.impl.UserDaoImpl"/>
    <bean id="userDao02" class="erer.spring.introduction.dao.impl.UserDaoImpl" scope="prototype"/>
</beans>
public class _Spring配置文件{
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
	@Test
    public void test01() {
        // 测试 singleton 属性:默认值、单例
        UserDao userDao01 = app.getBean("userDao01", UserDao.class);
        UserDao userDao02 = app.getBean("userDao01", UserDao.class);
        System.out.println(userDao01);	
        //erer.spring.introduction.dao.impl.UserDaoImpl01@45c7e403
        System.out.println(userDao02);	
        //erer.spring.introduction.dao.impl.UserDaoImpl01@45c7e403
        app.close();
    }
    @Test
    public void test02() {
        // 测试 prototype 属性:多例
        UserDao userDao01 = app.getBean("userDao02", UserDao.class);
        UserDao userDao02 = app.getBean("userDao02", UserDao.class);
        System.out.println(userDao01);						
        //erer.spring.introduction.dao.impl.UserDaoImpl01@752325ad
        System.out.println(userDao02);	
        //erer.spring.introduction.dao.impl.UserDaoImpl01@279fedbd
        app.close();
    }
}

3. 生命周期配置

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

  • destroy-method:指定类中销毁方法名称

1)创建初始化方法和销毁方法

package erer.spring.introduction.dao.impl;
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDao save running...");
    }
    public void init(){
        System.out.println("UserDaoImpl 初始化方法...");
    }
    public void destroy() {
        System.out.println("UserDaoImpl 销毁方法...");
    }
}

2)注入 bean 实例的同时指定初始化方法和销毁方法

<?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="erer.spring.introduction.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"/>
</beans>

3)使用 API 获得 Bean 实例

public class _生命周期配置{
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");	//UserDaoImpl 创建...
	@Test
    public void test() {
        UserDao userDao = app.getBean("userDao", UserDao.class);	
        //UserDaoImpl 初始化方法...
        System.out.println(userDao);	
        //erer.spring.introduction.dao.impl.UserDaoImpl01@ae13544
        app.close();	//UserDaoImpl 销毁方法...
    }
}

4. 实例化三种方式

  • 无参构造方法实例化(bean中必须存在无参构造函数)
package erer.spring.introduction.dao.impl;
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDao save running...");
    }
}
  • 工厂静态方法实例化(工厂的静态方法返回Bean实例)
package erer.spring.introduction.factory;
public class StaticFactoryBean {
    public static UserDao createUserDao() {
        return new UserDaoImpl();
    }
}
  • 工厂实例方法实例化 (工厂的非静态方法返回Bean实例)
package erer.spring.introduction.factory;
public class DynamicFactoryBean {
    public UserDao createUserDao() {
        return new UserDaoImpl();
    }
}
  • 注入 Bean
<?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
">
    <!-- 1、无参构造方法实例化-->
    <bean id="userDao01" class="erer.spring.introduction.dao.impl.UserDaoImpl"/>
    
    <!-- 2、工厂静态方法实例化-->
    <bean id="userDao02" class="erer.spring.introduction.factory.StaticFactoryBean" factory-method="createUserDao"/>
    
    <!-- 3、工厂实例方法实例化-->
    <bean id="factoryBean" class="erer.spring.introduction.factory.DynamicFactoryBean"/>
    <bean id="userDao03" factory-bean="factoryBean" factory-method="createUserDao"/>
</beans>
  • 使用 API 获得 Bean 实例
public class _bean实例化三种方式{
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
	@Test
    public void test() {
        //工厂静态方法实例化
        UserDao userDao01 = app.getBean("userDao02", UserDao.class);
        //工厂实例方法实例化
        UserDao userDao02 = app.getBean("userDao03", UserDao.class);
        System.out.println(userDao01);
        System.out.println(userDao02);
        app.close();
    }
}

4、Bean的依赖注入

1. 概念

service层调用dao层,业务层调用service层。。。

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

2. 注入方式

1)创建 UserDao 实现类

package erer.spring.introduction.dao.impl;
public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("UserDao save running...");
    }
}

2)创建 UserService,UserService 内部在调用 UserDao的save() 方法

  1. set方法注入

    (1)在 UserServiceImpl 中添加 setUserDao方法 创建有参构造

package erer.spring.introduction.service.impl;
public class UserServiceImpl implements UserService {
    private UserDao userDao;

    // Bean的依赖注入:set方法注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

(2)配置Spring容器进行注入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <!-- 无参构造方法实例化-->
    <bean id="userDao" class="erer.spring.introduction.dao.impl.UserDaoImpl"/>

    <!-- Bean的依赖注入:set方法注入-->
    <bean id="userService" class="erer.spring.introduction.service.impl.UserServiceImpl">
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

(3)使用 API 获得 Bean 实例

public class _set方法注入{
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
	@Test
    public void test() {
        UserService userService01 = app.getBean("userService", UserService.class);
        userService01.save();	// UserDao save running...
        UserService userService02 = new UserServiceImpl();
        userService02.save();	// 空指针异常,并非从spring容器获得,未对 UserDao 进行赋值
    }
}
  1. p命名空间注入
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <!-- 无参构造方法实例化-->
    <bean id="userDao" class="erer.spring.introduction.dao.impl.UserDaoImpl"/>
    <!-- Bean的依赖注入:P命名空间-->
    <bean id="userService" class="erer.spring.introduction.service.impl.UserServiceImpl" p:userDao-ref="userDao"/>
</beans>
  1. 构造方法注入
<?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="erer.spring.introduction.dao.impl.UserDaoImpl"/>
    <!-- Bean的依赖注入:构造方法注入-->
    <bean id="userService" class="erer.spring.introduction.service.impl.UserServiceImpl">
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>
</beans>

3. 数据类型

package erer.spring.introduction.dao.impl;
public class UserDaoImpl implements UserDao {
    private String username;
    private int age;
    private List<String> strList;
    private Map<String, User> userMap;
    private Properties properties;

    public List<String> getStrList() {
        return strList;
    }
    public void setStrList(List<String> strList) {
        this.strList = strList;
    }
    public Map<String, User> getUserMap() {
        return userMap;
    }
    public void setUserMap(Map<String, User> userMap) {
        this.userMap = userMap;
    }
    public Properties getProperties() {
        return properties;
    }
    public void setProperties(Properties properties) {
        this.properties = properties;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public void save() {
        System.out.println("username:" + username + "\tage:" + age);
        System.out.println("strList:" + strList);
        System.out.println("userMap:" + userMap);
        System.out.println("properties:" + properties);
        System.out.println("====================================");
    }
}
  • Bean注入
<?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="user01" class="erer.spring.introduction.domain.User">
        <property name="username" value="zhangsan"/>
        <property name="address" value="shanghai"/>
    </bean>
    <bean id="user02" class="erer.spring.introduction.domain.User">
        <property name="username" value="lisi"/>
        <property name="address" value="beijing"/>
    </bean>
	<!-- 一、普通数据类型注入-->
    <bean id="userDao" class="erer.spring.introduction.dao.impl.UserDaoImpl">
        <property name="username" value="zhangsan"/>
        <property name="age" value="18"/>
    </bean>
    <!-- 二、集合数据类型注入-->
    <bean id="userDao" class="erer.spring.introduction.dao.impl.UserDaoImpl">
        <!-- 1、List<String> -->
        <property name="strList">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
        <!-- 2、Map<String,User> -->
        <property name="userMap">
            <map>
                <entry key="user01" value-ref="user01"/>
                <entry key="user02" value-ref="user02"/>
            </map>
        </property>
        <!-- 3、Properties -->
        <property name="properties">
            <props>
                <prop key="p1">p1</prop>
                <prop key="p2">p2</prop>
                <prop key="p3">p3</prop>
            </props>
        </property>
    </bean>
</beans>
  • 使用API 获得 Bean 实例
public class _注入数据类型{
    ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
	@Test
	public void test() {
		UserDao userDao = app.getBean("userDao02", UserDao.class);
		userDao.save();
		app.close();
		// username:zhangsan	age:18
		// strList:[aaa, bbb, ccc]
		// userMap:{user01=User{username='zhangsan', address='shanghai'}, 			
		// user02=User{username='lisi', address='beijing'}}
		// properties:{p1=p1, p2=p2, p3=p3}
		// ====================================
    }
}

5、Spring相关API

1. 引入其他配置文件

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载。

<?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
">
    <!-- 引入其他配置文件-->
    <import resource="applicationContext01.xml"/>
</beans>

2. ApplicationContext的实现类

在这里插入图片描述

  • ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件,推荐使用这种
  • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,可以在磁盘的任意位置
  • AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建 spring 容器,它用来读取注解
public class _ApplicationContext的实现类 {
    @Test
    public void test01(){
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = app.getBean("userDao", UserDao.class);
        userDao.save();
        app.close();
    }
    @Test
    public void test02(){
        FileSystemXmlApplicationContext app = new FileSystemXmlApplicationContext("target/classes/applicationContext.xml");
        UserDao userDao = app.getBean("userDao", UserDao.class);
        userDao.save();
        app.close();
    }
}

3. getBean()方法使用

  • 方法一
public class _getBean方法使用 {
    @Test
    public void test01() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = app.getBean("userDao01", UserDao.class);
        userDao.save();
        app.close();
    }
}
  • 方法二:当参数的数据类型是字符串时,表示根据Bean的id从容器中获得Bean实例,返回是Object,需要强转;当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错。
public class _getBean方法使用 {
    @Test
    public void test02() {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDaoImpl user = app.getBean(UserDaoImpl.class);
        user.save();
        app.close();
    }
}

二、IoC和DI注解开发

1、Spring配置数据源

1. 数据源的作用

常见的数据源(连接池):DBCP、C3P0、BoneCP、Druid

  • 提高程序性能
  • 事先实例化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕后将连接资源归还给数据源

2. 手动创建数据源

2.1 引入相关依赖
<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>
<!-- c3p0数据库连接池 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
<!-- druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>
2.2 C3P0配置数据源
public class _创建数据库连接池 {
    @Test
    public void test() throws PropertyVetoException {
        //1、创建数据源
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        //2、 设置数据库连接参数
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/java");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        //3、获取链接对象
        try (
                Connection connection = dataSource.getConnection()
        ) {
            System.out.println(connection);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}
2.3 Druid配置数据源
public class _创建数据库连接池 {
    @Test
    public void test() throws SQLException {
        //1、创建数据源
        DruidDataSource dataSource = new DruidDataSource();
        //2、设置数据库连接参数
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/java");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        //3、获得连接对象
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //4、释放资源
        connection.close();
    }
}
2.4 通过加载配置文件
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java
jdbc.username=root
jdbc.password=123456
public class _创建数据库连接池 {
    @Test
    public void test() throws SQLException {
        //1、创建数据源
        DruidDataSource dataSource = new DruidDataSource();
        //2、读取配置文件
        ResourceBundle resourceBundle = ResourceBundle.getBundle("jdbc");
        //3、设置数据库连接参数
        dataSource.setDriverClassName(resourceBundle.getString("jdbc.driver"));
        dataSource.setUrl(resourceBundle.getString("jdbc.url"));
        dataSource.setUsername(resourceBundle.getString("jdbc.username"));
        dataSource.setPassword(resourceBundle.getString("jdbc.password"));
        //4、获得连接对象
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        //5、释放资源
        connection.close();
    }
}

3. Spring配置数据源

可以将 DataSource 的创建权交由 Spring容器 去完成

  • DataSource有无参构造方法,而Spring默认就是通过无参构造方法实例化对象的
  • DataSource要想使用需要通过set方法设置数据库连接信息,而Spring可以通过set方法进行字符串注入
3.1 配置数据源
<?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 
       http://www.springframework.org/schema/context/spring-context.xsd
">
    <bean id="dataSource01" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/java"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <bean id="dataSource02" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/java"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <!-- 加载外部 properties 文件(需要引入context命名空间和约束路径) -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource03" 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>
3.2. 获取数据源
public class _Spring配置数据源 {
    ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    @Test
    public void test01() throws SQLException {
        DataSource dataSource01 = app.getBean("dataSource01", DataSource.class);
        Connection connection01 = dataSource01.getConnection();
        System.out.println(connection01);
        connection01.close();
        DataSource dataSource02 = app.getBean("dataSource02", DataSource.class);
        Connection connection02 = dataSource02.getConnection();
        System.out.println(connection02);
        connection02.close();
    }
    @Test
    public void test02() throws SQLException {
        DataSource dataSource = app.getBean("dataSource03", DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }
}

2、Spring注解开发

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

1. Spring原始注解

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

注意:

  • 使用注解进行开发时,需要在 applicationContext.xml 中配置组件扫描
  • 作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。
<?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 
       http://www.springframework.org/schema/context/spring-context.xsd
">
    <!--注解的组件扫描-->
    <context:component-scan base-package="erer.spring"/>
    <!-- 加载外部 properties 文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
</beans>
  • 使用 @Compont 或 @Repository 标识 UserDaoImpl 需要 Spring 进行实例化
  • 使用 @Scope 标注Bean的范围
package erer.spring.IoCAndDI.dao.impl;
// @Scope("prototype")
// @Scope("singleton")
@Repository("userDao")
public class UserDaoImpl implements {
    @Override
    public void save() {
        System.out.println("UserDao save running...");
    }
}
  • 使用 @Compont 或 @Service 标识 UserServiceImpl 需要 Spring 进行实例化
  • 使用 @Autowired 或者 @Autowired+@Qulifier 或者 @Resource 进行 userDao 的注入
  • 使用 @Value 进行字符串的注入
  • 使用 @PostConstruct 标注初始化方法,使用 @PreDestroy 标注销毁方法
package erer.spring.IoCAndDI.service.impl;
@Service("userService")
public class UserServiceImpl implements UserService {
    //@Autowired
    //@Qualifier("userDao")
    @Resource(name = "userDao")
    private UserDao userDao;

    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @PostConstruct
    public void init() {
        System.out.println("初始化方法....");
    }
    @PreDestroy
    public void destroy(){
        System.out.println("销毁方法.....");
    }
}

2. Spring新注解

注解说明
@Configuration用于指定当前类是一个 Spring 配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定 Spring 在初始化容器时要扫描的包。
类似于 Spring 的 xml 配置文件中的:
<context:component-scan base-package=“erer.spring”/>
@Bean使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@PropertySource用于加载.properties 文件中的配置
@Import用于导入其他配置类
  • 配置类
package erer.spring.IoCAndDI.cofig;
//标准该类是 Spring 的核心配置类(替代"applicationContext.xml"配置文件)
@Configuration
//注解的组件扫描:<context:component-scan base-package="erer.spring"/>
@ComponentScan("erer.spring")
//引入其他配置文件:<import resource=""/>
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {

}
  • 配置文件
package erer.spring.IoCAndDI.cofig;
//加载外部 properties 文件
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class DataSourceConfiguration {

    @Value("${jdbc.driver}")
    private String driverClassName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /*
    <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>
    */
    @Bean("dataSource") //标注将该方法的返回值存储到 Spring 容器中
    public DataSource getDataSource() {
        //创建数据源
        DruidDataSource dataSource = new DruidDataSource();
        //设置数据库连接参数
        dataSource.setDriverClassName(driverClassName);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
  • 使用API 获得 Bean 实例
public class _Spring新注解 {
    @Test
    public void test() throws SQLException {
        AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        UserService userService = app.getBean(UserService.class);	//初始化方法....
        userService.save();	//UserDao save running...
        UserDao userDao = app.getBean(UserDao.class);
        userDao.save();	//UserDao save running...
        DataSource dataSource = app.getBean(DataSource.class);
        Connection connection = dataSource.getConnection();
        System.out.println(connection);	//com.mysql.cj.jdbc.ConnectionImpl@43b9fd5
    }
}  

3、Spring整合Junit

在测试类中,每个测试方法都有以下两行代码:

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
IAccountService as = app.getBean("accountService",IAccountService.class);

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

解决方案:

  • 让 SpringJunit 负责创建 Spring容器,但是需要将配置文件的名称告诉它
  • 将需要进行测试Bean直接在测试类中进行注入

1. 引入项目依赖

<!-- Spring 集成 Junit -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<!-- Junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
</dependency>

2. 替换运行周期

@RunWith(SpringJUnit4ClassRunner.class)

3. 指定配置文件

@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = SpringConfiguration.class)

4. 注入测试对象

@Autowired
private UserService userService;

5. 进行测试

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")
@ContextConfiguration(classes = SpringConfiguration.class)
public class _Spring集成Junit {
    @Autowired
    private UserService userService;

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

三、SpringMVC入门

1、自定义获取应用上下文

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

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

1. 引入依赖

<!--servlet-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <scope>provided</scope>
    <version>4.0.1</version>
</dependency>

2. 创建监听器

package erer.spring.SpringMVC.web.listener;
//@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();
        //读取 spring.web.xml 中的全局参数
        String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
        //获取 Spring 的应用上下文对象
        ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
        // 将 Spring 的应用上下文对象 存储到 servletContext 域中。
        servletContext.setAttribute("app", app);
    }
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
    }
}

3. 配置监听器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 全局初始化参数 -->
    <context-param>
        <!-- 监听器 ContextLoaderListener 配置 contextConfigLocation 参数 -->
        <param-name>contextConfigLocation</param-name>
        <param-value>applicationContext.xml</param-value>
    </context-param>
    <!-- 配置监听器 -->
    <listener>
        <listener-class>erer.spring.SpringMVC.web.listener.ContextLoaderListener</listener-class>
    </listener>

</web-app>

4. 创建客户端工具

package erer.spring.SpringMVC.web.listener;
public class WebApplicationContextUtils {
    public static ApplicationContext getWebApplicationContext(ServletContext servletContext) {
        return (ApplicationContext) servletContext.getAttribute("app");
    }
}

5. 进行测试

package erer.spring.SpringMVC.web.servlet;
@WebServlet("/userServlet")
public class UserServlet extends HttpServlet {
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        // 通过 客户端工具 获取应用上下文
        ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        UserService userService = app.getBean("userService",UserService.class);
        userService.save();
    }
}

2、Spring获取应用上下文

1. 引入依赖

<!-- WebApplicationContextUtils -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>

2. 配置监听器

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- Spring提供获取应用上下文的工具 -->
    <!-- 全局初始化参数 -->
    <context-param>
        <!-- 监听器 ContextLoaderListener 配置 contextConfigLocation 参数 -->
        <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>
</web-app>

3. 进行测试

package erer.spring.SpringMVC.web.servlet;
@WebServlet("/userServlet")
public class UserServlet extends HttpServlet {
    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }

    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        UserService userService = app.getBean("userService",UserService.class);
        userService.save();
    }
}

3、SpringMVC开发步骤

  • SpringMVC 是一种基于 Java 的实现 MVC设计模型的请求驱动类型的轻量级 Web框架,属于SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 中。
  • SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布,全面超越 Struts2,成为最优秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时它还支持 RESTful 编程风格的请求。

在这里插入图片描述

1. 引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>erer.spring.SpringMVC</groupId>
    <artifactId>ceshi</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--打包方式 -->
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>16</maven.compiler.source>
        <maven.compiler.target>16</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <scope>provided</scope>
            <version>8.0.1</version>
        </dependency>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
        <!-- WebApplicationContextUtils -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.12.RELEASE</version>
        </dependency>
        <!-- javax -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--servlet-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
            <version>4.0.1</version>
        </dependency>
        <!-- Jsp -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <scope>provided</scope>
            <version>2.3.3</version>
        </dependency>
	   <!-- json -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.12.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.4</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.12.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.1</version>
            </plugin>
            <!--jdk编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!--tomcat插件-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <!-- 通过maven tomcat7:run运行项目时,访问项目的端口号 -->
                    <port>80</port>
                    <!-- 项目访问路径 -->
                    <path>/spring</path>
                    <uriEncoding>UTF-8</uriEncoding>
                    <charset>UTF-8</charset>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2. 配置核心控制器

  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 1、配置 springMVC 前端控制器 DispatcherServlet -->
    <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>

    <!-- 2、Spring提供获取应用上下文的工具 -->
    <!-- 全局初始化参数 -->
    <context-param>
        <!-- 监听器 ContextLoaderListener 配置 contextConfigLocation 参数 -->
        <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>

    <!-- 3、解决当post请求数据乱码问题 -->
    <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>

3. 创建Controller类

package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    @RequestMapping("/quick")
    public String save(){
        System.out.println("controller save running.");
        return "success.jsp";
    }
}

4. 创建视图页面

  • success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!doctype html>
<head>
    <title>success</title>
</head>
<body>
<h1>success~ ${username}</h1>
</body>
</html>

5. 配置核心文件

  • 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 http://www.springframework.org/schema/context/spring-context.xsd
">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>
</beans>

6. 发起请求测试

  • 请求地址:http://localhost/spring/quick

在这里插入图片描述

4、SpringMVC的执行流程

在这里插入图片描述

1、用户发送请求至 --> 前端控制器:DispatcherServlet。

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

2、DispatcherServlet收到请求调用 --> 处理器映射器:HandlerMapping

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

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

4、DispatcherServlet调用 --> 处理器适配器:HandlerAdapter

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

5、HandlerAdapter 经过适配调用 --> 具体的处理器Handler (如:Controller,也叫后端控制器)。

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

6、Controller 执行完成返回 ModelAndView。

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

8、DispatcherServlet 将 ModelAndView 传给 View Resolver 视图解析器。

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

9、View Resolver解析后返回具体 View。

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

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

5、SpringMVC组件

1. @RequestMapping

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

2、位置:

  • 类上,请求URL 的第一级访问目录。此处不写的话,就相当于应用的根目录
  • 方法上,请求 URL 的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径

3、属性:

  • value:用于指定请求的URL。它和path属性的作用是一样的

  • method:用于指定请求的方式

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

    • params = {“username”},表示请求参数必须有 username
    • params = {“money!100”},表示请求参数中money不能是100
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick
    @RequestMapping(value = "/quick", method = RequestMethod.GET, params = {"username"})
    public String save(){
        System.out.println("Controller save running...");
        // return "/quick.jsp";
        // return "forward:/success.jsp";
        return "redirect:/success.jsp";
    }
}
  • return “forward:/success.jsp”;

在这里插入图片描述

  • return “redirect:/success.jsp”;

在这里插入图片描述

2. 视图解析器

<?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 http://www.springframework.org/schema/context/spring-context.xsd
">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick
    @RequestMapping("/quick")
    public String save02(){
        System.out.println("Controller save running...");
        // 转发地址:spring/views/success.jsp
        return "success";
    }
}

6、SpringMVC的数据响应

1. 页面跳转

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

    • 转发:return “forward:/success”;
    • 重定向:return “redirect:/success.jsp”;
  • 通过ModelAndView对象返回

    • Model:模型 用于封装数据
    • View:试图 用于展示数据
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick01
    @RequestMapping("/quick01")
    public ModelAndView save01(){
        ModelAndView modelAndView = new ModelAndView();
        // 设置模型数据
        modelAndView.addObject("username", "zhangsan");
        // 设置视图名称
        modelAndView.setViewName("success");
        return modelAndView;
    }
    
    //请求地址 http://localhost:80/spring/quick02
    @RequestMapping("/quick02")
    public ModelAndView save02(ModelAndView modelAndView) {
        // 设置模型数据
        modelAndView.addObject("username", "lisi");
        // 设置视图名称
        modelAndView.setViewName("success");
        return modelAndView;
    }
    
    //请求地址 http://localhost:80/spring/quick03
    @RequestMapping("/quick03")
    public String save03(Model model) {
        model.addAttribute("username", "wangwu");
        return "success";
    }

    //请求地址 http://localhost:80/spring/quick04
    @RequestMapping("/quick04")
    public String save04(HttpServletRequest request) {
        request.setAttribute("username", "林青霞");
        return "success";
    }
}

2. 回写数据

2.1 直接返回字符串
  • 通过SpringMVC框架注入的 response 对象,使用response.getWriter().print(“hello world”) 回写数据,此时不需要视图跳转,业务方法返回值为void。
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
  
	//请求地址 http://localhost:80/spring/quick
	@RequestMapping("/quick")
	public void save(HttpServletResponse response) throws IOException {
		response.getWriter().println("hello java!");
	}
}
  • 将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    
	//请求地址 http://localhost:80/spring/quick01
    @RequestMapping("/quick01")
    @ResponseBody   // 告诉 SpringMVC框架 不是跳转是直接在 http 响应体中返回字符串
    public String save01() {
        return "hello SpringMVC";
    }

    //请求地址 http://localhost:80/spring/quick02
    @RequestMapping("/quick02")
    @ResponseBody
    public String save02() {
        return "{\"username\":\"zhangsan\", \"age\":18}";
    }
}
2.2 返回对象或集合
  • 转换为 json 字符串回写(使用前引入相关依赖)
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {

    //请求地址 http://localhost:80/spring/quick
    @RequestMapping("/quick")
    @ResponseBody
    public String save() throws JsonProcessingException {
        User user = new User();
        user.setUsername("zhangsan");
        user.setAge(20);
        // json 转换工具,转换为 json 格式(使用前引入相关依赖)
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.writeValueAsString(user);
    }
}
  • spring-mvc.xml 配置 MappingJackson2HttpMessageConverter 后直接返回
<?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 http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、配置处理器映射器(可以直接返回对象、集合) -->
    <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>
</beans>
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {

    //请求地址 http://localhost:80/spring/quick01
    @RequestMapping("/quick01")
    @ResponseBody
    public User save01() {
        User user = new User();
        user.setUsername("lisi");
        user.setAge(20);
        return user;
    }

    //请求地址 http://localhost:80/spring/quick02
    @RequestMapping("/quick02")
    @ResponseBody
    public List<User> save02() {
        User user1 = new User();
        user1.setUsername("lisi");
        user1.setAge(20);
        User user2 = new User();
        user2.setUsername("lisi");
        user2.setAge(20);
        List<User> users = new ArrayList<>();
        users.add(user1);
        users.add(user2);
        return users;
    }
}
  • mvc的注解驱动:mvc:annotation-driven/ 替代 MappingJackson2HttpMessageConverter 配置
<?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"
       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/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">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、mvc的注解驱动 -->
	<mvc:annotation-driven/>
</beans>

3. 获得请求参数

  • 客户端请求参数的格式是:name=value&name=value …
  • 服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC 可以接收如下类型的参数:
3.1 基本类型参数
  • Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    
	//请求地址 http://localhost:80/spring/quick?username=zhangsan&age=18
    @RequestMapping("/quick")
    @ResponseBody
    public void save(String username, int age) {
        System.out.println("username:" + username + "\tage:" + age);
    }
}
3.2 POJO类型参数
  • Controller 中的业务方法的 POJO 参数的属性名与请求参数的 name 一致,参数值会自动映射匹配。
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick?username=lisi&age=20
    @RequestMapping("/quick")
    @ResponseBody
    public void save(User user) {
        System.out.println(user);
    }
}
3.3 数组类型参数
  • Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick0?strs=111&strs=222&strs=333
    @RequestMapping("/quick")
    @ResponseBody
    public void save(String[] strs) {
        System.out.println(Arrays.asList(strs));
    }
}
3.4 集合类型参数
  • 获得集合参数时,要将集合参数包装到一个POJO中才可以。
package erer.spring.SpringMVC.domain;
public class UserVo {
    private List<User> users;
    public List<User> getUsers() {
        return users;
    }
    public void setUsers(List<User> users) {
        this.users = users;
    }
    @Override
    public String toString() {
        return "UserVo{" +
                "users=" + users +
                '}';
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>collection</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/quick" method="post">
    <label>
        <span>user01-name</span><input type="text" name="users[0].username">
    </label><br>
    <label>
        <span>user01-age</span><input type="text" name="users[0].age">
    </label><br>
    <label>
        <span>user02-name</span><input type="text" name="users[1].username">
    </label><br>
    <label>
        <span>user02-age</span><input type="text" name="users[1].age">
    </label><br>
    <input type="submit" value="提交"><br>
</form>
</body>
</html>
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    
    //请求地址 http://localhost:80/spring/collection.jsp
    @RequestMapping("/quick")
    @ResponseBody
    public void save(UserVo usersVo) {
        System.out.println(usersVo);
    }
}
  • 当使用 ajax 提交时,可以指定 contentType 为 json 形式,那么在方法参数位置使用 @RequestBody 可以直接接收集合数据而无需使用POJO进行包装。
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <!--导入jquery-->
    <script src="${pageContext.request.contextPath}/bootstrap/js/jquery.min.js"></script>
    <title>ajax</title>
</head>
<body>
<script>
    //模拟数据
    let users = [];
    users.push({username: "zhangsan", age: "20"});
    users.push({username: "lisi", age: "20"});
    $.ajax({
        type: "POST",
        url: "${pageContext.request.contextPath}/quick",
        data: JSON.stringify(users),
        contentType: 'application/json;charset=utf-8'
    });
</script>
</body>
</html>
<?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"
       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/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

">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、mvc的注解驱动-->
    <mvc:annotation-driven/>
    
    <!-- 4、开发资源的访问权限(不去匹配 资源视图解析器 的前缀和后缀)-->
    <mvc:resources mapping="/bootstrap/**" location="/bootstrap/"/>
</beans>
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    
    //请求地址 http://localhost:80/spring/ajax.jsp
    @RequestMapping("/quick")
    @ResponseBody
    public void save(@RequestBody List<User> users) {
        System.out.println(users);
    }
}

4. 开放资源访问权限

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

">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、mvc的注解驱动-->
    <mvc:annotation-driven/>
    
    <!-- 4、开发资源的访问权限(不去匹配 资源视图解析器 的前缀和后缀)-->
	<!--<mvc:resources mapping="/bootstrap/**" location="/bootstrap/"/>-->
    <!-- 路径找不到的时候,交由 tomcat 寻找资源 -->
    <mvc:default-servlet-handler/>
</beans>

5. @requestParam

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

  • 注解 @RequestParam 还有如下参数可以使用:

    • value:与请求参数名称
    • required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
    • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    
    //请求地址 http://localhost:80/spring/quick01?name=zhangsan
    @RequestMapping("/quick01")
    @ResponseBody
    public void save01(@RequestParam("name") String username) {
        System.out.println("username:" + username);
    }

    //请求地址 http://localhost:80/spring/quick02
    @RequestMapping("/quick02")
    @ResponseBody
    public void save02(@RequestParam(value = "name", required = false) String username) {
        System.out.println("username:" + username);
    }

    //请求地址 http://localhost:80/spring/quick03
    //        http://localhost:80/spring/quick03?name=zhangsan
    @RequestMapping("/quick03")
    @ResponseBody
    public void save03(@RequestParam(value = "name", required = false, defaultValue = "erer") String username) {
        System.out.println("username:" + username);
    }
}

6. Restful风格

(1)概述

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

(2)获取方式

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

(3)实现

上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。

地址:/user/1 可以写成:/user/{id},占位符{id}对应的就是1的值。

在业务方法中我们可以使用 @PathVariable 注解进行占位符的匹配获取工作。

package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick/zhangsan
    @RequestMapping(value = "/quick/{name}", method = RequestMethod.GET)
    @ResponseBody
    public void save(@PathVariable("name") String username) {
        System.out.println("username:" + username);
    }
}

7. 自定义类型转换器

  • 自定义类型转换器

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

7.1 定义转换器

定义转换器类实现 Converter 接口

package erer.spring.SpringMVC.converter;
public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String dateStr) {
        // 将日期字符串转换为日期对象
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return simpleDateFormat.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return null;
    }
}
7.2 声明转换器
  • 在配置文件中声明转换器
7.3 引用转换器
  • <annotation-driven>中引用转换器
<?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"
       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/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

">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、mvc的注解驱动(引用日期转换器)-->
    <mvc:annotation-driven  conversion-service="conversionService"/>
    
    <!-- 4、开发资源的访问权限(不去匹配 资源视图解析器 的前缀和后缀, 路径找不到的时候,交由 tomcat 寻找资源)-->
    <mvc:default-servlet-handler/>

    <!-- 5、声明 Date 转换器 -->
    <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <list>
                <bean class="erer.spring.SpringMVC.converter.DateConverter"/>
            </list>
        </property>
    </bean>
</beans>
7.4 调用转换器
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //原始 请求地址 http://localhost:80/spring/quick?date=2021/09/10
    //自定义 请求地址 http://localhost:80/spring/quick?date=2021-09-10
    @RequestMapping("/quick")
    @ResponseBody
    public void save(Date date) {
        System.out.println("date:" + date);
    }
}

8. 获得Servlet相关API

package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick
    @RequestMapping("/quick")
    @ResponseBody
    public void save(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        System.out.println("request:" + request);
        System.out.println("response:" + response);
        System.out.println("session:" + session);
    }
}

9.获得请求头

9.1 @RequestHeader

@RequestHeader:相当于web阶段学习的request.getHeader(name),注解的属性如下:

  • value:请求头的名称
  • required:是否必须携带此请求头
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick
    @RequestMapping("/quick")
    @ResponseBody
    public void save(@RequestHeader(value = "User-Agent", required = false) String userAgent) {
        System.out.println("userAgent:" + userAgent);
    }
}
9.2 @CookieValue

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

  • value:指定cookie的名称
  • required:是否必须携带此cookie
package erer.spring.SpringMVC.controller;
@Controller
public class UserController {
    //请求地址 http://localhost:80/spring/quick
    @RequestMapping("/quick")
    @ResponseBody
    public void save(@CookieValue("JSESSIONID") String jsessionid) {
        System.out.println("jsessionid:" + jsessionid);
    }
}

10.文件上传

10.1 文件上传三要素
  • 表单项 type="file"
  • 表单的提交方式是 post
  • 表单的 enctype 属性是多部分表单形式,及 enctype="multipart/form-data"
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>upload</title>
</head>
<body>
<div class="container">
    <form action="${pageContext.request.contextPath}/quick" enctype="multipart/form-data" method="post">
        <div class="row">
            <label>
                <span>名称:</span><input type="text" name="username">
            </label>
        </div>
        <div class="row">
            <label>
                <input type="file" name="uploadFile">
            </label>
        </div>
        <div class="row">
            <input type="submit" value="提交">
        </div>
    </form>
</div>
</body>
</html>
10.2 文件上传原理
  • 当form表单修改为多部分表单时,request.getParameter() 将失效。
  • enctype="application/x-www-form-urlencoded"时,form表单的正文内容格式是:key=value&key=value&key=value
  • 当form表单的 enctype=Multipart/form-data时,请求正文内容就变成多部分形式:

在这里插入图片描述

10.3 单文件上传
  • 导入 commons-fileupload 和 commons-io 坐标
<!-- file:文件上传 -->
<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.4</version>
</dependency>
<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>
  • 配置文件上传解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/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

">

    <!-- 1、Controller 的组件扫描 -->
    <context:component-scan base-package="erer.spring.SpringMVC.controller"/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、mvc的注解驱动(引用日期转换器)-->
    <mvc:annotation-driven/>
    
    <!-- 4、开发资源的访问权限(不去匹配 资源视图解析器 的前缀和后缀,路径找不到的时候,交由 tomcat 寻找资源)-->
    <mvc:default-servlet-handler/>

    <!-- 5、配置文件上传解析器 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--上传文件总大小-->
        <property name="maxUploadSize" value="5242800"/>
        <!--上传单个文件的大小-->
        <property name="maxUploadSizePerFile" value="5242800"/>
        <!--上传文件的编码类型-->
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>
</beans>
  • 编写文件上传代码 MultipartFile uploadFile(名称要与 jsp文件 file 表单 name 一致)
package erer.spring.SpringMVC.controller;
@Controller
public class upload {
    //请求地址 http://localhost:80/spring/upload.jsp
    @RequestMapping("/quick")
    @ResponseBody
    public void save01(String username, MultipartFile uploadFile) throws IOException {
        System.out.println(username);
        String file = "D:\\programming\\Java\\05Spring\\03SpringMVC\\src\\main\\webapp\\upload\\";
        String filename = uploadFile.getOriginalFilename();
        uploadFile.transferTo(new File(file + filename));
    }
}
10.4 多文件上传
  • 多文件上传,只需要将页面修改为多个文件上传项,将方法参数 MultipartFile 类型修改为 MultipartFile[] 即可
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
    <title>upload</title>
</head>
<body>
<div class="container">
    <form action="${pageContext.request.contextPath}/quick" enctype="multipart/form-data" method="post">
        <div class="row">
            <label>
                <span>名称:</span><input type="text" name="username">
            </label>
        </div>
        <div class="row">
            <label>
                <input type="file" name="uploadFiles">
            </label>
        </div>
        <div class="row">
            <label>
                <input type="file" name="uploadFiles">
            </label>
        </div>
        <div class="row">
            <input type="submit" value="提交">
        </div>
    </form>
</div>
</body>
</html>
package erer.spring.SpringMVC.controller;
@Controller
public class upload {
    //请求地址 http://localhost:80/spring/upload.jsp
    @RequestMapping("/quick")
    @ResponseBody
    public void save02(String username, MultipartFile[] uploadFiles) throws IOException {
        System.out.println(username);
        String file = "D:\\programming\\Java\\05Spring\\03SpringMVC\\src\\main\\webapp\\upload\\";
        for (MultipartFile uploadFile : uploadFiles){
            String filename = uploadFile.getOriginalFilename();
            uploadFile.transferTo(new File(file + filename));
        }
    }
}

四、JdbcTemplate

1、引入依赖

<!-- 引入 junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
    <version>4.13.2</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.26</version>
</dependency>
<!-- c3p0数据库连接池 -->
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>
<!-- druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>
<!-- Spring -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<!-- Spring 集成 Junit -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<!-- Spring集成jdbc:JdbcTemplate -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<!-- Spring集成tx:事务控制 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>

2、创建数据库表和实体

  • 创建 java 数据库
  • 创建 Account 实体类
package erer.spring.JdbcTemplate.domain;
public class Account {
    private String name;
    private double balance;
}

3、 创建JdbcTemplate对象

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/java
jdbc.username=root
jdbc.password=123456
# 初始化连接数
jdbc.initialSize=5
# 最大连接数
jdbc.maxActive=10
# 超时时间
jdbc.maxWait=3000
<?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 http://www.springframework.org/schema/context/spring-context.xsd
">

    <!-- 1、加载jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 2、数据源DataSource -->
    <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}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
    </bean>
    <!-- 3、注入JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

4、执行数据库操作

1. 添加

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _JdbcTemplateTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Test
    public void testInsert() {
       jdbcTemplate.update("insert into account(name,balance) values(?,?)", "林青霞", 5000);
    }
}

2. 更新

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _JdbcTemplateTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Test
    public void testUpdate() {
        jdbcTemplate.update("update account set balance = ? where name = ?", 10000, "tom");
    }
}

3. 删除

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _JdbcTemplateTest {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Test
    public void testDelete() {
        jdbcTemplate.update("delete from account where name = ?", "尔尔");
    }
}

4. 查询

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

    @Test
    public void testQueryAll() {
        List<Account> accounts = jdbcTemplate.query("select * from account", new BeanPropertyRowMapper<>(Account.class));
        for (Account account : accounts) {
            System.out.println(account);
        }
    }
    @Test
    public void testQueryOne() {
        Account account = jdbcTemplate.queryForObject("select * from account where name = ?", new BeanPropertyRowMapper<>(Account.class), "尔尔");
        System.out.println(account);
    }
    @Test
    public void testQueryCount() {
        Long count = jdbcTemplate.queryForObject("select count(*) from account", Long.class);
        System.out.println(count);
    }
}

五、SpringMVC拦截器

1、拦截器的作用

  • Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
  • 将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。
  • 在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
  • 拦截器也是AOP思想的具体实现。

2、拦截器和过滤器区别

  • 使用范围

    • 过滤器:是 servlet 规范中的一部分,任何 Java Web 工程都可以使用
    • 拦截器:是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能用
  • 拦截范围

    • 过滤器:在 url-pattern 中配置了 /* 之后,可以对所有要访问的资源拦截
    • 拦截器:只会拦截访问的控制器方法,如果访问的是 jsp、html、css、image 或 js,是不会进行拦截的。在 <mvc:mapping path=“” /> 中配置 /** 之后,也可以拦截所有资源但是可以通过 <mvc:exclude-mapping path=“” /> 标签排除不需要拦截的资源

3、自定义拦截器

1. 创建拦截器类

package erer.spring.Interceptor.interceptor;
public class Interceptor implements HandlerInterceptor {
    // 在目标方法执行之前执行
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
        System.out.println("preHandle running...");
        String param = request.getParameter("param");
        if ("yes".equals(param)) {
            return true;    // 代表放行,继续执行后续方法
        } else {
            request.getRequestDispatcher("error.jsp").forward(request, response);
            return false;   // 代表不放行,后面所有方法均不执行
        }
    }

    // 在目标方法执行之后,视图对象返回之前执行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        assert modelAndView != null;
        modelAndView.addObject("name", "张三");
        System.out.println("postHandle running...");
    }

    // 在目标方法,视图对象返回之后执行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("afterCompletion running...");
    }
}

2. 配置拦截器

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

    <!-- 1、mvc的注解驱动(可以不使用 转发、重定向等 用 return 直接返回对象、集合) -->
    <mvc:annotation-driven/>

    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 3、开放资源的访问权限(路径找不到的时候,交由 tomcat 寻找资源)-->
    <mvc:default-servlet-handler/>

    <!-- 4、使用注解进行开发时,需要配置组件扫描范围 -->
    <context:component-scan base-package="erer.spring.Interceptor">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 5、配置拦截器-->
    <mvc:interceptors>
        <!-- 在上面的配置先执行 -->
        <mvc:interceptor>
            <!-- 对哪些资源执行拦截操作: /** 表示对所有资源进行拦截操作 -->
            <mvc:mapping path="/**"/>
            <!-- 拦截操作 排除以下资源 -->
            <mvc:exclude-mapping path="/css/**"/>
            <mvc:exclude-mapping path="/img/**"/>
            <mvc:exclude-mapping path="/plugins/**"/>
            <mvc:exclude-mapping path="/user/login"/>
            <bean class="erer.spring.Interceptor.interceptor.Interceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>

3. 测试拦截效果

package erer.spring.Interceptor.controller;
@Controller
public class TargetController {
    //请求地址 http://localhost:80/interceptor/target
    //请求地址 http://localhost:80/interceptor/target?param=yes
    @RequestMapping("/target")
    public ModelAndView show() {
        System.out.println("目标资源执行");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("name", "尔尔");
        modelAndView.setViewName("index");
        return modelAndView;
    }
}

4、拦截器方法说明

  • preHandle():

    • 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的
    • 当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;
    • 当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法
  • postHandle()

    • 该方法是在当前请求进行处理之后被调用,前提是preHandle 方法的返回值为true 时才能被调用
    • 它会在 DispatcherServlet 进行视图返回渲染之前被调用
    • 所以我们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作
  • afterCompletion()

    • 该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行
    • 前提是 preHandle 方法的返回值为 true 时才能被调用

六、异常处理机制

1、异常处理的思路

  • 系统中异常包括两类:预期异常和运行时异常RuntimeException,

    • 预期异常通过捕获异常从而获取异常信息,
    • 运行时异常主要通过规范代码开发、测试等手段减少运行时异常的发生。
  • 系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理:

在这里插入图片描述

2、异常处理两种方式

  • 使用 Spring MVC提供的简单异常处理器 SimpleMappingExceptionResolver
  • 实现 Spring 的异常处理接口 HandlerExceptionResolver 自定义自己的异常处理器

3、简单异常处理器

  • SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置
<?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"
       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/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
">

    <!-- 1、mvc的注解驱动(可以不使用 转发、重定向等 用 return 直接返回对象、集合) -->
    <mvc:annotation-driven/>
    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 3、开放资源的访问权限(路径找不到的时候,交由 tomcat 寻找资源)-->
    <mvc:default-servlet-handler/>
    <!-- 4、使用注解进行开发时,需要配置组件扫描范围 -->
    <context:component-scan base-package="erer.spring.exception">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 5、配置简单映射异常处理器-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 默认错误视图 value:错误视图名称(访问页面 error.jsp) -->
        <property name="defaultErrorView" value="error/error"/>
        <property name="exceptionMappings">
            <map>
                <!-- key:异常类型、value:错误视图名称 -->
                <entry key="java.lang.ClassCastException" value="error/error01"/>
                <entry key="erer.spring.exception.exception.MyException" value="error/error02"/>
            </map>
        </property>
    </bean>
</beans>

4、自定义异常处理步骤

1. 创建异常处理器

package erer.spring.exception.resolver;
public class MyExceptionResolver implements HandlerExceptionResolver {
    /**
     * @param httpServletRequest
     * @param httpServletResponse
     * @param o
     * @param e 异常对象
     * @return  跳转的错误视图信息
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView modelAndView = new ModelAndView();
        if(e instanceof MyException) {
            modelAndView.addObject("info", "自定义的异常");
        } else if (e instanceof ClassCastException) {
            modelAndView.addObject("info", "类型转换异常");
        } else {
            modelAndView.addObject("info", "通用异常");
        }
        modelAndView.setViewName("error/error");
        return modelAndView;
    }
}

2. 配置异常处理器

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

    <!-- 1、mvc的注解驱动(可以不使用 转发、重定向等 用 return 直接返回对象、集合) -->
    <mvc:annotation-driven/>
    <!-- 2、配置内部资源视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置内部资源路径的前缀和后缀 -->
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 3、开放资源的访问权限(路径找不到的时候,交由 tomcat 寻找资源)-->
    <mvc:default-servlet-handler/>
    <!-- 4、使用注解进行开发时,需要配置组件扫描范围 -->
    <context:component-scan base-package="erer.spring.exception">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    <!-- 5、配置自定义异常处理器-->
    <bean class="erer.spring.exception.resolver.MyExceptionResolver"/>
</beans>

3. 编写异常页面

<html>
<head>
    <title>error</title>
</head>
<body>
<h1>通用的错误提示页面</h1>
<h1>${info}</h1>
</body>
</html>

4. 测试异常跳转

package erer.spring.exception.controller;
@Controller
public class DemoController {

    @Resource(name = "demoService")
    private DemoService demoService;

    //请求地址 http://localhost/exception/show01
    @RequestMapping(value = "/show01")
    public String show01() {
        System.out.println("show01 running......");
        demoService.show01();
        return "index";
    }
    //请求地址 http://localhost/exception/show02
    @RequestMapping(value = "/show02")
    public String show02() {
        System.out.println("show02 running......");
        demoService.show02();
        return "index";
    }
    //请求地址 http://localhost/exception/show03
    @RequestMapping(value = "/show03")
    public String show03() throws FileNotFoundException {
        System.out.println("show03 running......");
        demoService.show03();
        return "index";
    }
    //请求地址 http://localhost/exception/show04
    @RequestMapping(value = "/show04")
    public String show04() {
        System.out.println("show04 running......");
        demoService.show04();
        return "index";
    }
    //请求地址 http://localhost/exception/show05
    @RequestMapping(value = "/show05")
    public String show05() throws MyException {
        System.out.println("show05 running......");
        demoService.show05();
        return "index";
    }
}
package erer.spring.exception.service.impl;
public class DemoServiceImpl implements DemoService {
    public void show01() {
        System.out.println("抛出类型转换异常....");
        Object str = "zhangsan";
        Integer num = (Integer)str;
    }

    public void show02() {
        System.out.println("抛出除零异常....");
        int i = 1/0;
    }

    public void show03() throws FileNotFoundException {
        System.out.println("文件找不到异常....");
        InputStream in = new FileInputStream("C:/xxx/xxx/xxx.txt");
    }

    public void show04() {
        System.out.println("空指针异常.....");
        String str = null;
        str.length();
    }

    public void show05() throws MyException {
        System.out.println("自定义异常....");
        throw new MyException();
    }
}

七、面向切面编程AOP

1、概述

1. 什么是AOP

  • AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程
  • 是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • AOP 是 OOP 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型
  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2. AOP 的作用及其优势

  • 作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 优势:减少重复代码,提高开发效率,并且便于维护

3. AOP 的底层实现

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

4. 常用的动态代理技术

  • JDK 代理 : 基于接口的动态代理技术
  • cglib 代理:基于父类的动态代理技术

在这里插入图片描述

2、JDK 代理

1. 目标类接口

package erer.spring.aop.proxy.jdk;
public interface TargetInterface {
    public void save();
}

2. 目标类

package erer.spring.aop.proxy.jdk;
public class Target implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

3. 动态代理代码

package erer.spring.aop.proxy.jdk;
public class Advice {
    public void before(){
        System.out.println("前置增强....");
    }
    public void afterReturning(){
        System.out.println("后置增强....");
    }
}

4. 调用代理对象

public class _JDK的动态代理 {
    @Test
    public void test() {
        //目标对象
        Target target = new Target();
        //增强对象
        Advice advice = new Advice();
        //创建动态生成代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
                target.getClass().getClassLoader(), //目标对象类加载器
                target.getClass().getInterfaces(), //目标对象相同的接口字节码对象数组
                new InvocationHandler() {
                    /**
                     * 调用代理对象的任何方法  实质执行的都是invoke方法
                     * @param proxy 代理对象
                     * @param method 代理对象调用的方法被封装的对象
                     * @param args 代理对象调用方法时,传递的实际参数
                     * @return 动态生成的代理对象
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        advice.before();    //前置增强
                        Object invoke = method.invoke(target, args);    //执行目标方法
                        advice.afterReturning();    //后置增强
                        return invoke;
                    }
                }
        );
        //测试,当调用接口的任何方法时,代理对象的代码都无法修改
        proxy.save();
    }
}

3、cglib 代理

1. 引入依赖

<!-- 引入 junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
    <version>4.13.2</version>
</dependency>
<!-- Spring:集成cglib坐标 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>

2. 目标类

package erer.spring.aop.proxy.cglib;
public class Target {
    public void save() {
        System.out.println("save running.....");
    }
}

3. 动态代理代码

package erer.spring.aop.proxy.cglib;
public class Advice {
    public void before(){
        System.out.println("前置增强....");
    }
    public void afterReturning(){
        System.out.println("后置增强....");
    }
}

4. 调用代理对象

public class _cglib的动态代理 {
    @Test
    public void test() {
        //目标对象
        Target target = new Target();
        //增强对象
        Advice advice = new Advice();
        //创建代理对象
        //1、创建增强器
        Enhancer enhancer = new Enhancer();
        //2、设置父类(目标)
        enhancer.setSuperclass(Target.class);
        //3、设置回调
        enhancer.setCallback(new MethodInterceptor() {
            /**
             * @param proxy 代理对象
             * @param method 代理对象调用的方法被封装的对象
             * @param args 代理对象调用方法时,传递的实际参数
             * @param methodProxy 方法代理对象
             * @return 动态生成的代理对象
             */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                advice.before();    //前置增强
                Object invoke = method.invoke(target, args);    //执行目标方法
                advice.afterReturning();    //后置增强
                return invoke;
            }
        });
        //4、创建代理对象
        Target proxy = (Target) enhancer.create();

        //测试,当调用接口的任何方法时,代理对象的代码都无法修改
        proxy.save();
    }
}

4、AOP 相关概念

  • Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,

  • 封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

  • 常用的术语如下:

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

5、开发明确的事项

1. 需要编写的内容

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

2. 实现的内容

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

3. 代理方式

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

6、基于XML的AOP开发

1. 引入依赖

<!-- 引入 junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <scope>test</scope>
    <version>4.13.2</version>
</dependency>
<!-- Spring:集成cglib坐标 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<!-- aspectj的织入(AOP) -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.7</version>
</dependency>

2. 目标类接口

package erer.spring.aop.aop;
public interface TargetInterface {
    void save();
}

3. 目标类(内部有切点)

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

4. 切面类(内部有增强方法)

package erer.spring.aop.aop;
public class MyAdvice {

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

5. 配置织入关系

  • 将目标类和切面类的对象创建权交给 spring
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
">

    <!-- 1、配置目标对象 -->
    <bean id="target" class="erer.spring.aop.aop.Target"/>
    
    <!-- 2、配置切面对象 -->
    <bean id="myAspect" class="erer.spring.aop.aop.MyAdvice"/>
    
    <!-- 3、配置织入:告诉spring框架,哪些方法(切点)需要进行怎样的增强(前置、后置...)-->
    <aop:config>
        <!-- 声明 myAspect 为切面对象(切点+通知) -->
        <aop:aspect ref="myAspect">
            <!-- 配置 Target 类的 save() 方法执行时要执行 MyAspect 类的 before() 方法进行前置增强 -->
            <!-- before:前置增强、method:前置增强的方法、pointcut:切点表达式 -->
            <aop:before method="before" pointcut="execution(public void erer.spring.aop.aop.Target.save())"/>
        </aop:aspect>
    </aop:config>
</beans>

6. 代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _基于XMLAOP开发 {
    @Autowired
    private TargetInterface target;

    @Test
    // 未增强测试
    public void test01() {
        TargetInterface target = new Target();
        target.save();	//save running.....
    }

    @Test
    // 增强后测试
    public void test02() {
        target.save();
        /**
         * XML配置-前置增强....
         * save running.....
         */
    }
}

7、XML配置AOP详解

1. 切点表达式

  • execution([修饰符] 返回值类型 包名.类名.方法名(参数))
  • execution(public void erer.spring.aop.aop.Target.save())
  1. 访问修饰符可以省略

    • execution(void erer.spring.aop.aop.Target.save())
  2. 返回值类型、包名、类名、方法名可以使用 * 代表任意

    • execution(void erer.spring.aop.aop.Target.*())
    • execution(* erer.spring.aop.aop.*.*()) – 常用
  3. 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类

    • execution(* erer.spring.aop…*.*())
  4. 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

    • execution(* erer.spring.aop…*.*(…))
    • execution(* *…*.*(…))

2. 通知的类型

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

名称标签说明
前置通知aop:before指定增强的方法在切入点方法之前执行
后置通知aop:after-returning指定增强的方法在切入点方法之后执行
环绕通知aop:around指定增强的方法在切入点方法之前和之后都执行
异常抛出通知aop:throwing指定增强的方法在出现异常时执行
最终通知aop:after无论增强方式执行是否有异常都会执行

3. 切点表达式的抽取

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

4. 目标类(内部有切点)

package erer.spring.aop.aop;
public class Target implements TargetInterface {
    @Override
    public void save01() {
        System.out.println("save01 running.....");
    }
    @Override
    public void save02() {
        System.out.println("save02 running.....");
    }
    @Override
    public void save03() {
        System.out.println("save03 running.....");
    }
    @Override
    public void save04() {
        System.out.println("save04 running.....");
        int i = 1/0;
    }
}

5. 切面类(内部有增强方法)

package erer.spring.aop.aop;
public class MyAdvice {

    public void before() {
        System.out.println("XML配置-前置增强....");
    }
    public void afterReturning() {
        System.out.println("XML配置-后置增强....");
    }
    // ProceedingJoinPoint:正在执行的连接点 === 切点
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("XML配置-环绕前增强....");
        // 切点方法
        Object proceed = joinPoint.proceed();
        System.out.println("XML配置-环绕后增强....");
        return proceed;
    }
    public void afterThrowing() {
        System.out.println("XML配置-异常抛出增强....");
    }
    public void after() {
        System.out.println("XML配置-最终增强....");
    }
}

6. 配置织入关系

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

    <!-- 1、配置目标对象 -->
    <bean id="target" class="erer.spring.aop.aop.Target"/>
    
    <!-- 2、配置切面对象 -->
    <bean id="myAspect" class="erer.spring.aop.aop.MyAdvice"/>
    
    <!-- 3、配置织入:告诉spring框架,哪些方法(切点)需要进行怎样的增强(前置、后置)-->
    <aop:config>
        <!-- 声明 myAspect 为切面对象 -->
        <aop:aspect ref="myAspect">
            <!-- 配置 Target 类的 save() 方法执行时要执行 MyAspect 类的 before() 方法进行前置增强 -->
            <!-- before:前置增强、method:前置增强的方法、pointcut:切点表达式 -->
            <aop:before method="before" pointcut="execution(* erer.spring.aop.aop.*.save01())"/>
            <aop:after-returning method="afterReturning" pointcut="execution(* erer.spring.aop.aop.*.save02())"/>
            <aop:around method="around" pointcut="execution(* erer.spring.aop.aop.*.save03())"/>
            <!-- <aop:after-throwing method="afterThrowing" pointcut="execution(* erer.spring.aop..*.*(..))"/>-->
            <!-- <aop:after method="after" pointcut="execution(* erer.spring.aop..*.*(..))"/>-->

            <!-- 抽取 execution 相同的 切点表达式 -->
            <aop:pointcut id="myPointcut" expression="execution(* erer.spring.aop.aop.*.*(..))"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
            <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

7. 代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _XML配置AOP详解 {
    @Autowired
    private TargetInterface target;
    @Test
    // 前置增强
    public void test01() {
        target.save01();
        /**
         * XML配置-前置增强....
         * save01 running.....
         * XML配置-最终增强....
         */
    }
    @Test
    // 后置增强
    public void test02() {
        target.save02();
        /**
         * save02 running.....
         * XML配置-最终增强....
         * XML配置-后置增强....
         */
    }
    @Test
    // 环绕增强
    public void test03() {
        target.save03();
        /**
         * XML配置-环绕前增强....
         * save03 running.....
         * XML配置-最终增强....
         * XML配置-环绕后增强....
         */
    }
    @Test
    // 异常抛出增强
    public void test04() {
        target.save04();
        /**
         * save04 running.....
         * XML配置-最终增强....
         * XML配置-异常抛出增强....
         * java.lang.ArithmeticException: / by zero
         */        
    }
}

8、基于注解的AOP开发

1. 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       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
">
    <!-- 1、组件扫描 -->
    <context:component-scan base-package="erer.spring.aop"/>
    <!-- 2、aop自动代理 -->
    <aop:aspectj-autoproxy/>
</beans>

2. 目标类(内部有切点)

package erer.spring.aop.annotation;
@Component("target")    // 使用在类上用于实例化Bean
public class Target implements TargetInterface {
    @Override
    public void save01() {
        System.out.println("save01 running.....");
    }

    @Override
    public void save02() {
        System.out.println("save02 running.....");
    }

    @Override
    public void save03() {
        System.out.println("save03 running.....");
    }

    @Override
    public void save04() {
        System.out.println("save04 running.....");
        int i = 1/0;
    }
}

3. 切面类(内部有增强方法)

package erer.spring.aop.annotation;
@Component("myAdvice")  // 使用在类上用于实例化Bean
@Aspect //标注 MyAdvice 是一个切面类
public class MyAdvice {

    // 切点表达式的抽取
    @Pointcut("execution(* erer.spring.aop.annotation.*.*(..))")
    public void pointcut(){}

    @Before("execution(* erer.spring.aop.annotation.*.save01())")
    public void before() {
        System.out.println("注解配置-前置增强....");
    }

    @AfterReturning("execution(* erer.spring.aop.annotation.*.save02())")
    public void afterReturning() {
        System.out.println("注解配置-后置增强....");
    }

    // ProceedingJoinPoint:正在执行的连接点 === 切点
    @Around("execution(* erer.spring.aop.annotation.*.save03())")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解配置-环绕前增强....");
        // 切点方法
        Object proceed = joinPoint.proceed();
        System.out.println("注解配置-环绕后增强....");
        return proceed;
    }


    //@AfterThrowing("execution(* erer.spring.aop.annotation.*.*(..))")
    @AfterThrowing("pointcut()")
    public void afterThrowing() {
        System.out.println("注解配置-异常抛出增强....");
    }

    //@After("execution(* erer.spring.aop.annotation.*.*(..))")
    @After("MyAdvice.pointcut()")
    public void after() {
        System.out.println("注解配置-最终增强....");
    }
}

4. 代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")
public class _基于注解的AOP开发 {
    @Autowired
    private TargetInterface target;

    @Test
    // 前置增强
    public void test01() {
        target.save01();
    }

    @Test
    // 后置增强
    public void test02() {
        target.save02();
    }

    @Test
    // 环绕增强
    public void test03() {
        target.save03();
    }

    @Test
    // 异常抛出增强
    public void test04() {
        target.save04();
    }
}

八、声明式事务控制

1、编程式事务控制相关对象

1. 事务管理器

PlatformTransactionManager

  • 方法

    • 获取事务的状态信息:TransactionStatus getTransaction(TransactionDefination defination)
    • 提交事务:void commit(TransactionStatus status)
    • 回滚事务:void rollback(TransactionStatus status)
  • 注意:PlatformTransactionManager 是接口类型,不同的 Dao 层技术则有不同的实现类,例如:

    • Dao 层技术是jdbc 或 mybatis 时:org.springframework.jdbc.datasource.DataSourceTransactionManager
    • Dao 层技术是hibernate时:org.springframework.orm.hibernate5.HibernateTransactionManager

2. 事务的定义信息对象

TransactionDefinition

  • 方法

    • 获得事务的隔离级别:int getIsolationLevel()
    • 获得事务的传播行为:int getPropogationBehavior()
    • 获得超时时间:int getTimeout()
    • 是否只读:boolean isReadOnly()
  • 事务隔离级别:设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。

    • ISOLATION_DEFAULT
    • ISOLATION_READ_UNCOMMITTED
    • ISOLATION_READ_COMMITTED
    • ISOLATION_REPEATABLE_READ
    • ISOLATION_SERIALIZABLE
  • 事务传播行为(A事务调用B事务)

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

3. 事务具体的运行状态

TransactionStatus

  • 方法

    • 是否存储回滚点:boolean hasSavepoint()
    • 事务是否完成:boolean isCompleted()
    • 是否是新事务:boolean isNewTransaction()
    • 事务是否回滚:boolean isRollbackOnly()
  • 事务隔离级别:设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。

    • ISOLATION_DEFAULT
    • ISOLATION_READ_UNCOMMITTED
    • ISOLATION_READ_COMMITTED
    • ISOLATION_REPEATABLE_READ
    • ISOLATION_SERIALIZABLE

2、什么是声明式事务控制

1. 什么是声明式事务控制

  • Spring 的声明式事务顾名思义就是采用声明的方式来处理事务。
  • 这里所说的声明,就是指在配置文件中声明,用在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

2. 声明式事务处理的作用

  • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
  • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

注意:Spring 声明式事务控制底层就是AOP。

3、基于XML的声明式事务控制

1. 引入依赖

<dependencies>
    <!-- 引入 junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- Druid连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
    </dependency>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>
    <!-- Spring 集成 Junit -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
    </dependency>
    <!-- Spring集成jdbc:JdbcTemplate -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
    </dependency>
    <!-- Spring集成tx:事务控制 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>
    <!-- aspectj的织入(AOP) -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- javax -->
    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
    </dependency>
</dependencies>

2. 账户类

package erer.spring.Transaction.XML.domain;
public class Account {
    private String name;
    private double balance;
}

3. 数据库操作

package erer.spring.Transaction.XML.dao.impl;
public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set balance = balance - ? where name = ?", money, outMan);
    }
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set balance = balance + ? where name = ?", money, inMan);
    }
    @Override
    public Account queryByName(String name) {
        return jdbcTemplate.queryForObject("select * from account where name = ?", new BeanPropertyRowMapper<>(Account.class), name);
    }
    @Override
    public void restore(String name) {
        jdbcTemplate.update("update account set balance = ? where name = ?", 5000, name);
    }
}

4. 业务层

package erer.spring.Transaction.XML.service.impl;
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Override
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0;
        accountDao.in(inMan,money);
    }
    @Override
    public Account queryByName(String name) {
        return accountDao.queryByName(name);
    }
    @Override
    public void restore() {
        accountDao.restore("tom");
        accountDao.restore("lucy");
    }
}

5. 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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       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
">

    <!-- 1、加载jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 2、配置数据源DataSource -->
    <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}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
    </bean>
    <!-- 3、注入JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <bean id="accountDao" class="erer.spring.Transaction.XML.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!-- 4、目标对象,内部的方法即为切点 -->
    <bean id="accountService" class="erer.spring.Transaction.XML.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 5、配置平台事务管理器:通过 DataSource 获取 Connection 进行事务控制 -->
    <!-- 如果 DAO 层的实现不是使用 Jdbc 的模板,class 可能要变 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 6、通知\事务增强配置 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 设置事务的属性信息 -->
        <tx:attributes>
            <!-- 哪些方法被增强 -->
            <!-- name:切点方法名(*任意方法)、isolation:事务的隔离级别、propagation:事务的传播行为、timeout:超时时间、read-only:是否只读  -->
            <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
        </tx:attributes>
    </tx:advice>

    <!-- 7、配置事务织入:事务的aop增强-->
    <aop:config>
        <!-- pointcut:切点表达式 -->
        <aop:pointcut id="myPointcut" expression="execution(* erer.spring.Transaction.XML.service.impl.*.*(..))"/>
        <!-- advisor:切面对象、但只有一个通知(类似 aspect)、 advice-ref:通知引用 、pointcut-ref:切点表达式 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>
</beans>

6. 代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _基于XML的声明式事务控制 {
    @Autowired
    private AccountService accountService;

    @Test
    public void test01() {
        // 复原
        accountService.restore();
        // 转账
        accountService.transfer("lucy", "tom", 500);
    }
    @Test
    public void test() {
        System.out.println("lucy:"+ accountService.queryByName("lucy").getBalance());
        System.out.println("tom:"+ accountService.queryByName("tom").getBalance());
    }
}

4、切点方法的事务参数的配置

<!-- 通知,事务增强配置 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 设置事务的属性信息 -->
    <tx:attributes>
        <!-- 哪些方法被增强 -->
        <!-- name:切点方法名(*任意方法)、isolation:事务的隔离级别、propagation:事务的传播行为、timeout:超时时间、read-only:是否只读  -->
        <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>
    </tx:attributes>
</tx:advice>

5、基于注解的声明式事务控制

1. 账户类

package erer.spring.Transaction.annotation.domain;
public class Account {
    private String name;
    private double balance;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
}

2. 数据库操作

package erer.spring.Transaction.annotation.dao.impl;
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Resource(name = "jdbcTemplate")
    private JdbcTemplate jdbcTemplate;

    public void out(String outMan, double money) {
        jdbcTemplate.update("update account set balance = balance - ? where name = ?", money, outMan);
    }
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set balance = balance + ? where name = ?", money, inMan);
    }
    @Override
    public Account queryByName(String name) {
        return jdbcTemplate.queryForObject("select * from account where name = ?", new BeanPropertyRowMapper<>(Account.class), name);
    }
    @Override
    public void restore(String name) {
        jdbcTemplate.update("update account set balance = ? where name = ?", 5000, name);
    }
}

3. 业务层

package erer.spring.Transaction.annotation.service.impl;
@Service
// 该类下的所有方法都使用同一套注解参数配
// @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0;
        accountDao.in(inMan,money);
    }
    @Override
    public Account queryByName(String name) {
        return accountDao.queryByName(name);
    }
    @Override
    public void restore() {
        accountDao.restore("tom");
        accountDao.restore("lucy");
    }
}

4. 配置文件

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       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
">
    <!-- 1、组件扫描-->
    <context:component-scan base-package="erer.spring.Transaction.annotation"/>

    <!-- 2、加载jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 3、配置数据源DataSource -->
    <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}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
    </bean>
    <!-- 4、注入JdbcTemplate-->
    <bean id="jdbcTemplate-anno" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 5、配置平台事务管理器 通过 DataSource 获取 Connection 进行事务控制 -->
    <!-- 如果 DAO 层的实现不是使用 Jdbc 的模板,class 可能要变 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 6、事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

5. 代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _基于注解的声明式事务控制 {
    @Autowired
    private AccountService accountService;
    @Test
    public void test01() {
        // 复原
        accountService.restore();
        // 转账
        accountService.transfer("lucy", "tom", 1000);
    }
    @Test
    public void test() {
        System.out.println("lucy:"+ accountService.queryByName("lucy").getBalance());
        System.out.println("tom:"+ accountService.queryByName("tom").getBalance());
    }
}
 + ? where name = ?", money, inMan);
    }
    @Override
    public Account queryByName(String name) {
        return jdbcTemplate.queryForObject("select * from account where name = ?", new BeanPropertyRowMapper<>(Account.class), name);
    }
    @Override
    public void restore(String name) {
        jdbcTemplate.update("update account set balance = ? where name = ?", 5000, name);
    }
}

3. 业务层

package erer.spring.Transaction.annotation.service.impl;
@Service
// 该类下的所有方法都使用同一套注解参数配
// @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public class AccountServiceImpl implements AccountService {

    @Resource(name = "accountDao")
    private AccountDao accountDao;

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double money) {
        accountDao.out(outMan,money);
        int i = 1/0;
        accountDao.in(inMan,money);
    }
    @Override
    public Account queryByName(String name) {
        return accountDao.queryByName(name);
    }
    @Override
    public void restore() {
        accountDao.restore("tom");
        accountDao.restore("lucy");
    }
}

4. 配置文件

<?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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       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
">
    <!-- 1、组件扫描-->
    <context:component-scan base-package="erer.spring.Transaction.annotation"/>

    <!-- 2、加载jdbc.properties -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 3、配置数据源DataSource -->
    <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}"/>
        <property name="initialSize" value="${jdbc.initialSize}"/>
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <property name="maxWait" value="${jdbc.maxWait}"/>
    </bean>
    <!-- 4、注入JdbcTemplate-->
    <bean id="jdbcTemplate-anno" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 5、配置平台事务管理器 通过 DataSource 获取 Connection 进行事务控制 -->
    <!-- 如果 DAO 层的实现不是使用 Jdbc 的模板,class 可能要变 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 6、事务的注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

5. 代码测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class _基于注解的声明式事务控制 {
    @Autowired
    private AccountService accountService;
    @Test
    public void test01() {
        // 复原
        accountService.restore();
        // 转账
        accountService.transfer("lucy", "tom", 1000);
    }
    @Test
    public void test() {
        System.out.println("lucy:"+ accountService.queryByName("lucy").getBalance());
        System.out.println("tom:"+ accountService.queryByName("tom").getBalance());
    }
}
  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值