黑马程序员2022新版SSM框架Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术——万字学习笔记:Spring+SpringMVC篇

学习目标:

提示:这里可以添加学习目标

例如:

  • 一周掌握 Java 入门知识

学习内容:

提示:养成自学好习惯和攻克难关能力

学习Spring+SpringMVC


学习时间:

提示:找了份单休的工作边打工边学习,花了一个半月的时间学到这里

例如:

  • 周二至周五早上 7 点-早上 9点, 晚上 10 点-晚上 1 点
  • 周六晚上 10 点-晚上 12 点
  • 周日下午 10 点-下午 12 点
  • 周一早上 10 点-下午1点,下午2点到晚上8点

学习产出:

提示:代码行数不记得

例如:

  • 技术笔记1.6万字的笔记一篇

Spring

为什么要学Spring?

1.专业角度:

  • 简化开发,降低企业级开发的复杂性

  • 框架整合,高效整合其他计算,提高企业级应用开发与运行效率

2.学什么?

  • 简化开发
    • IOC
    • AOP
      • 事务处理
  • 框架整合
    • MyBatis
    • MyBatis-plus
    • Struts
    • Struts2
    • Hibernate

Spring Framework系统架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-onTECLoY-1669819301997)(https://ts1.cn.mm.bing.net/th/id/R-C.40d21cdfc59cb8a652614cc7cfe0fd87?rik=4Bx8Us0bCDQ0FQ&riu=http%3a%2f%2fstatic.oschina.net%2fuploads%2fspace%2f2014%2f1231%2f111732_x0O8_1249631.png&ehk=nRnxwQgIJeO4XeIMAlfz2p7qAZwAyNSQ8Y%2fJoPmvIeI%3d&risl=&pid=ImgRaw&r=0)]

  • Core Container:核心容器
  • Aspects:AOP思想实现
  • AOP:面向切面
  • Data Integration:数据集成
  • Data Access:数据访问
  • Test:单元测试与集成测试

大纲

第一部分:核心容器

  • 核心概念(IoC/DI)
  • 容器基本操作

第二部分:整合

  • 整合数据层技术MyBatis

第三部分:AOP

  • 核心概念
  • AOP基础操作
  • AOP实用开发

第四部分:事务

  • 事务开发

第五部分:家族

  • SpringMVC
  • SpringBoot
  • SPringCloud

核心容器

1.核心概念

  • IoC(Inversion of Control)控制反转
    • 使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转
  • Spring技术对IoC思想进行了实现
    • 由Spring提供了一个容器,称为IoC容器,来充当IoC思想中的"外部"
    • IoC容器负责对象的创建,初始化等一系列,被创建或被管理的对象在IoC容器中统称为Bean
  • DI(Dependency Injection)依赖注入
    • 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
  • 目标:充分解耦
    • 使用IoC容器管理bean(IoC)
    • 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
  • 最终效果
    • 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系

2.IoC入门案例

  1. 管理什么?(Service与Dao)

  2. 如何将被管理的对象告知IoC容器?(配置)【将bean注册到application.xml里面】【通过xml管理IoC容器】

  3. 被管理的对象交给IoC容器,如何获取到IoC容器?(接口)

    一般,通过ApplicationContext接口中的实现类ClassPathXmlApplicationContext()

  4. IoC容器得到后,如何从容器中获取bean?(接口方法)

    通过ApplicationContext接口中的getBean方法

  5. 使用Spring导入哪些坐标?(pom.xml,在depencies里面导入坐标)

    
    <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-contextartifactId>
                <version>5.2.10.RELEASEversion>
    dependency>
    
    
//步骤2:定义Spring管理的类(接口)
public interface BookService{
	void save();
}
//实现接口
public class BookServiceImpl implements BookService{
	private BookDao bookDao = new BookDaoImpl();
	public void save(){
		bookDao.save();
	}
}


<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="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl"/>
    

3.DI入门案例

  1. 基于IoC管理bean
  2. Service中使用new形式创建的Dao对象是否保留?(否)
  3. Service中需要的Dao对象如何进入到Service中?【使用依赖注入】(提供方法)
  4. Service与Dao间的关系如何描述?(配置)【一个业务层可以控制很多数据层】
//步骤1:将业务层中的new的DAO对象删除,使用set方法实现

public class BookServiceImpl implements BookService{
	private BookDao;
	public void save(){
		bookDao.save();
	}
    //提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
        System.out.println("提供了对应的set方法");
    } 
}

<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
	
    <property name="bookDao" ref="bookDao"/>
bean>

4.Bean的配置

Bean的别名

在application.xml起别名,或取多个别名,【用空格,或者逗号,或者分号分隔开来】

ref属性既可以引用id也可以引用name

<bean id="bookDao" name="dao dao2" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" name="service" class="com.hcx.service.impl.BookServiceImpl">
	
    <property name="bookDao" ref="dao"/>
bean>
public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService= (BookService) context.getBean("service");
        bookService.save();
    }
//把getBean方法里的名称换为bean的别名也是可以的

注意:获取bean无论是通过id还是name获取,如果无法获取到,将抛出NoSuchBeanDefinitionException

Bean的作用范围【单例或非单例】

在application.xml中配置bean的作用范围

属性:scope

所属:bean标签

功能:定义bean的作用范围:

  • singleton:单例(默认)
  • prototype:非单例

范例:

<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl" scope="prototype"/>
  • 为什么bean默认的是单例的?

​ —便于管理复用对象,提高效率

  • 适合交给容器进行管理的bean

    • 封装实体的域对象(有状态的)

Bean的实例化方法

  • Bean本质上就是对象,创建bean使用构造方法完成
方法一:构造方法实现Bean
//首先在application中注册bean
//其次在实现类中实现构造方法实例化实现类
private BookDaoImpl(){
	System.out.println("Book is constructor is running ...");
}
//无论是使用private还是public都能访问到构造方法,利用到了反射
//无参构造方法是默认实现

<bean id="bookkDao" class="com.hcx.dao.impl.BookDaoImpl"/>
方法二:使用静态工厂
//新建一个静态工厂
public class OrderDaoFactory{
public static OrderDao getOrderDao(){
    return new OrderDaoImpl();
	}
}
//实例化对象时getBean()方法调用静态工厂实例化对象
<bean id="orderDao" class="com.hcx.factory.OrderDaoFactory" factory-method="getOrderDao"/>
方法三:实例工厂
public class AppForInstanceUser{
	public static void main(String[] args){
        //创建实例静态工厂对象
        UserDaoFactory userDaoFactroy = new UserDaoFactory();
        //通过实例工厂对象创建对象
        User userDao = userDaoFactory.getUserDao();
        userDao.save();
    }
}

public class UserDaoFactory{
    public UserDao getUserDao(){
        retrun new UserDaoImpl();
    }
}
<bean id="userFactory" class="com.hcx.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>
方式四:FactoryBean
public class UserDaoFactoryBean implements FactoryBean<UserDao>{
    public UserDao getObject() throws Expection{
        return new UserDaoImpl();
    }
    public Class<?> getObjectType(){
        return UserDao.class;
    }
}
<bean id="userDao" class="com.hcx.factory.UserDaoFactoryBean"/>

Bean的生命周期

  • 生命周期:从创建到消亡的完整过程
  • bean生命周期:bean从创建到销毁的整体过程
  • bean生命周期控制:在bean创建后到销毁前做一些事情
public void init(){
    System.out.print("Bean初始化操作");
}
public void destory(){
    System.out.print("Bean销毁操作");
}
<bean id="bookDao" class="com.hcx.impl.BookDaoImpl" init-method="init" destory-method="destory"/>
//无法调用销毁方式原因,JVM直接退出,未执行关闭ClassPathXmlApplicationContext对象
eg:
// 不用 ApplicationContext接口下ClassPathXmlApplication实现类型下的close()方法 ,还可以使用使用ApplicationContext下的实现类AnnotationConfigApplicationContext()实现下的close()方法
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao =(BookDao) context.getBean("bookDao");
//暴力方法
context.close();
//灵巧方法
context.registerShutdownHook();
//bean生命周期,标准实现方式,实现InitializingBean, DisposableBean两个接口中的方法
eg:
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    //    删除业务层中使用new的方式创建dao对象
//    private BookDao bookDao = new BookDaoImpl();
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }

    //提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
        System.out.println("提供了对应的set方法");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("service destory");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
}

总结

bean生命周期

  • 初始化容器

    1. 创建对象(内存分配)
    2. 执行构造方法
    3. 执行属性注入(set操作)
    4. 执行bean初始化方法
  • 使用bean

    1. 执行业务操作
  • 关闭/销毁容器

    1. 执行bean销毁方法

    bean销毁时机

    • 容器关闭前触发bean的销毁

    • 关闭容器方式

      • 手工关闭容器

        ConfigurableApplicationContext接口close()操作

      • 注册关闭钩子,在虚拟机退出前线关闭容器再退出虚拟机

        ConfigurableApplicationContext接口registerShutdownHook()操作

    控制bean生命周期的两种方法
  • 配置

    • init-method
    • destory-method
  • 接口

    • InitializingBean

    • DisposableBean

依赖注入方式

  • 思考:向一个类中传递数据的方式有几种?
    • 普通方法(set方法)
    • 构造方法
  • 思考:依赖注入描述了在容器中建立bean与bean之间依赖关系的过程,如果bean运行需要的是数字或字符串呢?
    • 引用类型
    • 简单类型(基本数据类型与String)
  • 依赖注入方式
    • setter注入
      • 简单类型
      • 引用类型
    • 构造器注入
      • 简单类型
      • 引用类型

Setter注入——引用类型

  • 在bean中定义引用类型属性并提供可访问的set方法

    public class BookServiceImpl implements BookService(
    	private BookDao bookDao;
        public void setBookDao(BookDao bookDao){
            this.bookDao = bookDao;
        }
    )
    
  • 配置中使用property标签****value属性注入引用类型对象

    <bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
    	<property name="bookDao" ref="bookDao"/>
    bean>
    <bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
    

Setter注入——简单类型

  • 在bean中定义简单类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao(
	private int connectionNum;
	private String databaseName;
	public void setConnectionNum(int connectionNum){
        this.connectionNum=connectionNum;
    }
    public void setDatabaseName(String databaseName){
        this.databaseName=datanaseName;
    }
)
  • 配置中使用property标签****value属性注入基本类型对象
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
	<property name="connectionNum" value="100"/>
    <property name="databaseName" value="MySql"/>
bean>

构造器注入——引用类型

  • 在bean中定义引用类型属性并提供可访问的构造方法
public class BookServiceImpl implements BookService(
	private BookDao bookDoa;
    public BookServiceImpl(BookDao bookDao){
        this.bookDao = bookDao;
    }
)
   
  • 配置中使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl">
	<constructor-arg name="bookDao" ref="bookDao"/>
bean>
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>

构造器注入——简单类型

  • 在bean中定义引用类型属性并提供可访问的set方法
public class BookDaoImpl implements BookDao{
	private int connectionNumber;
    public BookDaoImpl(int connectionNumber){
   		this.connectionNumber = connectionNumber;
    }
}
  • 在配置中使用constructor-arg标签value属性注入简单数据类型
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
	<constructor-arg name="connectionNumber" value="10"/>
bean>

参数设置

  • 配置中使用constructor-arg标签type属性设置按形参类型注入
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
	<constructor-arg type="int" value="10"/>
	<constructor-arg type="java.lang.String" value="mysql"/> 
bean>
  • 配置中使用constructor-arg标签index属性设置按形参位置注入
<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl">
	<constructor-arg index="0" value="10"/>
	<constructor-arg index="1" value="MYSQL"/>
bean>

依赖注入方式选择

  1. 强制依赖使用构造器进行,使用setter注入有概率步进行注入导致null对象出现
  2. 可选依赖使用setter注入进行,灵活性能强
  3. Spring框架倡导使用构造器,第三方框架内大多数采用构造器注入的形式进行数据初始化,相对严谨
  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选项依赖的注入
  5. 实际开发过程中还要更卷实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
  6. 自己开发的模块推荐使用setter注入

依赖自动装配

  • IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称之为自动装配

  • 自动装配方式

    • 按类型(常用)
    • 按名称
    • 按构造方法
    • 不启用自动装配
  • 配置中使用bean标签autowire属性设置自动装配的类型

<bean id="bookDao" class="com.hcx.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.hcx.service.impl.BookServiceImpl" autowire="byType"/>
  • 自动装配用于引用类型依赖注入,不能对简单类型进行操作
  • 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
  • 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名和配置耦合,不推荐使用
  • 自动装配优先级低于setter注入和构造器注入,同时出现时自动装配失效

集合注入

数据类型:

数组

<property name="array">
	<array>
		<value>100value>
		<value>200value>
		<value>300value>
	array>
property>

List

<property name="list">
            <list>
                <value>加油value>
                <value>每天value>
                <value>学习value>
            list>
property>

Set

<property name="set">
            <set>
                <value>好好value>
                <value>工作value>
                <value>跑路value>
            set>
property>

Map

<property name="map">
            <map>
                <entry key="country" value="china">entry>
                <entry key="city" value="成都">entry>
                <entry key="province" value="四川">entry>
            map>
property>

Properties

<property name="properties">
            <props>
                <prop key="country">中国prop>
                <prop key="city">南昌prop>
                <prop key="province">江西prop>
            props>
property>

案例:数据源对象管理

  • 导入druid坐标

    <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druidartifactId>
                <version>1.1.10version>
    dependency>
    
  • 配置数据源对象作为spring管理的bean


    <bean id="dataSource"  class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/message"/>
        <property name="username" value="root"/>
        <property name="password" value="505489"/>
    bean>
  • 导入c3p0坐标和mysql连接坐标
 <dependency>
            <groupId>c3p0groupId>
            <artifactId>c3p0artifactId>
            <version>0.9.1.2version>
 dependency>
 <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.6version>
 dependency>
  • 配置数据源对象作为spring管理的bean

    
        
        
        
        
    
  • 加载properties文件
    1. 首先开启命名空间
<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
">
beans>

​ 2.使用context空间加载properties文件

<context:property-placeholder location="jdbc.properties"/>

​ 3.使用属性占位符${}读取properties文件中的属性

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.Driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
bean>

加载properties文件

  • 不加载系统属性

  • 加载多个properties文件
<context:property-placeholder location="jdbc.properties,msg.properties"/>
  • 加载所有properties文件
<context:property-placeholder location="*.properties"/>
  • 加载properties文件标准格式
<context:property-placeholder location="classpath:*.properties"/>
  • 从路径或jar包中搜索properties文件
<context:property-placeholder location="classpath*:*.properties"/>

容器

容器创建(顶级父类BeanFactory接口)

  • 方式一:路径加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
  • 方式二:文件路径加载配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\applicationContext.xml");
  • 方式三:读取web下的资源文件
XmlWebApplicationContext ctx = new XmlWebApplicationContext();
  • 加载多个配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml"); 

获取bean

  • 方式一:使用bean名称获取
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
  • 方式二:使用bean名称获取并指定类型
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
  • 方式三:使用bean类型获取
BookDao bookDao = ctx.getBean(BookDao.class);

容器层次结构

总览 BeanFactory 体系,按照接口的抽象层次,大体可以分层四层:

  • 第一层:BeanFactory
  • 第二层:HierarchicalBeanFactoryListableBeanFactoryAutowireCapableBeanFactory
  • 第三层:ConfigurableBeanFactory,此外还有一个关联性较强SingletonBeanRegistry
  • 第四层:ConfigurableListableBeanFactory

BeanFactory初始化

  • 类路径加载配置文件
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao =  bf.getBean("bookDao",BookDao.class);
bookDao.save();

注意:BeanFactory创建完毕后,所有的bean均为延时加载

延时加载好处:当为了追求传输效率就会需要什么就再去创建什么时,就会体现出延迟加载的好处,有一个缓冲时间。

核心容器总结

容器相关

  • BeanFactory是IoC容器的顶层接口,初始化BeanFactory对象时,加载的bean延时加载

  • ApplicationContext接口是Spring容器的核心接口,初始化时bean立即加载

  • ApplicationContext接口提供基础的bean操作相关方法,通过其他接口扩展其功能

  • ApplicationContext接口常用初始化类

    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext

bean相关

<bean
	id="bookDao"														 bean的Id
	name="dao bookDaoImpl daoImpl"										   bean的别名
	class="com.itheima.dao.impl.BookDaoImpl"							    bean的类型,静态工厂,FactoryBean类
	scope="singleton" 													  控制bean的实例数量
	init-method="init"													  生命周期初始化方法
	destory-method="destory"											   生命周期销毁方法
	autowire="byType"													  自动装配类型
	factory-method="getInstance"										   bean工厂方法,应用于静态工厂或实例工厂
	factory-bean="com.itheima.factory.BookDaoFactory"						 实例工厂bean
	lazy-init="true"													  控制bean延时加载
	/>

依赖注入

<bean id="bookSerbice" class="com.hcx.impl.BookServiceImpl">
	<constructor-arg name="bookDao" ref="bookDao"/>							构造器注入引用类型
    <constructor-arg name="userDao" ref="userDao"/>							 
    <constructor-arg name="msg" value="WARN"/>								构造器注入简单类型	
    <constructor-arg type="java.lang.String" index="3" value="WARN"/> 		  类型匹配与索引匹配
    <property name="bookDao" ref="bookDao"/>								setter注入引用类型
    <property name="userDao" ref="userDao"/>								
    <property name="msg" value="WARN"/>										setter注入简单类型
    <property name="names">												   list集合
    	<list>
        	<value>itcastvalue>										   集合注入简单类型
            <ref bean="dataSource"/>									    集合注入引用类型
        list>
    property>
bean>

注解开发

注解开发定义bean

  • 使用@Component定义bean
@Component("bookDao")
public class BookDaoImpl implements BookDao{
}
@Component
public class BookServiceImpl implements BookService{
}
  • 核心配置文件中通过组件扫描加载bean
<context:component-scan base-package="com.hcx"/>
  • Spring提供@Component注解的三个衍生注解
    • @Controller:用于表现层bean定义
    • @Service:用于业务层bean定义
    • @Repository:用于数据层bean定义
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
}
@Service
public class BookServiceImpl implements BookService{
}

Spring 3.0 纯注解开发

  • Spring 3.0 升级了纯注解开发模式,使用了Java类代替配置文件,开启了Spring快速开发赛道
  • Java类代替Spring核心配置文件,

<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.springframework.org/schema/beans/spring-beans.xsd">
	   <context:component-scan base-package="com.hcx"/>
beans>

@Configuration
@ComponentScan("com.hcx")
public class SpringConfig{
}

代替

  • @Configuration注解用于设定当前类为配置类
  • @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
@ComponentScan({"com.hcx.service","com.hcx.dao"})
  • 读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象
//加载配置文件初始化容器(xml版)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//加载配置类初始化容器
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

总结

  1. 定义bean

    • @Component

      • @Controller

      • @Service

      • @Repository

  2. 纯注解开发

    • @Configuration
    • @ComponentScan
    • AnnotationConfigApplicationContext

注解开发bean管理

  1. bean的作用范围
    • @Scope
  2. bean的生命周期
    • @PostConstruct
    • @PreDestroy

注解开发依赖注入

自动装配

引用类型注入
  • 使用@Autowired注解开启自动装配模式(按类型)
@Servcie
public class BookServiceImpl implements BookService{
	@Autowired
	private BookDao bookDao;
	public void setBookDao(BookDao bookDao){
	this.bookDao=bookDao;
	}
	public void save(){
	System.out.print("book service save ...");
	bookDao.save();
	}
}
  • 注意:自动装配基于反射设计创建对象并暴力反射对应属性初始化数据,因此无需提供setter()方法

  • 注意:自动装配建议使用无参构造方法创建对象(默认),如果不提供对应构造方法,请提供唯一的构造方法

  • 使用@Qualifier注解开启指定名称装配bean

@Service 
public class BookServiceImpl implements BookService{
	@Autowired
	@Qualifier("bookDao")
	private BookDao bookDAO;
}
  • 注意:@Qualifier注解无法单独使用,必须配合@Autowired注解使用
简单数据类型注入

在定义的数据类型上放使用@Value("值")

@Value("学习")
private String action;

加载外部properties文件

  • 在SpringConfig类下,使用@PropertySource注解加载properties文件
@Configuration
@ComponentScan("com.hcx")
@uPropertySource("classpath:jdbc.properties")
public class SpringConfig{}
  • 在给属性注入值时
@Value("${外部文件中定义的属性名}")
private String value;
  • 注意:路径仅支持单一文件配置,多文件请使用数组格式配置,不允许使用通配符*
总结回顾
  1. 自动装配
    • @Autowired:自动装配暴力映射
    • @Qualifier:指定名称注入
    • @value:注入简单类型
  2. 读取properties文件
    • @PropertySource:在SpringConfig里读取外部文件

第三方bean管理

第三方bean配置
  • 使用@Bean配置第三方bean
@Configuration
public class SpringConfig{
	@Bean
	public DataSource dataSource(){
    DruidDataSource ds = new DruidDataSource();
	ds.setDriverClassName("com.mysql.jdbc.Driver");
	ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
	ds.setUsername("root");
	ds.setPassword("root");
	return ds;
	}
}
//不推荐
  • 将独立的配置类加入核心配置
  • 方式一:导入式
public class JdbcConfig{
	@Bean
	public DataSource dataSource(){
	DruidDataSource ds = new DruidDataSource();
	//相关配置
	return ds;
	}
}
  • 使用@Import注解手动加入配置类到核心配置,此注解只能添加一次,多个数据请用数组格式
@Configuration
@Import(JdbcConfig.class)
public class SpringConfig{
}
第三方bean依赖注入
  • 简单类型依赖注入
public class JdbcConfig{
	@Value("com.mysql.jdbc.Driver")
	private String dirver;
	@Value("jdbc:mysql://localhost:3306/spring_db")
	private String url;
	@Value("root")
	private String userName;
	@Value("505489")
	private String password;
	@Bean
	public DataSource dataSource(){
		DruidDataSource ds = new DruidDatSource();
		ds.setDriverClassName(driver);
		ds.setUrl(url);
		ds.setUserName(userName);
		ds.setPassword(password);
		return ds;
	}
}
  • 引用类型依赖注入
@Bean
public DataSource dataSource(BookService bookService){
	Sytstem.out.print(bookService);
	DruidDataSource ds = new DruidDataSource();
	//属性设置
	return ds;
}
  • 引用类型注入只需要为bean定义方法设置形参即可,容器会更卷类型自动装配对象

总结

第三方Bean总结
  1. 第三方bean管理
    • @Bean
  2. 第三方依赖注入
    • 引用类型:方法形参
    • 简单类型:成员变量
XML配置对比注解配置
功能 XML配置 注解
定义bean bean标签:id属性 class属性 @ComponentScan @Component:@Controller @Service @Repository
设置依赖注入 setter注入(set方法):引用/简单 构造器注入(构造方法):引用/简单 自动装配 @Autowired:@Qualifier @Value
配置第三方bean bean标签 静态工厂,实例工厂,FactoryBean @Bean
作用范围 scope属性 @Scope
生命周期 标准接口:init-method destory-method @PostConstryctor @ProDestory

Spring整合MyBatis

xml方式实现
  • MyBatis程序核心对象分析
//1.创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilde();
//2.加载SqlMapConfig.xml配置文件
InpiyStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//3.创建SqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
/*初始化SqlSessionFactroy*/
//4.获取SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//5.执行SqlSession对象执行查询,获取结果User
AccountDao accountDao = sqlSession.getMapper(AccountDao.class);
/*获取实现*/
Account ac = accountDao.findById(2);
System.out.printIn(ac);
/*获取数据层接口*/
//6.释放资源
sqlSession.close();
/*关闭连接*/
  • 整合MyBatis
<configuration>
    
    <properties resource="jdbc.properties">properties>
    
    <typeAliases>
        <package name="com.itheima.domain"/>
    typeAliases>
    
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC">			               <dataSource type="POOLED">
            	<property name="driver" value="${jdbc.driver}">property>
                <property name="url" value="${jdbc.url}">property>
                <property name="username" value="${jdbc.username}">property>
                <property name="password" value="${jdbc.password}">property>
            dataSource>
            transactionManager>
        environment>
    environments>
    
    <mappers>
    	<package name="com.itheima.do">package>
    mappers>
configuration>
注解方式实现
  • 注册SqlSessionFactoryBean类,并配置数据库
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
	SqlSessionFactryBean ssfb = new SqlSessionFactoryBean();
    ssfb.setTypeAliasePackage("com.hcx.pojo");
    ssfb.setDataSource(dataSource);
    return ssfb;
}
  • 配置映射包
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
	MapperScannerConfigurer msc = new MapperScannerConfigurer();
	msc.setBasePackage("com.hcx.dao");
	return msc;
}
整合JUnit
测试接口(一般测业务层,很少测数据层)
  • 使用Spring整合Junit专业类加载器
@RunWith(SpringJUnit4ClassRunner.class)
@ConetextConfiguration(classes = SpringConfig.class)
public Class BookServiceTest{
	@Autowired
	private BookService bookService;
	
	@Test
	public void testSave(){
		bookservice.save();
	}
}

AOP

AOP简介

  • AOP(Aspect Oriented Programming)面向切面编程,是一种编程范式,指导开发者如何组织程序结构
    • OOP(Object Oriented Programming)面向对象编程
  • 作用:在步惊动原始设计的基础上进行功能增强,追加功能
  • Spring理念:无侵入式

AOP核心概念

  • 连接点(JoinPoint):程序执行过程中的任意位置,粒度为执行方法,抛出异常,设置变量等
    • 在SpringAOP中,理解为方法的执行
  • 切入点(Pointcut):匹配连接点的式子
    • 在SpringAop中,一个切入点可以只描述一个具体方法,也可以匹配多个方法
      • 一个具体方法:com.hcx.dao包下的BookDao接口中的无形参返回值的save方法
      • 匹配多个方法,所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法
  • 通知(Advice):在切入点处执行的操作,也就是共性功能
    • 在Spring中,功能最终以方法的形式呈现
  • 通知类:定义通知的类
  • 切面(Aspect):描述通知与切入点的对应关系

AOP入门案例(注解版)

  1. 导入aop相关坐标
<dependency>
    <groupId>org.aspectjgroupId>
    <artifactId>aspectjweaverartifactId>
    <version>1.9.4version>
dependency>	

说明:Spring-context坐标依赖spring-aop坐标

2.定义dao接口与实现类

public interface BookDao{
	public void save();
	public void update();
}
@Repository
public class BookDaoImpl implements BookDao{
	public void save(){
		System.out.println(System.currentTimeMills());
		System.out.println("book dao save...");
	}
	
	public void update(){
		System.out.println("book dao update...");
	}
}

3.定义通知类,制作通知

public class MyAdvice{
	public void before(){
	System.out.println(System.currentTimeMills());
	}
}

4.定义切入点

public class MyAdvice{
	@Pointcut("execution(void com.hcx.dao.BookDao.update())")
	private void pt(){}
}

说明:切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑

5.绑定切入点与通知关系,并指定通知添加到原始连接点的具有执行位置

public class MyAdvice{
	@Pointcut("execution(void com.itheima.dao.BookDao.update())")
    private void pt(){}
    
    @Before("pt()")
    public void before(){
        System.out.println(System.currenTimeMillis());
    }
}

6.定义通知类受Spring容器管理,并定义当前类为切面类

@Componet
@Aspect
public class MyAdvice{
	@Pointcut("execution(void com.hcx.dao.BookDao.update())")
	private void pt(){}
	@Before("pt()")
	public void before(){
		System.out.println(System.currentTimeMillis());
	}
}

7.开启Spring对AOP注解驱动支持

@Configuration
@ComponetScan("com.hcx")
@EnableAspectAutoProxy
public class SpringConfig{
}

AOP工作流程

1.Spring容器启动

2.实现所有切面配置中的切入点

@Component
@Aspect
public class MyAdvice{
@Pointcut("excution(void com.hcx.dao.BookDao.save())")
private void ptx(){}

@Pointcut("excution(void com.hcx.dao.BookDao.update())")
private void pt(){}

@Before("pt()")
public void method(){
	System.out.println(System.currentTimeMillis());
	}
}

3.初始化bean,判定bean对应的类中的方法是否匹配到任意切入点

  • 匹配失败,创建对象
  • 匹配成功,创建原始对象(目标对象)的代理对象

4.获取bean执行方法

  • 获取bean,调用方法并执行,完成
  • 获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作

AOP核心概念

  • 目标对象(Target):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的
  • 代理(Proxy):目标对象无法直接工作,需要对其进行功能会填,通过原始对象的代理对象实现
  • SpringAOP本质:代理模式

AOP切入表达式

  • 切入点:要进行增强的方法
  • 切入点表达式:要进行增强方法的描述方式
public Interface BookDao{
	public void update();
}
public class BookDaoImpl implements BookDao{
		public void update(){
			System.out.println("book dao update ...");
		}
}

描述方式一:执行com.hcx.dao包下的BookDao接口中的无参数update方法

execution(void com.hcx.dao.BookDao.update())

描述方法二:执行com.hcx.dao.impl包下的BookDaoImpl类中的无参数update方法

execution(void com.hcx.iml.BookDaoImpl.update())

  • 切入点表达式标准格式:动作关键字(访问修饰符,返回值,包名,类/接口.方法名(参数)异常名)

    execution(public User com.hcx.service.UserService.findById(int))
    
  • 动作关键字:描述切入点的行为动作,例如excution表示执行到指定切入点

  • 访问修饰符:public,private 等,可以省略

  • 返回值

  • 包名

  • 类/接口名

  • 方法名

  • 参数

  • 异常名:方法定义中抛出异常,可以省略

AOP通知类型

  • AOP通知描述了抽取的共性功能,根据共性功能抽取位置不同,最终运行代码时要将其加入到合理的位置

  • AOP通知分为5种类型

    • 前置通知
    • 后置通知
    • 环绕通知(重点)
    • 返回通知(了解)
    • 抛出执行异常后通知(了解)
  • 名称:@Around(重点,常用)

  • 类型:方法注释

  • 位置:通知方法定义上方

  • 作用:设置当前通知方法与切入点之间的绑定关系,当通知方法在原始切入点方法前后运行

  • 范例:

    @Around("pt()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println("around before advice ...");
    Object ret = pjp.proceed();
    System.out.println("arround after advice ...");
    return ret;
    }
    
    • @Around注意事项

      1. 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
      2. 通知中如果为使用ProceedIngJoinPoint对原始方法进行调用将跳过原始方法的执行
      3. 对原始方法的调用可以不接收返回值,通知方法设置称void即可,如果接收返回值,必须设定为Object类型
      4. 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
      5. 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象
      @Around("pt()")
      public Object around(ProceedingJoinPoint pjp) throws Throwable{
      System.out.println("around before advice ...");
      Object ret = pjp.proceed();
      System.out.println("around after advice ...");
      return ret;
      }
      
      • 名称:@AfterReturning(了解)

      • 类型:方法注解

      • 位置:通知方法定义上方

      • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法正常执行完毕后运行

      • 范例:

        @AfterReturning("pt()")
        public void after Returning(){
        System.out.println("afterReturning advice ...");
        }
        
      • 相关属性:value(默认):切入点方法名,格式为类名.方法名()

      • 名称:@AfterThrowing(了解)

      • 类型:方法注解

      • 位置:通知方法定义上方

      • 作用:设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行

      • 范例:

      @AfterThorwing("pt()")
      public void afterThorwing(){
      	System.out.println("afterThrowing advice ...");
      }
      
    • 相关属性:value(默认):欺辱点方法名,格式为:类名.方法名()

案例:测量业务层接口万次执行效率

步骤一:

@Around("ProjectAdvice.serbicePt()")
public void runSpeed(ProceedingJoinPoint pjp) throws Throwable{
	long start = System.currentTimeMillis();
    //获取执行签名信息
    Singnature signature= pjp.getSignature();
    //通过签名获取执行类型(接口名)
    Stirng className = signature.getDeclaringTypeName();
    String methodName = signature.getName();
	for(int i= ; i< 10000;i++){
		pjp.proceed();
	}
	long start =System.currentTimeMillis();
	System.out.println("业务层接口万次执行"+className+"."+methodnName+"时间
:"+(end-start))+"ms"
}

需求:任意业务层接口军可执行显示器执行效率(执行时长)

分析:

  • 业务层功能:业务接口执行前后分别记录时间求差值得到执行效率
  • 通知类型选择前后均可增强的类型——环绕通知

AOP通知获取数据

AOP通知获取参数数据

  • 获取切入点方法的参数

    • JoinPoint:适用于前置,后置,返回后,抛出异常通知
    • ProceedJointPoint:适用于环绕通知
  • 获取切入点方法返回值

    • 返回后通知
    • 环绕通知
  • 获取切入点方法运行异常信息

    • 抛出异常后通知
    • 环绕通知
  • JointPoint对象描述了连接点方法的运行状态,可以获取到原始方法的调用参数

@Before("pt()")
public void before(JointPoint jp){
	Object[] args = jp.getArgs();
	System.out.println(Arrays.toString(args));
}
  • ProceedJointPoint的子类
public Object around(ProceedingJointPoint pjp) threos Throwable{
	Object[] args = pjp.getArgs();
	System.out.println(Arrays.toString(args));
	Object ret = pjp.proceed();
	return ret;	
}

AOP通知获取返回值数据

  • 抛出异常后通知可以获取切入点方法中的异常信息,使用形参可以接收对应的异常对象
@AfterReturning(Value = "pt()",returning = "ret")
public void afterReturning(String ret){
	System.out.println("afterRetruning advice ..."+ ret);
}
  • 环绕通知中可以手工书写对原始方法的调用,得到结果即为原始方法的返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
	Object ret = pjp.proceed();
	return ret;
}

AOP通知获取异常数据(了解)

  • 抛出异常后通知可以获取切入点帆帆中出现的异常信息,是用形参可以接收对应的异常对象
@AfterThrowing(value = "pt()",throwing ="t")
public void afterThrowing(Throwable t){
 System.out.println("afterThrowing adivce ..."+ t);
}
  • 抛出异常后通知可以回去切入点方法运行的异常信息,使用形参可以接收运行时抛出的异常对象
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
	Object ret = null;
	try{
		ret= pjp.proceed();
	}catch(Throwable t){
		t.printStackTrace();
	}
	return ret;
}

总结

AOP通知获取数据

  • 参数
  • 返回值
  • 异常

案例:百度网盘密码数据兼容处理

案例:百度网盘分析链接输入密码数据错误兼容性处理

分析:

  1. 在业务方法执行前对之前对所有的输入输出参数进行格式处理——trim()
  2. 使用处理后的参数调用原始方法——环绕通知中存在对原始方法的调用
@Around("DataAdvice.servicePt()")
public Object trimString(ProceedingJoinPoint pjp) throws Throwable{
	Object[] args = pjp.getArgs();
	//对原始参数的每一个参数进行操作
	for(int i = 0; i < args.leng ; i++){
	//如果是字符串数据
	if(args[i].getClass().equals(String.class)){
	//取出数据,trim()操作后,更新数据
	args[i] = args[i].toString().trim();
	}
	}
	return pjp.proceed(args);
}

AOP总结

核心概念

  • 概念:AOP面向切面编程,是一种编程范式
  • 作用:在不惊动原始设计的基础上为方法进行功能增强
  • 核心概念
    • 代理(Proxy):SpringAOP的本质核心就是采用代理模式实现的
    • 连接点(JoinPoint):在SpringAop中,理解为任意方法的执行
    • 切入点(Pointcut):匹配连接点的式子,也是具有共性功能的方法描述
    • 通知(Advice):若干个方法的共性,在切入点处执行,最终体现为一个方法
    • 切面(Aspect):描述通知与欺辱点对应关系
    • 目标对象(Target):被代理的原始对象称为目标对象

切入点

  • 切入点表达式标准格式,动作关键字(访问修饰符 返回值 包名.类/接口名.方法名(参数)异常名 )
    • excution(* com.hcx.service.*Service.*(..))
  • 切入点表示描述统配符:
    • 作用:用于快速描述,范围描述
    • *:匹配任意符号(常用)
    • …:匹配多个连续任意符号(常用)
    • +:匹配子类类型
  • 切入点表达式书写技巧
    1. 按标准规范开发
    2. 查询操作的返回值建议在使用 * 匹配
    3. 减少使用…去描述包
    4. 对接口进行描述,使用表示模块名,例如UserService的匹配描述为Service
    5. 方法名书写保留动词,例如get,使用表示名称,例如getById匹配描述为getBy
    6. 参数更卷实际情况灵活调整

环绕通知

  • 通知类型
    • 前置通知
    • 后置通知
    • 环绕通知(重点)
      • 环绕通知依赖形参ProceedingJoinPoint才能实现对原始方法的调用
      • 环绕通知可以隔离原始方法的调用执行
      • 环绕通知返回设置为Objec类型
      • 环绕通知中可以对原始方法调用过程中出现的异常进行处理
    • 返回后通知
    • 抛出异常后通知

AOP通知获取数据

  • 获取切入点方法的参数
    • JoinPoint:适用于前置,后置,返回后,抛出异常后通知,设为方法发第一个形参
    • ProceedJointPoint:适用于环绕通知
  • 获取切入点方法返回值
    • 返回后通知
    • 环绕通知
  • 获取切入点方法运行异常信息
    • 抛出异常后通知
    • 环绕通知

Spring事务简介

  • 事务作用:在数据层保障一系列的数据库操作同成功同失败
  • Spring事务作用:在数据层或业务层保障一系列的数据操作同成功同失败
public interface PlatformTransactionManager{
	void commit(TransactionStatus status) throws TransactionException;
	void rollback(TransactionSattus status) throws TransactionException;
}
public class DataSourceTransactionManager{
	...
}

案例:银行账户转账

需求:实现任意两个账户间转账操作

需求微缩:A账户减钱,B账户加钱

分析:

  1. 数据层提供基础操作,指定账户减钱(outMoney),指定账户加钱(inMoney)
  2. 业务层提供转账操作(transfer),调用减钱和加钱的操作
  3. 提供2个账号和操作金额执行转账操作
  4. 基于Spring整合MyBatis环境搭建上述操作

结果分析:

  1. 程序正常执行时,账号金额A减B加,没有问题
  2. 程序出现异常,转账失败,单是异常之前操作成功,异常之后操作失败,整体业务失败

添加事务管理操作流程

  1. 在业务层接口上添加Spring事务管理

    public interface AccountService{
    	@Transactional
    	public void transfer(String out,String in,Double money);
    }
    

    注意:Spring注解式事务通常添加在业务层接口中而不会添加到业务层实习类中,降低耦合度

    ​ 注解式业务可以添加到业务方法上表示开启事务,也可以添加到接口上表示当前接口所有方法开启事务

  2. 设置事务管理

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSoure){
    	DataSouceTransactionManager ptm = new DataSourceTransactionManager();
    	ptm.setDataSource(dataSource);
    	return ptm;
    }
    

    注意:事务管理要根据实现技术进行选择

    ​ MyBatis框架使用JDBC事务

  3. 开启注解事务驱动

    @Configuration
    @ComponentScan("com.hcx")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    @EnableTransactionManagement
    public class SpringConfig{
    }
    

Spring事务角色(通过相同数据源来管理)

  • 事务角色
    • 事务管理员:发起事务方,在Spring中通常指代业务层开启事务的方法
    • 事务协调员:加入事务方,在Spring中通常带着数据层方法,也可以是业务层方法

事务相关配置

黑马程序员2022新版SSM框架Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术——万字学习笔记:Spring+SpringMVC篇_第1张图片

注意:IOException不属于运行时异常,事务回滚不管理

事务传播行为

  • 事务传播行为:事务协调员对事务管理员所携带事务的处理态度

Spring中七种事务传播行为

事务传播行为类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

黑马程序员2022新版SSM框架Spring+SpringMVC+Maven高级+SpringBoot+MyBatisPlus企业实用开发技术——万字学习笔记:Spring+SpringMVC篇_第2张图片案例:转账业务追加日志

需求:实现任意两个账户间转账操作,并对每次转账操作在数据库进行留痕

需求微缩:A账户减钱,B账户加钱,数据库记录日志

分析:

  1. 基于转账操作案例添加日志模块,实现数据库中记录日志
  2. 业务层转账操作(transfer),调用减钱,加钱与记录日志功能

实现效果预期:

无论转账是否成功,均进行转账操作的日志留痕

存在问题:

日志的记录与转账操作隶属于同一事务,同成功同失败

失效效果预取改进:

无论转账操作是否成功,日志必须保留

步骤

  1. 在业务层接口上添加Spring事务,设置事务传播行为REQUIRES_NEW(需要新事务)
@Service
public class LogServiceImpl implements LogService{
	@Autowired
	private LogDao logDao;
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void log(String out,String in,Double money){
		logDao.log("转账操作由"+out+"到"+in+",金额:"+money)}
}

SpringMVC

  • SpringMVC技术与Servlet技术功能相同,均属于web层开发技术,但SpringMVC技术更简洁,能用更少的代码进行开发

SpringMVC概述

  • SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
  • 功能
    • 用于进行表现层功能开发
  • 优点
    • 使用简单,开发便捷(相比于Servlet)
    • 灵活性强

SpringMVC入门案例

  1. 使用SpringMVC技术需要县导入SpringMVC坐标与Servlet坐标

     
        <dependency>
          <groupId>javax.servletgroupId>
          <artifactId>javax.servlet-apiartifactId>
          <version>3.1.0version>
          <scope>providedscope>
        dependency>
        <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-webmvcartifactId>
          <version>5.2.10.RELEASEversion>
        dependency>
    
  2. 创建SpringMVC控制器类(等同于Servlet功能)

    @Controller
    public class UserController{
    	@RequestMapping("/save")
    	@ResponseBody
    	public String save(){
    		System.out.println("user save ...");
    		return "{'module':'springmvc'}";
    	}
    }
    
  3. 初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean

    @Configuration
    @ComponentScan("com.iteima.controller")
    public class SpringMvcConfig{
    }
    
  4. 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理请求

    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
    
    //4.定义一个servlet容器启动的配置类,在里面加载spring的配置
    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        //加载springMVC容器配置
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(SpringMvcConfig.class);
            return context;
        }
        //设置哪些请求归属springMVC处理
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    	//加载spring容器配置
        @Override
        protected WebApplicationContext createRootApplicationContext() {
            return null;
        }
    }
    

    入门案例用到的注解

    • 名称:@Controller

    • 类型:类注解

    • 位置:SpringMVC控制器定义上方

    • 作用:设置SpringMVC的核心控制器bean

    • 范例:

      @Controller
      public class UserController{
      }
      
    • 名称:@ResquestMapping

    • 类型:方法注释

    • 位置:SpringMVC控制器方法定义上方

    • 作用:设置当前控制器方法请求访问路径

    • 范例:

      @ResquestMapping("/save")
      public void save(){
      	System.out.println("user save ...");
      }
      
    • 相关属性

      • value(默认):请求访问路径
    • 名称:@ResponseBody

    • 类型:方法注释

    • 位置:SpringMVC控制器方法定义上方

    • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

    • 范例:

      @RequestMapping("/save")
      @ResponseBody
      public String save(){
      System.out.println("user save ...");
      return "{'info':"springmvc"}";
      }
      
    • SpringMVC入门程序开发总结(1+N)

      • 一次性工作
        • 创建工程,设置服务器,加载工程
        • 导入坐标
        • 创建web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
        • SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器bean)
      • 多次工作
        • 定义处理请求的控制器类
        • 定义处理请求的控制器方法,并配置映射路径(@RequestMapping)与返回json数据(@ResponseBody)

Servlet容器配置类

  • AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化web3.0容器的抽象类

  • AbstractDispatcherServletInitializer提供三个接口方法供用户实现

    • createServletAppilcationContext()方法,创建servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围

      protected WebApplicationContext createServletApplicationContext(){
      AnnotattionConfigWebApplicationContext context = new AnnotattionConfigWebAppklicationtContext();
      context.register(SpringMvcConfig.class);
      return context;
      }
      
    • getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入道SpringMVC进行处理

      protected String[] getServletMapping(){
      	return new String[]("/");
      }
      
    • createRootApplicationContext()方法,如果创建Servlet容器时需要加载飞SpringMVC对应的bean,使用当前方法进行,使用方式用createServletAppilcationContext()

      protected WebApplicationContext createRootApplicationContext(){
      return null;
      }
      

      入门案例工作流程分析

      • 启动服务器初始化过程
        1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器
        2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
        3. 加载SpringMvcConfig
        4. 执行@ComponentScan加载对应的bean
        5. 加载UserController,每过@RequestMapping的名称对应一个具体的方法
        6. 执行getServletMappings方法,定义所有请求都通过SpringMVC
      • 单次请求过程
        1. 发射请求localhost/save
        2. web容器发现所有请求都经过SpringMVC,将请求交割SpringMVC处理
        3. 解析请求路径/save
        4. 由/save匹配执行对应的方法saev()
        5. 执行save()
        6. 检测道由@ResponseBody之际将save()方法的返回值作为响应体返回给请求方

Controller加载控制与业务bean加载控制

  • SpringMVC相关bean(表现层bean)
  • Spring控制的bean
    • 业务bean(Service)
    • 功能bean(DatatSource等)
  • SpringMVC相关bean加载控制
    • SpringMVC加载的bean对应的包均在com.hcx.controller包内
  • Spring相关bean加载控制
    • 方式一:Spring加载的bean设定扫描范围为com.hcx,排除controller包内的bean
    • 方式二:Spring加在的bean设定扫描范围为精准范围,例如service包,dao包等

注解复习

  • 名称:@ComponentScan

  • 类型:类注解

  • 范例:

    @Configuration
    @ConmponentScan(value="com.itheima",
    	excludeFilters = @ComponentScna.Filter(
    		type = FilterType.ANNOTATION,
    		classer = Controller.class
    		)
    )
    public class SpringConfig{}
    
  • 属性

    • excludeFilters: 排除扫描路径中加载的bean,需要指定类别(type)与具体项(classer)
    • includeFilters: 加载指定的bean,需要指定类别(type)与具体项(class)
  • bean的加载格式

    //4.定义一个servlet容器启动的配置类,在里面加载spring的配置
    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        //加载springMVC容器配置
        @Override
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(SpringMvcConfig.class);
            return context;
        }
        //设置哪些请求归属springMVC处理
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    //加载spring容器配置
        @Override
        protected WebApplicationContext createRootApplicationContext() {
             AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.register(SpringConfig.class);
            return context;
        }
    }
    
    
  • 简化开发

    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
    
        @Override
        protected Class<?>[] getRootConfigClasses() {
            return new Class[]{SpringMvcConfig.class};
        }
    
        @Override
        protected Class<?>[] getServletConfigClasses() {
            return new Class[0];
            /* return new Class[]{SpringConfig.class}*/
        }
    
        @Override
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    }
    

请求与响应

  • 名称:@RequestMapping

  • 类型:方法注解 类注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前方法请求访问路径前缀

  • 范例:

    @Controller
    @RequestMapping("/user")
    public class UserController{
    	@RequestMapping("/save")
    	@ResponseBody
    	public String save(){
    		System.out.println("user save ...");
    		return "{'module':'user save'}";
    	}
    }
    
  • 属性

    • value(默认):请求访问路径,或访问路径前缀
中文乱码处理
Post请求中文乱码处理
  • 为web容器添加过过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器

    //乱码处理
        @Override
        protected Filter[] getServletFilters() {
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            return new Filter[]{filter};
        }
    
请求参数
  • 参数种类

    • 普通参数
    • POJO类型参数
    • 嵌套POJO类型参数
    • 数组类型参数
    • 集合类型参数
  • 普通参数:url 地址穿参,地址参数名与形参变量名相同,定义形参即可接收参数

    @RequestMapping("/commoParam")
    @ResponseBody
    public String commonParam(String name,int age){
    	Stystem.out.println("普通参数传递 name ==> " + name );
    	System.out.println("普通参数 age ==> " + age);
    	return "{'module':'common param'}";
    }
    
使用到的注解
  • 名称:@RequestParam

  • 类型:形参注解

  • 位置:SpringMVC控制器方法形参定义前面

  • 作用:绑定请求参数与处理器方法形参间的关系

  • 范例:

    @RequestMapping("/commonParamDifferentName")
    @ResponseBody
    public String commonParamDifferentName(@RequestParam("name") String userName , int age){
     	System.out.println("普通参数 userName ==>" + userName);
     	System.out.println("普通参数 userName ==>" + age);
     	return "{'module':'common param different name'}";
    }
    
  • 参数:

    • required: 是否为必传参数
    • defaultValue:参数默认值
POJO参数/嵌套的POJO参数
  • POJO参数/嵌套的POJO参数:请求数名与形参对象属性名相同,定义POJP类型形参即可接收参数

    //嵌套pojo参数
        @RequestMapping("/saveObj")
        @ResponseBody
        public String saveObj(User user){
            System.out.println("对象接收数据"+user);
            return "{'module':'pojo contain pojo param'}";
        }
    
Json数据传参

请求参数

  • json数组
  • json对象(POJO)
  • json数组(POJO)
步骤一:导入JSON的pom坐标

    <dependency>
      <groupId>com.fasterxml.jackson.coregroupId>
      <artifactId>jackson-databindartifactId>
      <version>2.12.4version>
    dependency>
步骤二:设置发送json数据(请求body中添加json数据)
步骤三:在SpringMvcConfig.class里面加入@EnableWebMvc,开启自动转换json数据的支持
//3.创建springmvc的配置文件,加载controller对应的bean
@Configuration
//@ComponentScan("com.hcx.controller")
@ComponentScan(value = "com.hcx"/*, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)*/)
@EnableWebMvc
public class SpringMvcConfig {
}
第四步:在Controller(控制层)编写代码,设置json数据
//集合参数:JSON格式
    @RequestMapping("/listParamJson")
    @ResponseBody
    public String listParamJson(@RequestBody List<String> list){
        System.out.println("集合参数传递 list ==>" + list);
        return "{'module':'listJson param'}";
    }
    //POJO参数:JSON格式
    @RequestMapping("/pojoParamJson")
    @ResponseBody
    public String pojoParamJson(@RequestBody User user){
        System.out.println("pojo(json)参数传递 user ==>"+user);
        return "{'module':'pojoJson param'}";
    }
    //对象集合类型:JSON格式
    @RequestMapping("/listUserParamJson")
    @ResponseBody
    public String listUserParamJson(@RequestBody List<User> list){
        System.out.println("List参数传递 ==>" + list);
        return "{'module':'ListJson param'}";
    }
注解复习
  • 名称:@EnableWebMvc

  • 类型:配置类注解

  • 位置:SpringMVC配置定义上方

  • 作用:开启SpringMVC多项负责功能

  • 范例:

    //3.创建springmvc的配置文件,加载controller对应的bean
    @Configuration
    //@ComponentScan("com.hcx.controller")
    @ComponentScan(value = "com.hcx"/*, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)*/)
    @EnableWebMvc
    public class SpringMvcConfig {
    }
    
  • 名称:@RequestBody

  • 类型:形参注解

  • 位置:SpringMVC控制器方法形参定义前面

  • 作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次

  • 范例:

    @RequestMapping("/listParamForJson")
    @ResponseBody
    public String listParamForJson(@RequestBody List<String> likes){
     	System.out.println("list common(json)参数传递 list ==>"+ likes);
     	return "{'moudle':'list common for json param'}";
    }
    
@RequestBody与@RequestParam区别
  • 区别
    • @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
    • @RequestBody用于接收json数据【application/json】
  • 应用
    • 后期开发中,发送json格式数据为主,@ResquestBody应用较广
    • 如果发送飞json格式数据,选用@RequestParam接收请求参数
日期类型参数传递
  • 日期类型数据基于系统不同格式也不尽相同

    • 2022-08-30
    • 2022/08/30
    • 08/18/2022
  • 接收形参是,根据不同的日期格式设置不同的接收方式

    @RequestMapping("/dateParam")
    @ResponseBody
    public String dateParam(Date date,
    						@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1,
    						@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")Date date2){
    						System.out.println("参数传递 date ==>" + date);
    						System.out.println("参数传递 date(yyyy-MM-dd) ==>" + date1);
    						System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==>" + date2);
    			retrun "{'module':'data param'}";				
    		}				
    
日期类型注解
  • 名称:@DateTimeFormat

  • 类型:形参注解

  • 位置:SpringMVC控制器方法形参前面

  • 作用:设定日期时间型数据格式

  • 范例:

    @RequestMapping("/dateParam")
    @ResponseBody
    public String dateParam(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date){
    	System.out.println("参数传递 date ==>" + date);
    	retrun "{'module':'data param'}";	
    }
    
  • 属性:pattern = “日期时间格式字符串”

类型转化器
  • Converter接口

    public interface Converter<S,T>{
    	@Nullable
    	T convert(S source);
    }
    
    • 请求参数年龄数据(String+Integer)
    • 日期格式转换 (String -> Date)
  • @EnableWebMvc功能之一:根据类型匹配对应类型转换器

响应
  • 响应页面

  • 响应数据

    • 文本数据
    • JSON数据
  • 响应文本数据

    @RequestMapping("/toText")
    @ResponseBody
    public String toText(){
    	return "response text";
    }
    
  • 响应json数据(对象转json)

    //响应POJO对象数据
        @RequestMapping("/toJsonPOJO")
        @ResponseBody
        public User toJsonPOJO(){
            System.out.println("返回json对象数据");
            User user = new User();
            user.setName("hcx");
            user.setAge(21);
            user.setAddress(new Address("南昌","江西"));
            return user;
        }
    
  • 响应对象集合转json数组

    //响应POJO集合对象
        @RequestMapping("/toJsonList")
        @ResponseBody
        public List<User> toJsonList(){
            System.out.println("返回json对象集合");
            User user= new User();
            user.setName("开源协议");
            user.setAge(21);
            user.setAddress(new Address("南昌","江西"));
    
            User userOne= new User();
            userOne.setName("阿里巴巴开发手册");
            userOne.setAge(15);
            userOne.setAddress(new Address("杭州","浙江"));
    
            List<User> userList = new ArrayList<>();
            userList.add(user);
            userList.add(userOne);
            return userList;
        }
    
注解复习
  • 名称:@ResponseBody

  • 类型:方法注解

  • 位置:SpringMVC控制器方法定义上方

  • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

  • 范例:

    @RequestMapping("/save")
    @ResponseBody
    public String save(){
    	System.out.print("Save...");
    	return "{'info':'springmvc'}";
    }
    
  • HttpMessageConverter接口

    public interface HttpMessageConverter<T> {
        boolean canRead(Class<?> var1, @Nullable MediaType var2);
    
        boolean canWrite(Class<?> var1, @Nullable MediaType var2);
    
        List<MediaType> getSupportedMediaTypes();
    
        T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
    
        void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
    }
    

REST风格

  • REST(Representational State Transfer),表现形式状态转换

    • 传统风格资源描述形式

      http://localhost/user/getById?id=1

      http://localhost/user/saveUser

    • REST风格描述形式

      http://localhost/user/1

      http://localhost/user

  • 优点:

    • 隐藏资源的访问行为,无法通过地址得治对资源是何种操作
    • 简化书写
REST风格简介
  • 安装REST风格访问资源是使用行为动作区分对资源进行何种操作
    • http://localhost/users 查询全部用户信息 GET (查询)
    • http://loocalhost/users/1 查询指定用户信息 GET(查询)
    • http://localhost/users 添加用户信息 POST(新增/保存)
    • http://localhost/users 修改用户信息 PUT(修改/更新)
    • http://localhost/users/1 删除用户信息 DELETE(删除)

注意事项:

上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范

描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users,books,accounts…

RESTful入门案例
  1. 设定http请求动作(动词)

    @RequestMapping(value="/users",method =RequestMethod.POST)
    @ResponseBody
    public String save(@RequestBody User user){
    	System.out.println("user save..." + user);
    	return "{'module':'user save'}";
    }
    
    @RequestMapping(value="/users",method=RequestMethod.PUT)
    @ResponseBody
    publiic String update(@RequestBody User user){
    	System.out.println("user update ..."+ user);
    	return "{'module':'user update'}";
    }
    
  2. 设置请求参数(路径变量)

    @RequestMapping(value ="/users/{id}",method =RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
     System.out.println("user delete ..."+ id);
     return "{'module':'user deleter'}";
    }
    
    注解复习
    • 名称:@PathVariable

    • 类型:形参注解

    • 位置:SpringMVC控制器方法形参定义前面

    • 作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应

    • 范例:

      @RequestMapping(value = "/users/{id}",method =RequestMethod.DELETE)
      @ResponseBody
      public String save(@PatVariable Integer id){
      	System.out.println("user delete ..." + id);
      	return "{'module':'user delete'}";
      }
      
    • 名称:@RequestMapping

    • 类型:方法注解

    • 位置:SpringMVC控制器定义上方

    • 作用:设置当前控制器方法请求访问路径

    • 范例:

      @RequestMapping(value = "/users",method =RequestMethod.POST)
      @ResponseBody
      public String save(@RequestBody User user){
      	System.out.println("user save ..." + user);
      	return "{'module':'user save'}";
      }
      
    • 属性

      • value(默认):请求访问路径
      • method:http请求动作,标准动作(POST/GET/PUT/DELETE)
@RequestBody @RequestParam @PathVariable
  • 区别
    • @RequestParam用于接收url地址传参或表单传参
    • @RequestBody用于接收JSON数据
    • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
  • 应用
    • 后期开发中,发送请求参数超过一个时,以json格式为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数
    • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收路径变量,常用于传递id值
RESTful快速开发
@RequestMapping(value="/books",method = RequestMethod.POST)
@ResponseBody
public String save(@RequestBody Book book){
	System.out.println("book save ..."+ book);
	return "{'module':'book save'}";
}
@RequestMapping(value="/books",method = RequestMethod.PUT)
@ResponseBody
public String save(@RequestBody Book book){
	System.out.println("book update ..."+ boook);
	return "{'module':'book update'}";
}
合并注解事项简化开发
  • 名称:@RestController

  • 类型:类注解

  • 位置:基于SpringMVC的RESTful开发控制器类定义上方

  • 作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能

  • 范例:

    @RestControoler
    public class BookController{
    }
    
  • 名称:@GetMapping @PostMapping @PutMapping @DeleteMapping

  • 类型:方法注解

  • 位置:基于SpringMVC的RESTful开发控制器方法定义上方

  • 作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应Get请求

  • 范例:

    @GetMapping("/{id}")
    public String getById(@PathVariable Integer id){
        System.out.pringln("book getById ..." + id);
        return "{'module':'book getById'}";
    }
    
  • 属性

    • value(默认):请求访问路径
案例:基于RESTful页面数据交互
  1. 制作SpringMVC控制器,并通过PostMan测试接口功能

    @RestController
    @RequestMapping("/books")
    public Class BookController{
    	@PostMapping
    	public String save(@RequestBody Book book){
    		System.out.println("book save ==>" + book);
    		return "{'moudule':'book save success'}";
    	}
    	@GetMapping
        public List<Book> getAll(){
            List<Book> bookList = new ArrayList<>();
            Book book = new Book();
            book.setType("计算机");
            book.setName("SpringMVC入门教程");
            book.setDescription("小试牛刀");
            bookList.add(book);
            Book book1 = new Book();
            book1.setType("计算机");
            book1.setName("SpringMVC入门教程");
            book1.setDescription("加油");
            bookList.add(book1);
            //模拟数据
            return bookList;
        }
    }
    
  2. 设置对静态支援的访问通行

    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {
        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            //当访问/pages/???不要走mvc,走/pages目录下的内容访问
            registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
            //放行js
            registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    	   //放行css
            registry.addResourceHandler("/css/**").addResourceLocations("/css/");
            //放行插件
            registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
        }
    }
    
  3. 前端页面通过异步提交访问后台控制器

    //添加
    saveBook(){
    	axios.post("/books",this.formData).then((res)=>{
    	
    	});
    },
    
    //主页列表查询
    getAll(){
    	axios.get("/books").then((res)=>{
    		this.dataList = res.data;
    	});
    },
    
  4. 总结

    • 先做后台功能,开发接口并调通接口
    • 再做页面异步调用,确认功能可以正常访问
    • 最后再完成页面数据的显示
    • 补充:放行静态资源访问

SSM整合

概览

  • SSM整合
  • 表现层数据封装
  • 异常处理器
  • 项目异常处理方案
  • 案例:SMM整合标准开发

SSM整合流程

  1. 创建工程,并导入坐标

    
    
    <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.0modelVersion>
    
        <groupId>org.examplegroupId>
        <artifactId>springmvc_08_ssmartifactId>
        <version>1.0-SNAPSHOTversion>
        <packaging>warpackaging>
        <properties>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <maven.compiler.source>1.8maven.compiler.source>
            <maven.compiler.target>1.8maven.compiler.target>
        properties>
    
        <dependencies>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <version>1.18.12version>
            dependency>
    
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-webmvcartifactId>
                <version>5.2.10.RELEASEversion>
            dependency>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-jdbcartifactId>
                <version>5.2.10.RELEASEversion>
            dependency>
            <dependency>
                <groupId>org.springframeworkgroupId>
                <artifactId>spring-testartifactId>
                <version>5.2.10.RELEASEversion>
            dependency>
            
            <dependency>
                <groupId>org.mybatisgroupId>
                <artifactId>mybatisartifactId>
                <version>3.5.6version>
            dependency>
            <dependency>
                <groupId>org.mybatisgroupId>
                <artifactId>mybatis-springartifactId>
                <version>1.3.2version>
            dependency>
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>5.1.47version>
            dependency>
            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druidartifactId>
                <version>1.2.11version>
            dependency>
            
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.12version>
                <scope>testscope>
            dependency>
            
            <dependency>
                <groupId>javax.servletgroupId>
                <artifactId>servlet-apiartifactId>
                <version>2.5version>
                <scope>providedscope>
            dependency>
            
            <dependency>
                <groupId>com.fasterxml.jackson.coregroupId>
                <artifactId>jackson-databindartifactId>
                <version>2.9.0version>
            dependency>
        dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.tomcat.mavengroupId>
                    <artifactId>tomcat7-maven-pluginartifactId>
                    <version>2.1version>
                    <configuration>
                        <port>8080port>
                        <path>/path>
                    configuration>
                plugin>
            plugins>
        build>
    project>
    
    
  2. SSM整合

    • Spring

      • SpringConfig

        @Configuration
        @ComponentScan("com.hcx")
        @PropertySource("classpath:jdbc.properties")
        @Import({JdbcConfig.class,MyBatisConfig.class})
        public class SpringConfig{
        }
        
    • MyBatis

      • MybatisConfig

        import org.mybatis.spring.SqlSessionFactoryBean;
        import org.mybatis.spring.mapper.MapperScannerConfigurer;
        import org.springframework.context.annotation.Bean;
        
        import javax.sql.DataSource;
        
        public class MyBatisConfig {
            @Bean
            public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
                SqlSessionFactoryBean factoryBean =  new SqlSessionFactoryBean();
                factoryBean.setDataSource(dataSource);
                factoryBean.setTypeAliasesPackage("com.hcx.domain");
                return factoryBean;
            }
        
            @Bean
            public MapperScannerConfigurer mapperScannerConfigurer(){
                MapperScannerConfigurer mapperScannerConfigurer=new MapperScannerConfigurer();
                mapperScannerConfigurer.setBasePackage("com.hcx.dao");
                return mapperScannerConfigurer;
            }
        }
        
      • JdbcConfig

        public class JdbcConfig{
        	@Value("${jdbc.driver}")
        	private String driver;
        	@Value("${jdbc.url}")
        	private String url;
        	@Value("${jdbc.username}")
        	private String userName;
        	@Value("${jdbc.password}")
        	private String password;
        	@Bean
        	public DataSource dataSource(){
        	DruidDataSource dataSource = new DruidDataSource();
        	 dataSource.setDriverClassName(driver);
                dataSource.setUrl(url);
                dataSource.setUsername(username);
                dataSource.setPassword(password);
                return dataSource;
        	}
            
            //开启事务管理
            @Bean
            public PlatformTransactionManager transactionManager(DataSource dataSource){
                DataSourceTransactionManager ds = new DataSourceTransactionManager();
                ds.setDataSource(dataSource);
                return ds;
            }
        }
        
      • jdbc.properties

        jdbc.driver=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/ssm_db
        jdbc.username=root
        jdbc.password=******
        
    • SpringMVC

      • ServletConfig

        package com.hcx.config;
        
        import org.springframework.web.filter.CharacterEncodingFilter;
        import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
        
        import javax.servlet.Filter;
        
        public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
            protected Class<?>[] getRootConfigClasses() {
                return new Class[]{SpringConfig.class};
            }
        
            protected Class<?>[] getServletConfigClasses() {
                return new Class[]{SpringMvcConfig.class};
            }
        
            protected String[] getServletMappings() {
                return new String[]{"/"};
            }
        
            //乱码处理——post表单提交时处理中文乱码
            @Override
            protected Filter[] getServletFilters() {
                CharacterEncodingFilter filter = new CharacterEncodingFilter();
                filter.setEncoding("UTF-8");
                return new Filter[]{filter};
            }
        }
        
        
      • SpringMvcConfig

        import org.springframework.context.annotation.ComponentScan;
        import org.springframework.context.annotation.Configuration;
        import org.springframework.web.servlet.config.annotation.EnableWebMvc;
        
        @Configuration
        @ComponentScan("com.hcx.controller")
        @EnableWebMvc
        public class SpringMvcConfig {
        }
        
  3. 功能模块

    • 表与实体类

      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      import lombok.ToString;
      
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      @ToString
      public class Book {
          private Integer id;
          private String type;
          private String name;
          private String description;
      
      }
      
    • dao(接口+自动代理)

      import com.hcx.domain.Book;
      import org.apache.ibatis.annotations.Delete;
      import org.apache.ibatis.annotations.Insert;
      import org.apache.ibatis.annotations.Select;
      import org.apache.ibatis.annotations.Update;
      import org.springframework.stereotype.Repository;
      
      import java.util.List;
      
      public interface BookDao {
          /*写法一:@Insert("insert into tbl_book values(null,#{type},#{name},#{description})")*/
          @Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")
          public void save(Book book);
      
          @Update("update tbl_book set type= #{type},name = #{name},description = #{description} where id = #{id}")
          public void update(Book book);
      
          @Delete("delete from tbl_book where id = #{id}")
          public void delete(Integer id);
      
          @Select("select * from tbl_book where id = #{id}")
          public Book getById(Integer id);
      
          @Select("select * from tbl_book")
          public List<Book> getAll();
      }
      
      
    • service(接口+实现类)

      • 接口
      import com.hcx.domain.Book;
      import org.springframework.transaction.annotation.Transactional;
      
      import java.util.List;
      
      @Transactional
      public interface BookService {
          /**
           * 保存
           * @param book
           * @return
           */
          public Boolean save(Book book);
      
          /**
           * 修改
           * @param book
           * @return
           */
          public Boolean update(Book book);
      
          /**
           * 根据id删除
           * @param id
           * @return
           */
          public Boolean delete(Integer id);
      
          /**
           * 根据id查询
           * @param id
           * @return
           */
          public Book getById(Integer id);
      
          /**
           * 查询全部
           * @return
           */
          public List<Book> getAll();
      }
      

      实现类

      import com.hcx.dao.BookDao;
      import com.hcx.domain.Book;
      import com.hcx.service.BookService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import java.util.List;
      @Service
      public class BookServiceImpl implements BookService {
          @Autowired
          private BookDao bookDao;
          public Boolean save(Book book) {
              bookDao.save(book);
              return true;
          }
      
          public Boolean update(Book book) {
              bookDao.update(book);
              return true;
          }
      
          public Boolean delete(Integer id) {
              bookDao.delete(id);
              return true;
          }
      
          public Book getById(Integer id) {
              return bookDao.getById(id);
          }
      
          public List<Book> getAll() {
      
              return bookDao.getAll();
          }
      }
      
      
      • 业务层接口测试(整合JUnit)

        import com.hcx.config.SpringConfig;
        import com.hcx.domain.Book;
        import org.junit.Test;
        import org.junit.runner.RunWith;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.test.context.ContextConfiguration;
        import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
        
        import java.util.List;
        
        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(classes = SpringConfig.class)
        public class BookServiceTest {
            @Autowired
            private BookService bookService;
            @Test
            public void testGetById(){
                Book book = bookService.getById(1);
                System.out.println(book);
            }
        
            @Test
            public void testGetAll(){
                List<Book> list=bookService.getAll();
                System.out.println(list);
            }
        }
        
    • controller

      import com.hcx.domain.Book;
      import com.hcx.service.BookService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.*;
      
      import java.util.List;
      
      @RestController
      @RequestMapping("/books")
      public class BookController {
          @Autowired
          private BookService bookService;
      
      
          @PostMapping
          public Result save(@RequestBody Book book) {
              boolean flag = bookService.save(book);
              return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
          }
      
          @PutMapping
          public Result update(@RequestBody Book book) {
              boolean flag = bookService.update(book);
              return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);
          }
      
          @DeleteMapping("/{id}")
          public Result delete(@PathVariable Integer id) {
              boolean flag = bookService.delete(id);
              return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);
          }
      
          @GetMapping("/{id}")
          public Result getById(@PathVariable Integer id) {
              Book book=bookService.getById(id);
              Integer code = book != null ? Code.GET_OK:Code.GET_ERR;
              String msg = book != null ? "数据查询成功" : "数据查询失败请重试";
              return new Result(code,book,msg);
          }
      
          @GetMapping
          public Result getAll() {
              List<Book> bookList = bookService.getAll();
              Integer code = bookList != null ? Code.GET_ALL_OK:Code.GET_ALL_ERR;
              String msg = bookList != null ? "全部数据查询成功":"数据查询失败请重试";
              return new Result(code,bookList,msg);
          }
      }
      
      

      数据传输协议所要用到的bean

      Result.java

      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      import lombok.ToString;
      
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      @ToString
      public class Result {
          private Integer code;
          private Object data;
          private String msg;
      
          public Result(Integer code, Object data) {
              this.code = code;
              this.data = data;
          }
      }
      
      

      Code.java

      public class Code {
          public static final Integer SAVE_OK = 20011;
          public static final Integer DELETE_OK = 20021;
          public static final Integer UPDATE_OK = 20031;
          public static final Integer GET_OK = 20041;
          public static final Integer GET_ALL_OK = 20051;
          public static final Integer SAVE_ERR = 20010;
          public static final Integer DELETE_ERR = 20020;
          public static final Integer UPDATE_ERR = 20030;
          public static final Integer GET_ERR = 20040;
          public static final Integer GET_ALL_ERR = 20050;
      }
      
      • 表现层接口测试(Apifox)

SSM整合-表现层与前端数据传输协议定义

数据返回格式:

{
	"code":Integer,
	"data":Obj,
	"msg":String
}

完成方式:

在Controller里面添加Result类和Code类,并让Controller里面的方法都返回Result类型的结果

SSM整合-异常处理

异常处理器

  • 程序开发过程中不可避免的会出现异常
  • 出现异常现象的常见位在与常见诱因如下:
    • 框架内部抛出异常:因使用不规范导致
    • 数据层抛出的异常:因为外部服务器故障导致(例如:服务器访问超时)
    • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
    • 表现层抛出的异常:因数据收集,校验规则导致(例如:不匹配的数据类型间导致异常)
    • 工具类抛出异常:因工具类书写不严谨不狗健壮导致(例如:必要释放的连接长使劲啊未释放等)
  1. 各个层级均出现异常,异常处理代码书写在哪一层

    ——所有的异常均抛出到表现层处理

  2. 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决——AOP思想

  • 定义异常处理器

    • 集中的,统一的处理项目中出现的异常
    @ResControllerAdvice
    public class ProjectExceptionAdvice{
    	@ExceptionHandler(Exception.class)
    	public Result doExpetion(Exception ex){
    		System.out.println("异常被捕获");
    		return new Result(001,null,"异常被捕获");
    	}
    }
    

注解使用:

  • 名称:@RestControllerAdvice

  • 类型:类注解

  • 位置:Rest风格开发的控制器增强类定义上方

  • 作用:为Rest风格开发的控制器类做增强

  • 范例:

    @ResControllerAdvice
    public class ProjectExceptionAdvice{
    }
    
  • 说明:

    • 此注解自带@ResponseBody注解与@Component注解,具备对应的功能
  • 名称:@ExceptionHandler

  • 类型:方法注解

  • 位置:专用于异常处理的控制器上方

  • 作用:设定指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

  • 范例:

    @RestControllerAdvice
    public class ProjectExceptionAdvice{
    	@ExceptionHandler(Exception.class)
    	public Result doException(Exception exception){
    		return new Result(001,null,"异常已被捕获")
    	}
    }
    
  • 说明:

    • 此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

SSM整合-项目异常处理方案

  • 项目异常分类

    • 业务异常(BusinessException)
      • 规范的用户行为产生的异常
      • 不规范的用户行为操作产生的异常
    • 系统异常(SystemException)
      • 项目运行过程中可预计且无法避免的异常
    • 其他异常(Exception)
      • 编程人员为预期到的异常
  • 项目异常处理方案

    • 业务异常(BusinessException)
      • 发送对应消息传递给用户,提醒规范操作
    • 系统异常(SystemException)
      • 发送固定消息传递给用户,安抚用户
      • 发送特定消息给运维人员,提醒维护
      • 记录日志
    • 其他异常(Exception)
      • 发送固定消息给用户,安抚用户
      • 发送特定消息给编程人员,提醒维护(纳入预期范围内)
      • 记录日志

项目异常处理

  1. 自定义项目系统级异常

    public class SystemException extends RuntimeException{
        private  Integer code;
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public SystemException(Integer code) {
            this.code = code;
        }
    
        public SystemException(Integer code,String message ) {
            super(message);
            this.code = code;
        }
    
        public SystemException(Integer code,String message, Throwable cause ) {
            super(message, cause);
            this.code = code;
        }
    
        public SystemException(Integer code,Throwable cause ) {
            super(cause);
            this.code = code;
        }
    
        public SystemException(Integer code,String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
            this.code = code;
        }
    }
    
  2. 自定义项目业务级异常

    public class BusinessException extends RuntimeException{
        private  Integer code;
    
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public BusinessException(Integer code) {
            this.code = code;
        }
    
        public BusinessException(Integer code, String message ) {
            super(message);
            this.code = code;
        }
    
        public BusinessException(Integer code, String message, Throwable cause ) {
            super(message, cause);
            this.code = code;
        }
    
        public BusinessException(Integer code, Throwable cause ) {
            super(cause);
            this.code = code;
        }
    
        public BusinessException(Integer code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
            this.code = code;
        }
    }
    
  3. 自定义异常编码(持续补充)

    public class Code{
    public static final Integer SAVE_OK = 20011;
        public static final Integer DELETE_OK = 20021;
        public static final Integer UPDATE_OK = 20031;
        public static final Integer GET_OK = 20041;
        public static final Integer GET_ALL_OK = 20051;
        public static final Integer SAVE_ERR = 20010;
        public static final Integer DELETE_ERR = 20020;
        public static final Integer UPDATE_ERR = 20030;
        public static final Integer GET_ERR = 20040;
        public static final Integer GET_ALL_ERR = 20050;
    
        public static final Integer SYSTEM_ERR = 20060;
        public static final Integer SYSTEM_TIMEOUT_ERR = 20070;
    
        public static final Integer BUSINESS_ERR = 20080;
        public static final Integer SYSTEM_UNKNOW_ERR = 20090;
    }
    
  4. 触发定义异常

    @Service
    public class BookServiceImpl implements BookService {
        @Autowired
        private BookDao bookDao;
        public Boolean save(Book book) {
            bookDao.save(book);
            return true;
        }
    
        public Boolean update(Book book) {
            bookDao.update(book);
            return true;
        }
    
        public Boolean delete(Integer id) {
            bookDao.delete(id);
            return true;
        }
    
        public Book getById(Integer id) {
            //模拟业务异常
            if(id == 1){
                throw new BusinessException(Code.BUSINESS_ERR,"访问连接过多异常");
            }
            //将可能出现的异常进行包装,转换称自定义异常
            try {
                int i = 1/0;
            }catch (Exception exception){
                throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器访问超时,请重试",exception);
            }
            return bookDao.getById(id);
        }
    
        public List<Book> getAll() {
            return bookDao.getAll();
        }
    }
    
  5. 拦截并处理异常

    @RestControllerAdvice
    public class ProjectExceptionAdvice {
        @ExceptionHandler(Exception.class)
        public Result doException(Exception exception) {
            //记录日志(错误堆栈)
            //发送消息给运维
            //发送邮件给开发人员,exception对象发送给开发人员
            System.out.println("其他异常被捕获");
            return new Result(Code.SYSTEM_UNKNOW_ERR, null, "系统繁忙请稍后再试");
        }
    
        @ExceptionHandler(SystemException.class)
        public Result doSystemException(SystemException exception) {
            //记录日志(错误堆栈)
            //发送消息给运维
            //发送邮件给开发人员,exception对象发送给开发人员
            System.out.println("系统异常被捕获");
            return new Result(exception.getCode(), null, exception.getMessage());
        }
    
        @ExceptionHandler(BusinessException.class)
        public Result doSystemException(BusinessException exception) {
            System.out.println("业务异常被捕获");
            return new Result(exception.getCode(), null, exception.getMessage());
        }
    }
    
  6. 异常处理效果对比

总结:

  • 异常处理器
  • 自定义异常
  • 异常编码
  • 自定义消息

SSM整合标准开发

SpringMVC拦截器

拦截器概念

  • 拦截器(Interceptor)是一种动态拦截方法调用的机制,再SpringMVC中动态拦截控制器方法的执行
  • 作用:
    • 在指定的方法调用前后执行预先设定的代码
    • 阻止原始方法的执行

拦截器和过滤器的区别

  • 归属不同:Filter属于Servlet计算,Interceptor属于SpringMvc技术
  • 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

拦截器入门案例

  1. 制作拦截器功能类

  2. 配置拦截器的执行位置

  3. 声明拦截器的bean,并实现HandlerInterCeptor接口(注意:扫描加载bean)

    @Component
    public class ProjectInterCeption implements HandlerInterceptor{
    	public boolean preHandle(..) throws Exception{
    		System.out.println("preHandle...");
    		return true;
    	}
    	public void postHandle(..) throws Exception{
    		System.out.println("postHandle...");
    	}
    	public void afterCompletion(..) throws Exception{
    		Syetm.out.println("afterCompletion...");
    	}
    }
    
  4. 定义配置类,集成WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置)

    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport{
    	@Override
    	public void addInterceptors(InterceptorRegistry registry){
    		...
    	}
    }
    
  5. 添加拦截器并设置拦截访问路径,路径可以通过可变参数设置多个

    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport{
    	@Autowired
    	private ProjectInterceptor projectInterceptor;
    	
    	@Override
    	public void addInterCeptors(InterceptorRegisetry registry){
    		regisyry.addInterceptor(proejectInterceptor).addPathPatternas("/books");
    	}
    }
    
  6. 使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)

    @Configuration
    @ComponentScan("com.hcx.controller")
    @EnableWebMvc
    public class SpringMvcConfig implements WebMvcConfigurer{
    	@Autowired
    	private ProjectInterCeptor projectInterceptor;
    	
    	public void addInterceptors(InterceptorRegistry registry){
    		registry.addInterceptor(projectInterceptor).addPatternas("/books","/books");
    	}
    }
    

拦截器执行流程

  • preHandle
    • return true
      • contrller
      • postHandle
      • afterCompletion
    • return false
      • 结束

拦截器参数

  • 前置处理

     @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("preHandle...");
            return true;
        }
    
  • 具体参数:

    • handler:被调用的处理器对象,本质上是一个方法对象,对反射计数中的Method对象进行了再包装,可用于获取拦截方法信息

    • request:请求对象

    • response:响应对象

    • modelAndView:获取页面跳转相关数据

    • ex:拿到原始程序执行过程中出现的异常,表现层出现的异常

  • 返回值

    • 返回值未false,被拦截的处理器将不执行

多个拦截器执行顺序

  • 当配置多个拦截器是,形成拦截器链
  • 拦截器链的运行属性呢参照拦截器添加顺序未准
  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
总结:
  1. 拦截器链配置方式

  2. 拦截器链的运行顺序

    • preHandle:与配置顺序相同,必定运行【配置顺序就是运行顺序】
    • postHandle:与配置顺序相反,可能不运行【配置顺序就是运行顺序反过来】
    • afterComletion: 与配置顺序相反,可能不运行【配置顺序就是运行顺序反过来】

    解释:顺序情况下的话,走到哪里出现错误返回false,则后面的postHandle拦截器都不运行,afterComletion会运行

你可能感兴趣的:(笔记,ssm笔记,spring,maven,spring,boot)