spring

概述

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

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

优势

  • 1、方便解耦,简化开发
  • 2、AOP编程的支持
  • 3、声明式事务的支持
  • 4、方便程序的测试
  • 5、方便继承各种优秀框架
  • 6、减低javaEE API的使用难度
  • 7、Java源码是经典学习范例

IOC

什么是IOC

控制反转,把对象创建和对象之间的调用过程,交给Spring进行管理

2)使用IOC目的:为了耦合度降低

正转

  • 由程序员进行对象的创建和依赖注入称为正转,程序员说了算。
Student stu = new Student();
stu.setName("张三");
stu.setAge(22);

反转

  • 由Spring容器创建对象和依赖注入称为反转,将控制权从程序员手中夺走,由给Spring容器,称为反转。
/*
容器启动,启动瞬间对象创建
打印====》无参构造方法
*/
<bean id="stu" class="mzj.pojo.Student"/>

2、IOC底层原理

1)xml解析、工厂模式、反射

Ioc(接口)

  • 1、思想基于IOC容器完成,IOC容器底层就是对象工厂

  • 2、Spring提供IOC容器实现两种方式:(两个接口)

  • 1)BeanFactory:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用

    加载配置文件时候不会创建对象,在获取对象(使用)才会创建对象

  • 2)ApplicationContext:BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

    加载配置文件时候就会把再配置文件对象进行创建

Bean管理

1、基于xml配置文件方式实现

2、基于注解方式

IOC操作Bean管理(FactoryBean)

  • 1、Spring有两种类型bean,一种普通bean,另外一种工厂bean(factoryBean)
  • 2、普通bean:配置文件中定义bean类型就是返回类型
  • 3、工厂bean:在配置文件定义bean类型可以和返回类型不一样

配置文件

App.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--配置继承接口-->
    <bean id="userDao" class="Mzj.impl.UserDaoImpl"/>
</beans>

main方法

package Mzj.Demo;

import Mzj.dao.UserDao;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserDaoDemo {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("App.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

UserDao

package Mzj.dao;

public interface UserDao {
    public void save();
}

UserDaoImpl

package Mzj.impl;


import Mzj.dao.UserDao;

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eq3TXbF1-1653223158134)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220410154414204.png)]

对象的实例

单个——加载时创建
<?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">
    <!--scope控制着单例或者实例-->
    <bean id="userDao" class="Mzj.impl.UserDaoImpl" scope="singleton"/>
</beans>
多个——获取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">
    <!--scope控制着单例或者实例-->
    <bean id="userDao" class="Mzj.impl.UserDaoImpl" scope="prototype"/>
</beans>

Bean生命周期

  • 1、通过构造器创建bean实例(无参数构造)
  • 2、为bean的属性设置值和对其他bean引用(调用set方法)
  • 3、调用bean的初始化方法(需要进行配置初始化方法
  • 4、bean可以使用了(对象获取到了)
  • 5、当容器关闭时候,调用bean的销毁方法(需要进行配置销毁的方法)
init-method:指定类中的初始化方法名称
<?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="Mzj.impl.UserDaoImpl" init-method="init"/>
</beans>
destroy-method:指定类中销毁方法名称
<?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="Mzj.impl.UserDaoImpl" destroy-method="destroy"/>
</beans>

Bean实例化三种方式

  • 无参构造方法实例化
    • 下面部分bean的注入就是一种方式
  • 工厂静态方法实例化
  • 工厂实例方法实例化

工厂静态方法实例化

App.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--添加工厂-->
    <bean id="userDao" class="Mzj.factory.StaticFactory" factory-method="getUserDao"/>
</beans>

StaticFactory

package Mzj.factory;

import Mzj.dao.UserDao;
import Mzj.impl.UserDaoImpl;

public class StaticFactory {
    public static UserDao getUserDao(){
        return new UserDaoImpl();
    }
}

main

import Mzj.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    @Test
    public static void main(String[] args) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("App.xml");
        UserDao userDao1 = (UserDao) app.getBean("userDao");
        System.out.println(userDao1);
        app.close();
    }
}

工厂实例方法实例化

App.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--添加工厂-->
    <bean id="factory" class="Mzj.factory.DynamicFactory"/>
    <bean id="userDao" factory-bean="factory" factory-method="getUserDao"/>
</beans>

Bean的依赖注入方式

Bean的依赖注入概念

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

在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。

IOC解耦只是降低他们的依赖关系,但是不会消除。例如:业务层仍会调用持久层的方法。

那这种业务蹭喝持久层的依赖关系,在使用Spring之后,就让Spring来维护了。

set方法

beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userDao" class="impl.UserDaoImpl"></bean>
    <bean id="user" class="bean.User">
        <!--set方法/>-->
        <property name="username" value="绍利临"/>
        <property name="password" value="12312312"/>
        <!--有参构造/>-->
        <!--<constructor-arg name="username" value="绍利临"></constructor-arg>-->
        <!--<constructor-arg name="password" value="12312312"></constructor-arg>-->
    </bean>
</beans>
User
package bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private String username;
    private String password;
    public void test1(){
        System.out.println(username+"======="+password);
    }
}
main
package controller;

import bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;

public class TestController {
    @Test
    public void testadd(){
        ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) app.getBean("user");
        user.test1();
    }
}

构造方法

beans.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userDao" class="impl.UserDaoImpl"></bean>
    <bean id="user" class="bean.User">
        <!--set方法/>-->
        <!--<property name="username" value="绍利临"/>-->
        <!--<property name="password" value="12312312"/>-->
        <!--有参构造/>-->
        <constructor-arg name="username" value="绍利临"></constructor-arg>
        <constructor-arg name="password" value="12312312"></constructor-arg>
    </bean>
</beans>
User
package bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private String username;
    private String password;
    public void test1(){
        System.out.println(username+"======="+password);
    }
}
main
package controller;

import bean.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;

public class TestController {
    @Test
    public void testadd(){
        ApplicationContext app = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) app.getBean("user");
        user.test1();
    }
}

p命名空间

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <!--<bean id="user" class="bean.User">-->
    <!--set方法/>-->
    <!--<property name="username" value="绍利临"/>-->
    <!--<property name="password" value="12312312"/>-->
    <!--有参构造/>-->
    <!--<constructor-arg name="username" value="绍利临"></constructor-arg>-->
    <!--<constructor-arg name="password" value="12312312"></constructor-arg>-->
    
    <!--p标签方便开发-->
    <bean id="user" class="bean.User" p:password="123123" p:username="绍利临">
    </bean>
</beans>

注入空值或特殊符号

  • 空值
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <bean id="user" class="bean.User">
        <!--set方法-->
        <property name="username" value="绍利临"/>
        <property name="password" value="12312312"/>
        <!--空值-->
        <property name="test">
            <null></null>
        </property>
</bean>
</beans>
  • 特殊符号
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">
    <bean id="user" class="bean.User">
        <!--set方法-->
        <property name="username" value="绍利临"/>
        <property name="password" value="12312312"/>
        <!--特殊符号-->
        <property name="test">
            <value><![CDATA[<<南京>>]]></value>
        </property>
    </bean>
</beans>

注入外部bean

  • beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
">

    <bean id="userDao" class="impl.UserDaoImpl"></bean>
    <bean id="userService" class="impl.UserServiceImpl">
        <!--注入userDao对象-->
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>
  • UserDaoImpl
package impl;

import dao.UserDao;

public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("userDao…………");
    }
}
  • UserServiceImpl
package impl;

import dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import service.UserService;

public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    //写了Autowired就不用写这个set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

提取list集合类型属性注入

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:util="http://www.springframework.org/schema/util"
        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/util http://www.springframework.org/schema/util/spring-util.xsd
">
<!--提取list集合烈性属性注入-->
<util:list id="bookList">
    <value>绍利临</value>
    <value>李德新</value>
    <value>增配设</value>
</util:list>
<!--属性注入使用-->
    <bean id="book" class="">
        <property name="" ref="bookList"></property>
    </bean>
</beans>

Bean的依赖注入的数据类型

  • 普通数据类型
  • 引用数据类型
  • 集合数据类型
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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="Mzj.impl.UserDaoImpl">
        <!--普通数据-->
        <property name="username" value="zhangsan"/>
        <property name="age" value="18"/>
        <!--列表数据-->
        <property name="strList">
            <list>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
        </property>
        <!--map数据-->
        <property name="userMap">
            <map>
                <entry key="user1" value-ref="user1"/>
                <entry key="user2" value-ref="user2"/>
            </map>
        </property>
        <!--properties数据-->
        <property name="properties">
            <props>
                <prop key="p1">p1</prop>
                <prop key="p2">p2</prop>
                <prop key="p3">p3</prop>
            </props>
        </property>
    </bean>
    
    <!--map原始数据-->
    <bean id="user1" class="Mzj.domain.User">
        <property name="name" value="tom"/>
        <property name="addr" value="beijing"/>
    </bean>
    <bean id="user2" class="Mzj.domain.User">
        <property name="name" value="Mzj"/>
        <property name="addr" value="guangzhou"/>
    </bean>
</beans>

UserDaoImpl

package Mzj.impl;


import Mzj.dao.UserDao;
import Mzj.domain.User;
import lombok.Data;

import java.util.List;
import java.util.Map;
import java.util.Properties;

@Data
public class UserDaoImpl implements UserDao {
    private List<String> strList;
    private Map<String, User> userMap;
    private Properties properties;
    private String username;
    private int age;

    @Override
    public void save() {
        System.out.println(username + "=======" + age);
        System.out.println(strList);
        System.out.println(userMap);
        System.out.println(properties);
    }
}

map的User对象

package Mzj.domain;

import lombok.Data;

@Data
public class User {
    private String name;
    private String addr;
}

引入其他文件设置

    <import resource="文件名字"/>

Spring相关API

ApplicationContext的实现类

  • 1、ClassPathXmlApplicationContext

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

  • 2、FileSystemXmlApplicationContext

    文件绝对路径导入

  • AnnotationConfigApplicationContext

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J7gQIPCg-1653223158135)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220412205440488.png)]

<context:component-scan base-package="Mzj.dao"/>

加载properties文件

<?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:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置数据源对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
     <!--jdbc模板对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
     <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

新注解自动配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-llQM11XY-1653223158135)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220412214740017.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4bELACu1-1653223158136)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504183118476.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qOi5fzDl-1653223158136)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504183316476.png)]

完全注解开发

main
package Mzj.controller;

import Mzj.config.SpringConfig;
import Mzj.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.testng.annotations.Test;

public class TestController {
    @Test
    public void testadd(){
        ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserServiceImpl userService = (UserServiceImpl) app.getBean("userService");
        userService.add();
    }
}
UserDaoImpl

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EpeJ9kF7-1653223158136)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504185657272.png)]

UserServiceImpl

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dY3gz5P7-1653223158137)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504185732643.png)]

监听器

  • 应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件)方式获取的,但是每次从容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(spring配置文件),这样的弊端是配置文件加载多次,应用上下文对象创建多次。
  • 在web项目于中,可以使用ServletContextListener监听web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

以类来开发注解

SpringConfiguration类

package Mzj.config;

import org.springframework.context.annotation.*;

//代表标志该类是Spring的核心配置类
@Configuration
//<context:component-scan base-package="Mzj.impl"/>
@ComponentScan("Mzj.impl")
//<!--加载外部的配置-->
//<!--<context:property-placeholder location="classpath:jdbc.properties"/>-->
@Import(sqlconfig.class)
public class SpringConfiguration {

}

sqlconfig类

package Mzj.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@PropertySource("classpath:jdbc.properties")
public class sqlconfig {
    @Bean("dataSource")
    public DataSource dataSource() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///day17");
        dataSource.setUser("root");
        dataSource.setPassword("936541");
        return dataSource;
    }
}

配置监听器

web.xml

<listener>
    <listener-class>Mzj.listener.ContextLoaderListener</listener-class>
</listener>
package Mzj.listener;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("App.xml");
        ServletContext servletContext = sce.getServletContext();
        servletContext.setAttribute("app",app);
        System.out.println("调用了监听器");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContextListener.super.contextDestroyed(sce);
    }
}
package Mzj.web;

import Mzj.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(value = "/UserServiceServlet")
public class UserServiceServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        ClassPathXmlApplicationContext app = (ClassPathXmlApplicationContext) servletContext.getAttribute("app");
        UserService bean = app.getBean(UserService.class);
        System.out.println(bean);
    }
}

spring5新功能

  • 1、整个Spirng5框架的代码基于java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除了
  • 2、Spring5框架自带了通用的日志封装
  • 1)Spring5已经移除了Log4jConfigListener,官方建议使用Log4j2

整合日志框架

log4j2

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序:OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL-->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <appenders >
        <console name="Console" target="SYSTEM_OUT">
            <!--输出日志信息到控制台-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} -msg%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目于的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

Spring5框架核心容器支持@Nullable注解

1)@Nullable注解可以使用在方法上面,属性上面,参数上面,标识方法返回可以为空,属性值可以为空,参数值可以为空

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KA87FzuF-1653223158137)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504200827340.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G2AO34zU-1653223158137)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504200839697.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LXe4kvbb-1653223158137)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504200858825.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zqQHtDvw-1653223158137)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504201438359.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RG5VKq7d-1653223158138)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220504200352774.png)]

整合Junit4

package Ts;

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

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
//@ContextConfiguration("classpath:beans.xml")
public class Jtest4 {
    @Autowired
    UserService userService;
    @Test
    public void test1(){
        userService.add();
    }
}

整合Junit5

package Ts;

import Mzj.config.SpringConfig;
import Mzj.service.UserService;
import org.junit.Test;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;

import javax.annotation.Resource;

@SpringJUnitConfig(classes = SpringConfig.class)
//@SpringJUnitConfig(locations = "classpath:beans.xml")
public class Jtest5 {
    @Resource
    UserService userService;
    @Test
    public void test1(){
        userService.add();
    }
}

Webflux

Webflux使用当前比较流程响应式编程出现的框架,是一种异步非阻塞的框架,异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的。

java8以及之前版本

  • 提供的观察者模式俩个乐力Observer和Observable

响应式编程(Reactor)

  • 1)响应式编程操作中,Reactor是满足Reactive规范框架
  • 2)Reactor有俩个核心类,Mono 和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现翻发布者,返回0或者1个元素
  • 3)Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:

元素值,错误信息,完成信号,错误信号和完成信号都代表种植信号,终止信号用于告诉订阅者数据流结束了

4)

package mzj.webflux_01.reactor8;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class TestReactor {
    public static void main(String[] args) {
        Flux.just(1,2,3,4);
        Mono.just(1);

        Integer[] array = {1,2,3,4};
        Flux.fromArray(array);
        List<Integer> list = Arrays.asList(array);
        Flux.fromIterable(list);

        Stream<Integer> stream = list.stream();
        Flux.fromStream(stream);
    }
}

5)三种信号特点

  • 错误信号和完成信号都是终止信号,不能共存的
  • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示空数据流
  • 如果没有错误信号,没有完成信号,表示无限数据流

6)调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vcTL6I8o-1653223158138)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220506203758197.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9pRA94aZ-1653223158138)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220506205815089.png)]

SpringMVC

什么是MVC

MVC是一种软件架构的思想

M:Model,模型层,之工程中的JavaBean,作用是处理数据

JavaBean分为两类:

  • 一类称为实体类Bean:专门存储业务数据的
  • 一类称为业务处理Bean:指Service或Dao对象,专门用于处理业务逻辑和数据访问。

V:View视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据

C:Controller,控制层,指工程中的servlet,作用是用来接收请求和响应浏览器

MVC的工作流程:

用户通过视图层发送请求到服务器,在服务器中请求被Controller调用相应的Model层处理请求,处理完毕将结果返回到Controoler,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器

注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet

web开发底层是servlet,框架是在servlet基础上面加入一些功能,让你做web开发方便。

SpringMVC就是一个Spring。Spring是容器,ioc能够管理对象,@ControllerSpringMVC能够创建对象,放入到容器中(SpringMVC容器),springMVC容器中放的是控制器对象,

我们要做的是使用@Controller创建对象,把对象放入到springMVC容器中,把创建的对象作为控制器使用,这个控制器对象能接收用户的请求,显示处理结果,就当作是一个servlet使用。

框架的核心对象

  • 1)DispatcherServlet叫做中央调度器,是一个servlet,它的父类是继承HttpServlet
  • 2)DispatcherServlet也叫做前端控制器(front controller)
  • 3)DispatcherServlet负责接收用户提交的请求,调用其他的控制器对象,并把请求的处理结果显示给用户

快速代码实现

默认配置web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-appxmlns="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">
    <!--声明注册springmvc的核心对象DispathcherServlet
    需要再tomcat服务器启动后,创建DispatcherServlet对象的实例。
    为社么要创建DispatcherServlet对象的实例呢?
    因为DispatcherServlet在他的创建过程中,会同时创建springmvc容器对象,
    读取springmvc的配置文件,把这个配置文件中的对象都创建好,
    当用户发起请求时就可以直接适用对象了

    servlet的初始化会执行init()方法。DispatcherServlet在init()中{
    //创建容器,读取配置文件
    WebApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
    //把容器对象放入到ServletContext中
    getServletContext().setAttribute(key,ctx);
    } -->

    <!--读取的文件默认是 `servlet-name` 定义的 加上 `servlet`
    /WEB-INF/springmvc-servlet.xml-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--自定义springmvc读取的配置文件的位置-->
        <init-param>
            <!--springmvc的配置文件的位置的属性-->
            <param-name>contextConfigLocation</param-name>
            <!--指定自定义的文件的位置-->
            <param-value>classpath:MVC.xml</param-value>
        </init-param>
        <!--在tomcat启动后,创建Servlet对象
        load-on-startup:表示tomcat启动后创建对象的顺序。
        它的值是整数,数值越小,tomcat创建对象的时间越早。
        大于等于0的整数
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--
        使用框架的时候,url-pattern可以使用两种值
        1、使用扩展名方式,语法 *.xxxx, xxxx是自定义的扩展名 。 常用的方式 *.do ,*.action, *.mvc等
        http://localhost/some.do

        2.使用斜杠 "/"
        -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

创建请求控制器

由于前端控制器对浏览器发送的请求进行了统一的处理,到那时具体的请求有不同的处理过程,因此需要创建具体请求的类,即请求控制器

请求控制器中每一个处理请求的方法成为控制器方法

因为SpringMVC的控制器由一个POJO(普通的java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在

package controller;

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

@Controller
public class MyController {
    @RequestMapping("/some")
    public ModelAndView dosome(){
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","欢迎使用mvc");
        mv.addObject("fun","执行的是some方法");
        //mv.setViewName("/show.jsp");
        mv.setViewName("show");
        return mv;
    }
}

mvc.xml—jsp

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="controller"/>
    <!--视图解析器,帮助开发人员设置视图文件的路径-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/subview/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

mvc.xml—themleaf

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        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 https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="Mzj.controller"/>
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="order" value="1"/>
        <property name="characterEncoding" value="UTF-8"/>
        <property name="templateEngine">
            <bean class="org.thymeleaf.spring5.SpringTemplateEngine">
                <property name="templateResolver">
                    <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
                        <!-- 视图前缀 -->
                        <!--在webapp目录下新建目录tm-->
                        <property name="prefix" value="/tm/"/>
                        <!-- 视图后缀 -->
                        <property name="suffix" value=".html"/>
                        <property name="templateMode" value="HTML5"/>
                        <property name="characterEncoding" value="UTF-8" />
                    </bean>
                </property>
            </bean>
        </property>
    </bean>

</beans>

@RequestMapping详解

// 标识到类上:设置映射请求的请求路径的初始信息
// 标识到方法上:设置映射请求请求路径的具体信息
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

value

@RequestMapping(value = {"/success1","success2"})
public String success(){
    return "success";
}

method

@RequestMapping(value = {"/success1","success2"},
                method = {RequestMethod.GET,RequestMethod.POST})
public String success(){
    return "success";
}

params属性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOdbekl3-1653223158138)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505152142299.png)]

@RequestMapping(value = {"/success1","success2"},
                method = {RequestMethod.GET,RequestMethod.POST},
                params = {"username != admin"}
               )
public String success(){
    return "success";
}

SpringMVC支持ant风格的路径

  • 1、?:表示任意的单个字符

    @GetMapping(value = {"/a?a/testAnt1"})
    public String testAnt1(){
        return "success";
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uuyMVb2o-1653223158139)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505153932670.png)]

  • 2、*:表示任意的0个或多个字符

        @GetMapping(value = {"/a*a/testAnt1"})
        public String testAnt1(){
            return "success";
        }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Uv4aVTCJ-1653223158139)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505154049924.png)]

  • 3、**:表示任意的一层或多层目录

    @GetMapping(value = {"/**/testAnt1"})
    public String testAnt3(){
        return "success";
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V0Ovh7Dp-1653223158139)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505154510938.png)]

注意:在使用** 时,只能使用/**/xxx的方式

RESTFul风格

  • 表现层资源状态转移
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mi11654O-1653223158139)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220510093919595.png)]
@GetMapping(value = {"/testRest1/{id}/{username}"})
public String testRest1(@PathVariable("id") String id,@PathVariable("username") String username){
    System.out.println("id:"+id+",username:"+username);
    return "success";
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jtLNotyO-1653223158140)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505160917117.png)]

RESTFul的实现

web.xml

<filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  • put方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fMfJ1goL-1653223158140)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220510103156534.png)]

域对象共享数据

  • 1、servletAPI向request域对象共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8SJHjszZ-1653223158140)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505164517660.png)]

  • 2、使用ModeAndView向request域对象共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JK8LCI6h-1653223158140)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505164532555.png)]

  • 3、使用Model向request域对象共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GrzaRnZ6-1653223158140)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505164546410.png)]

  • 4、使用Map向request域对象共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pwuwU2rU-1653223158140)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505164555061.png)]

  • 5、使用ModelMap向request域对象共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kAOoxk7e-1653223158141)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505164616643.png)]

  • 这些实质上就是BuindingAwareModelMap

  • 6、使用session域共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tw1cUvZ1-1653223158141)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505165936768.png)]

  • 7、向application域共享数据

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXqm5AuC-1653223158141)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505170036551.png)]

SpringMVC的执行流程

  • 1、用户发送请求至前端控制器DispatcherServlet。
  • 2、DispatcherServlet收到请求调用Handlermapping处理器映射器
  • 3、处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找)、生成处理器对象以及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  • 4、DispatcherServlet调用HandlerAdapter处理器适配器
  • 5、Handleradapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
  • 6、Controller执行完成返回ModelAndView。
  • 7、HandlerAdapter将controller执行结果ModelAndView 返回给DispatcherServlet。
  • 8、DispatcherServlet将ModelAndView传给Viewreslover视图解析器
  • 9、Viewreslover解析后返回具体View。
  • 10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

SpringMVC的执行流程(大概流程)

浏览器

——>请求网页——>

前端控制器DispathcerServlet

——>请求查询Handler ——>

处理器映射器HandlerMapping

——>返回处理器执行链HandlerExecutionChain——>

前端控制器DIspathcherServlet

——>请求Handler——>

处理器适配器HandlerAdaptor

——>请求 ——>

处理器Handler

——>响应——>

处理适配器HandlerAdaptor

——>返回ModelAndView

前端控制器DispatcherServlet

——>请求视图解析器——>

视图解析器ViewResolver

——>返回视图View对象——>

前端控制器DispatcherServlet

——>渲染视图——>

视图页面jsp

SpringMVC组件解析

  • 1、@RequstMapping

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

位置:

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

属性:

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

例如:

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

SpringMVC注解解析

1、mvc命名空间引入

命名空间

xmlns:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc"

约束地址

http://www.springframework.org/schema/context      http://www.springframework.org/schema/context/spring-context-4.2.xsd

http://www.springframework.org/schema/mvc      http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd

2、组件扫描

SpringMVC基于Spring容器,所以在进行SpringMVC操作时,需要将Controller存储到Spring容器中,如果使用@Controller注解标注的话,就需要使用

<context:compent-scan base-pachage="controller文件"/>

进行组件扫描

知识要点

SpringMVC的相关组件

  • 前端控制器:
  • 处理器映射器:
  • 处理器适配器:
  • 处理器:
  • 视图解析器:
  • 视图:

SpringMVC的注解和配置

  • 请求映射注解:@RequestMapping

  • 视图解析器配置:
    REDIRECT_URL_PREFIX=“redirect:”

    FORWARD_URL_PREFIX=“forward:”

    prefix=“”;

    suffix=“”;

回写数据

2、返回对象或集合

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

<mvc:annotation-driven/>

在SpringMVC的各个组件中,处理映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。

在使用mvc:annotation-driven自动加载 (处理映射器)和(处理适配器),可用在Springj-xml.xml配置文件中使用mvc:annotation-driven替代注解处理器和适配器的配置。

同时使用mvc:annotation-driven默认底层就会继承jackson进行对象或集合的json格式字符串的转换。

数据响应

SpringMVC的数据响应方式

  • 1、页面跳转
    • 直接返回字符串
    • 通过ModelAndView对象返回
  • 2、回写数据
    • 直接返回字符串
    • 返回对象或集合

获得集合参数

静态资源放行

<!--开发资源的访问-->
<!--    <mvc:resources mapping="/js/**" location="/js/"/>-->
<!--    <mvc:resources mapping="/img/**" location="/img/"/>-->
<mvc:default-servlet-handler></mvc:default-servlet-handler>

乱码问题

web.xml

<!--    配置去全局过滤的filter-->
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

参数绑定注解@requestParam

  • value:与请求参数名称
  • required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
  • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
    @RequestMapping(value = "/quick16")
    @ResponseBody
    public void save16(@RequestParam(value = "name",required = false,defaultValue = "Mzj") String username) {
        System.out.println(username);
    }

Restful风格的参数

概述

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

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

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

Restful参数获取

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

转换器

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

<!--    声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="converter.DateConverter"/>
        </list>
    </property>
</bean>
package converter;

import org.springframework.core.convert.converter.Converter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String daeStr) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        try {
            date = format.parse(daeStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

文件上传解析器

<!--    配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="maxUploadSizePerFile" value="5242800"/>
    <property name="maxUploadSize" value="5242800"/>
</bean>

JdbcTemplate

package test;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import java.beans.PropertyVetoException;

public class JdbcTemplateTest {
    @Test
    public void test1() throws PropertyVetoException {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql:///day17");a
        dataSource.setUser("root");
        dataSource.setPassword("936541");
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        int row = jdbcTemplate.update("insert into account values(?,?)", "tom", 5000);
        System.out.println(row);
    }
}

Spring产生JdbcTemplate对象

SpringMVC拦截器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FzjvFSsh-1653223158141)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507203410483.png)]

<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="interceptor.MyInterceptor1"/>
    </mvc:interceptor>
</mvc:interceptors>
package interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.handler.Handler;

public class MyInterceptor1 implements HandlerInterceptor {
    //在目标方法执行之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle");
        return true;//false 不执行
    }

    //在目标方法执行之后 视图返回之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle");
    }

    //在整个流程都执行完毕后 执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion");
    }
}
结果:
preHandle
目标资源执行
postHandle
afterCompletion

SpringMVC异常处理

简单异常处理器SimpleMappingExceptionResolver

<!--配置简单映射异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!--<property name="defaultErrorView" value="error"/>-->
    <property name="exceptionMappings">
        <map>
            <entry key="java.lang.ClassCastException" value="error1"/>
            <entry key="java.lang.ClassCastException" value="error2"/>
        </map>
    </property>
</bean>

注解

package mzj.spring_boot_web.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
    /**
     *  指定spring的配置类
     * @return
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{};
    }

    /**
     * 指定springmvc的配置类
     * @return
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        //上面的类似
        return new Class[]{myConfig.class};
    }

    /**
     * 指定DispatcherServlet的映射规则,集url-partern
     * @return
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    /**
     *     ctrl+o
     *     重写方法,有很多反复噶
     */
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SQ0OsYD6-1653223158142)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507204541501.png)]

HttpMessageConverter

  • 报文信息转换器,将请求报文转换为java对象,获将java对象转换为响应报文HttpMessageConverter提供了两个注解和两个类型:

@RequestBody

@ResponseBody

@RequestEntity

@ResponseEntity

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WIpdkhpj-1653223158142)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507194204973.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RN3z3sOy-1653223158142)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507194242329.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eXDEIsiZ-1653223158142)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507194348533.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dgdf1gwe-1653223158142)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507195221637.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ZG5eCdh-1653223158143)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220507195246250.png)]

Spring 的AOP简介

概述

AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,l利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的课重用性,同时提高了开发的效率。

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

AOP底层使用哪种代理方式

AOP底层使用动态代理

有两种情况动态代理

  • 1、有接口情况,使用JDK动态代理
  • 2、没有接口情况,使用CGLIB动态代理

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

实现JDK方式

方法有三个参数:

第一参数:类加载器

第二参数:增强方法所在的类,这个类实现的接口,支持多个接口

第三参数:实现这个接口InvocationHandler,创建代理对象,写增强方法

package mzj.proxy5;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {
    public static Object getAgent(Service service, AOP aop) {
        return Proxy.newProxyInstance(
                //类加载器
                service.getClass().getClassLoader(),
                //目标对象实现的所有接口
                service.getClass().getInterfaces(),
                //代理功能实现
                new InvocationHandler() {

                    @Override
                    public Object invoke(
                            //生成代理对象
                            Object proxy,
                            //正在被调用的目标方法buy(),show()
                            Method method,
                            //目标方法的参数
                            Object[] args) throws Throwable {
                        Object obj = null;
                        try {
                            //切面
                            aop.before();
                            //业务
                            obj = method.invoke(service, args);
                            //切面
                            aop.after();
                        } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException e) {
                            aop.exception();
                        }
                        //切面
                        return obj;
                    }
                }
        );
    }
}

创建接口,实现方法

package Mzj;

public interface UserDao {
    public int add(int a,int b);
    public String update(String id);
}
package Mzj;

public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public String update(String id) {
        return id;
    }
}

使用JDKProxy

package Mzj;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class JDKProxy {
    public static void main(String[] args) {
        Class[] interfaces = {UserDao.class};
        //Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
        //    @Override
        //    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //        return null;
        //    }
        //});
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
        int add = dao.add(1, 2);
        System.out.println("re:" + add);
    }
}

//创建代理对象代码
class UserDaoProxy implements InvocationHandler {
    //把创建的是谁的代理对象,把谁传递过来
    private Object obj;

    public UserDaoProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法之前
        System.out.println("方法之前执行…………" + method.getName() + ":传递的参数……" + Arrays.toString(args));
        //被增强的方法执行
        Object res = method.invoke(obj, args);
        //方法之后
        System.out.println("方法之后执行…………" + obj);

        return res;
    }
}

AOP术语

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8lFsWYY-1653223158143)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505203617266.png)]

切面:就是哪些重复的,公共的,通用的功能称为切脉你,例如:日志,事务,权限

连接点:就是目标方法。因为在目标方法中要实现目标方法的功能和切面功能。

切入点:多个连接点构成切入点。切入点可以是一个没有标反复噶,可以是一个类中的所有方法,可以是某个包下的所有类中的方法。

AOP准备工作

  • 1、Spring框架一般都是基础与AspectJ实现AOP操作
  • 1)什么是AspectJ
  • AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
  • 2、基于AspectJ实现AOP操作
  • 1)基于xml配置配置文件实现
  • 2)基于注解方式实现(使用)

原生

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cA5fGEpC-1653223158143)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220511215612010.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YHXm7mN-1653223158143)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220511220052086.png)]

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
">
    <!--目标对象-->
    <bean id="target" class="aop.TargetTest"/>
    <!--切面对象-->
    <bean id="myAspect" class="aop.MyAspect"/>
    <!--配置织入:告诉spring框架 哪些方法需要惊醒增强(前置、后置…………)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--抽取切点表达式-->
            <aop:pointcut id="myPointcut" expression="execution(* aop.*.*(..))"/>
            <!--切面:切点 + 通知-->
            <!--<aop:before method="before" pointcut="execution(public void aop.TargetTest.save())"/>-->
            <!--<aop:before method="before" pointcut="execution(* aop.*.*(..))"/>-->
            <!--<aop:around method="around" pointcut="execution(* aop.*.*(..))"></aop:around>-->
            <!--<aop:after-throwing method="afterThrowing" pointcut="execution(* aop.*.*(..))"/>-->

            <aop:around method="around" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

例图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqeRlIsB-1653223158144)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220511215301758.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVTi94ME-1653223158144)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220511215341275.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x9PXLWjo-1653223158144)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220511215219988.png)]

切点表达式语法

execution([修饰符]返回值/类型 包名.类名.方法名)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-neIDuQRP-1653223158144)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505204453427.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ueuQdI4f-1653223158144)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505204503985.png)]

  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号*代表任意
  • 包名与类名之间一个点.代表当前包下的类,俩个点..表示当前包及子包下的类
  • 参数列表可以使用两个点..表示任意个数,任意类型的参数列表
<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"/>
  • 代理的切换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Djy7s5v-1653223158145)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220511221133509.png)]

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

切点表达式抽取

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
">
    <!--目标对象-->
    <bean id="target" class="aop.TargetTest"/>
    <!--切面对象-->
    <bean id="myAspect" class="aop.MyAspect"/>
    <!--配置织入:告诉spring框架 哪些方法需要进行增强(前置、后置…………)-->
    <aop:config>
        <!--声明切面-->
        <aop:aspect ref="myAspect">
            <!--抽取切点表达式-->
            <aop:pointcut id="myPointcut" expression="execution(* aop.*.*(..))"/>
            <aop:around method="around" pointcut-ref="myPointcut"/>
        </aop:aspect>

    </aop:config>
</beans>

基于注解的AOP开发

1、创建目标接口和目标类(内部有切点)
2、创建切面类(内部有增强方法)
3、将目标类和切面类的对象创建权交给spring
4、在切面类中使用注解配置织入关系
5、在配置文件中开启组件扫描和AOP的自动代理
6、测试

1、创建类,在类中定义方法

package aopanno;

public class UserDaoImpl {
    public void add() {
        System.out.println("add…………");
    }
}

2、创基增强类(编写增强逻辑)

1)在增强类里面,创建放啊,让不同方法代表不同通知类型

package aopanno;
//被增强的类
public class UserProxy {
    public void before(){
        System.out.println("beforea……");
    }
}

3、进行通知的配置

1)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: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="aopanno"/>
</beans>

2)使用注解创建User和UserProxy对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zy7rAaVQ-1653223158145)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505205736571.png)]

3)在增强类上面添加注解@aspect

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BjK3jOUM-1653223158145)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505210913497.png)]

4)在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:context="http://www.springframework.org/schema/context"
       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/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

">
    <!--开启注解扫描-->
    <context:component-scan base-package="aopanno"/>
    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy/>
</beans>

4、配置不同类型的通知

1)在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点

package mzj.s01;

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

import java.util.Arrays;
import java.util.Locale;

/**
 * 切面类,包含各种切面方法
 */
@Component
@Aspect//交给AspectJ的框架去识别切面类
public class MyAspect {

    /**
     * 所有切面的功能都是由切面方法来实现
     * 可以将各种切面都在此类中进行开发
     * <p>
     * 前置通知的切面方法规范
     * 1)访问权限是public
     * 2)方法的返回值是void
     * 3)方法名称自定义
     * 4)方法没有参数,如果有也只是JoinPoint类型
     * 5)必须使用@Before注解来声明切入的实际是前且功能和切入点
     * 参数:value 指定切入点表达式
     */
    @Before(value = "execution(public String mzj.s01.SomeServiceImpl.dosome(String,int))")
    public void myBefore(JoinPoint joinPoint) {
        System.out.println("切面方法中的前置通知功能实现");
        System.out.println(joinPoint.getSignature());
        System.out.println(Arrays.toString(joinPoint.getArgs()));
    }

    /**
     * 后置通知的方法的规范
     * 1)访问权限时public
     * 2)反复噶没有返回值void
     * 3)方法名称自定义
     * 4)方法有参数(也可以没有参数,如果目标方法没有返回值,则可以写无参放啊,
     * 但一般写有参,这样可以处理无参可以处理有参),这个切面方法的参数就是目标方法的返回值
     * 5)使用@AfterReturning注解表名是后置通知
     * 参数:value:指定切入点表达式
     * returning:指定方法的返回值的名称,则名称必须与切面方法的参数名称一致
     */
    @AfterReturning(value = "execution(* *.*(..))", returning = "obj")
    public void myAfterReturning(Object obj) {
        System.out.println("后置通知功能实现");
        if (obj != null) {
            //判断类型是否为Stirng
            if (obj instanceof String) {
                obj = obj.toString().toUpperCase();
                System.out.println("在切面方法中目标方法的返回值" + obj);
                //这个并不能改变最后的结果,
                // 要有一个实体类来改变
            }
        }
    }

    /**
     * 环绕通知方法的规范
     * 1)访问权限是public
     * 2)切面方法有返回值,此返回值就是目标方法的返回值
     * 3)方法名称自定义
     * 4)放啊有参数,此参数就是目标放啊
     * 5)回避异常
     * 6)使用@Around注解声明是环绕通知
     * 参数:
     * value:指定切入点表达式
     */
    @Around("execution(* *.*(..))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //前切功能实现
        System.out.println("环绕通知的前置功能");
        //目标方法调用
        Object obj = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        //后切功能实现
        System.out.println("环绕通知的后置功能");
        return obj.toString().toUpperCase();
    }

    /**
     * 无论方法是否正常执行,最终通知的代码都会被执行
     * 最终通知方法规范
     * 1)访问权限式public
     * 2)方法没有返回值
     * 3)方法名称自定义
     * 4)方法没有参数,如果有也只能是JoinPoint
     * 5)使用@After注解表明是最终通知
     * 参数:
     * value:指定切入点表达式
     */
    @After(value = "execution(* *.*(..))")
    public void myAfter() {
        System.out.println("最终通知的功能");
    }
}

使用方法进行抽取

package aopanno;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//增强的类
@Component
@Aspect
public class UserProxy {
    @Pointcut(value = "execution(* aopanno.UserDaoImpl.add(..))")
    public void pointDemo(){
        
    }
    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "pointDemo()")
    public void before(){
        System.out.println("beforea1……");
    }
}

增强优先级

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TeUhvKnn-1653223158145)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220505211536697.png)]

编程式事务控制相关对象

PlatformaTransactionManager

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

方法说明
TransactionStatus getTransaction(TransactionDefination defination)获取事务的状态信息
void commit(TransactionStatus status)提交事务
void rollbakc(TransactionStatus status)回滚事务

TransactionDefinition

方法说明
int getisolationLevel()获得事务的隔离级别
int getPropogationBehavior()获得事务的传播行为
int getTimeout()获得超时时间
boolean isreadOnly()是否只读
  • 1、事务隔离级别

  • mysql:默认的隔离级别是REPEATABLE_READ,也就是可重复读

  • Oraclr:支持READ_COMMITTEDSERIALIZABLE这两种事务隔离级别。默认系统事务隔离级别READ_COMMITTED

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

  • ISOLATION_DEFAULT:默认隔离级别

  • ISOLATION_READ_UNCOMMITTED:允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

  • ISOLATTION_READ_COMMITTED:只能读取到已经提交的数据。Oracle等多数数据库默认改级别(不可重复读)

  • ISOLATION_REPEATABLE_READ:可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还是存在幻像读,到那时innoDB解决了幻读

  • ISOLATION_SERIALIZABLE:完全串行化的读,每次读都选哟获得表级共享锁,读写相互都会阻塞

  • 2、事务传播行为

  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)

  • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常

  • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起

  • NOT_SUPPORTED:以非事务方式执行操作,如果当前在事务中,把当前事务挂起

  • NEVER:以非事务方式运行,如果当前存在事务,抛出异常

  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似操作

  • 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置

  • 是否只读:建议查询时设置为只读

TransactionStatus

TransactionStatus接口提供的是事务具体的运行状态

方法说明
boolean hasSavepoint()是否存储回滚点
boolean isCompleted()事务是否完成
boolean isNewTransaction()是否是新事务
boolean isRollbackOnly()事务是否回滚

三大对象

  • PlatformTransactionmanager
  • TransactionDefinition
  • TransactionStatus

基于XML的声明式事务控制

什么是声明式事务控制

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

声明式事务处理的作用

  • 事务管理不侵入开发的组件。具体俩说业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可。

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

声明式事务控制的实现

声明式事务控制明确事项:

  • 谁是切点?
  • 谁是通知?
  • 配置切面?
<!--目标对象 内部的方法就是切点-->
<bean id="accountService" class="Mzj.Impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"/>
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--通知 事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--配置事务的AOP织入-->
<aop:config>
        <aop:pointcut  id="txPointcit" expression="execution(* Mzj.Impl.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcit"/>
        <!--事务优先级-->
        <aop:advisor order="1" advice-ref="txAdvice" pointcut="execution(* Mzj.impl.*.*())"/>
</aop:config>

注解配置声明式事务控制解析

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--事务的注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
 @Transactional(propagation = Propagation.REQUIRED,//事务的传播特性
            noRollbackForClassName = "ArithmeticException",//指定发生什么异常不回滚,使用的是异常的名称
            noRollbackFor = ArithmeticException.class,//指定什么异常发生不回滚,使用的是异常的类型
            rollbackForClassName = "",//指定发生什么异常必须回滚
            rollbackFor = ArithmeticException.class,//指定发生什么异常必须回滚
            timeout = -1,//连接超时设置,默认值是-1,表示永不超时
            readOnly = false,//默认是false,如果是查询操作,必须设置为true
                isolation = Isolation.DEFAULT)//使用数据库自己的隔离级别

Mybatis

入门

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>
    <!--注册实体类的别名-->
    <typeAliases>
        <!--单个实体类别名注册-->
        <!--<typeAlias type="pojo.Student" alias="student"/>-->
        <!--批量注册别名
        别名是类名的驼峰命名法
        -->
        <package name="pojo"/>
    </typeAliases>

    <!--配置数据库的环境变量(数据库连接配置)-->
    <environments default="development">
        <!--开发时在公司使用的数据库配置-->
        <environment id="development">
            <!--配置事务管理器
            type:指定事务管理的方式
            JDBC:事务的控制交给程序员处理
            ManAGED:由容器(Spring)来管理事务
            -->
            <transactionManager type="JDBC"/>
            <!--裴矩数据源
            type:指定不同的配置方式
            JNDI:java命名目录接口,在服务器端进行数据库连接池的管理
            POOLED:使用数据库连接池
            UNPOLLED:不适用数据库连接池
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--
    注册mapper.xml文件
    resource:从resources目录下找指定名称的文件注册
    url:使用绝对路径注册
    class:动态代理方式下的注册
    -->
    <mappers>
        <mapper resource="Studentmapper.xml"></mapper>
    </mappers>
</configuration>
  • 1、environments标签

其中,事务管理器(TransacctionManager)类型有两种:

  • JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
  • MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。

其中,数据源(dataSource)类型有三种:

  • UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
  • POOLED:这种数据源的实现利用"池"的概念和JDBC连接对象组织起来。
  • JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
create database ssm default charset utf8;

use ssm;

create table `student`(
`id` int(11) auto_increment primary key,
`name` varchar(255) default null,
`email` varchar(255) default null,
`age` int(11) default null
)engine=InnoDB default charset=utf8;

create table `users`(
`id` int(11) auto_increment primary key,
`username` varchar(32) comment '用户名称',
`birthday` date default null comment '生日',
`sex` char(2) default null comment '性别',
`address` varchar(256) default null comment '地址'
)engine=InnoDB AUTO_INCREMENT=27 default charset=utf8;

insert into student(name,email,age) values("张三","zhangsan@126.com",11);
insert into student(name,email,age) values("李四","lisi@126.com",22);
insert into student(name,email,age) values("王五","wangwu@qq.com",33);
insert into student(name,email,age) values("赵六","zhaoliu@qq.com",44);

insert into users values(1,"赵六","2001-02-01","2","安徽");
insert into users values(2,"赵七","2001-02-02","1","高州");
insert into users values(3,"赵八","2001-02-03","2","广州");
insert into users values(4,"赵九","2001-02-04","1","高明");
insert into users values(5,"赵十","2001-02-05","1","日本");

mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="mapper.UsersMapper">
    <delete id="deleter" parameterType="int">
        delete
        from users
        where id = #{id}
    </delete>
    <!--
    查询全部用户信息
    -->
    <select id="getAll" resultType="users">
        select id, username, birthday, sex, address
        from users
    </select>
    <select id="getById" resultType="users" parameterType="int">
        select id, username, birthday, sex, address
        from users
        where id = #{id}
    </select>
    <select id="getByName" resultType="users" parameterType="String">
        select id, username, birthday, sex, address
        from users
        where username like '%${username}%'
    </select>
    <!--优化后的拼接查询-->
    <select id="getByNameGood" resultType="users">
        select id, username, birthday, sex, address
        from users
        where username like concat('%',#{username},'%')
    </select>

    <update id="update" parameterType="users">
        update users
        set username=#{username},
            birthday=#{birthday},
            sex=#{sex},
            address=#{address}
        where id = #{id}
    </update>
</mapper>

userTest

package com;

import mapper.UsersMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import pojo.Users;

import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class UseTest {
    SqlSession sqlSession;
    UsersMapper usersMapper;
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");

    @Before
    public void testBefore() throws IOException {
        InputStream in = Resources.getResourceAsStream("SqlMapCon.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = sessionFactory.openSession();
        usersMapper = sqlSession.getMapper(UsersMapper.class);
    }

    @Test
    public void test01() {
        //取出动态代理的对象,完成接口中方法的调用,实则时调用xml文件中相应的标签功能
        List<Users> all = usersMapper.getAll();
        all.forEach(System.out::println);
    }

    @Test
    public void test02() throws ParseException {
        //取出动态代理的对象,完成接口中方法的调用,实则时调用xml文件中相应的标签功能
        Users u = new Users(5, "haha", sf.parse("2000-10-20"), "2", "北京");
        int update = usersMapper.update(u);
        sqlSession.commit();
        System.out.println(update);
    }

    @Test
    public void test03() throws ParseException {
        //取出动态代理的对象,完成接口中方法的调用,实则时调用xml文件中相应的标签功能
        Users id = usersMapper.getById(1);
        System.out.println(id);
    }

    @Test
    public void test04() throws ParseException {
        //取出动态代理的对象,完成接口中方法的调用,实则时调用xml文件中相应的标签功能
        List<Users> byName = usersMapper.getByName("赵");
        System.out.println(byName);
    }

    @Test
    public void test05() throws ParseException {
        //取出动态代理的对象,完成接口中方法的调用,实则时调用xml文件中相应的标签功能
        int deleter = usersMapper.deleter(1);
        sqlSession.commit();
        System.out.println(deleter);
    }
    @After
    public void testafter() {
        sqlSession.close();
    }
}

MyBatis对象分析

1)Resources类

就是解析SqlMapConfig.xml文件,创建出相应的对象

InputStream in = Resources.getResourceAsStream("Sqlmapper.xml");

2)SqlSessionFactoryBuilder类

使用ctrl+h快捷键查看本接口的子接口及实现类

DefaultSqlSessionFactory是实现类

SqlSessionFactory factory =  new SqlSessionFactoryBuilder().build(in);

3)SqlSessionFactory接口

DefaultSqlSession实现类

没有放在resources目录下的xml文件

  • 需要在pom.xml添加

    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <!--<include>**/*.properties</include>-->
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
    

为实体类注册别名

  • 1)单个起别名

    <!--注册实体类的别名-->
    <typeAliases>
        <!--单个实体类别名注册-->
        <typeAlias type="pojo.Student" alias="student"/>
    </typeAliases>
    
  • 2)批量起别名

    <!--注册实体类的别名-->
    <typeAliases>
        <!--批量注册别名
                别名是类名的驼峰命名法
                -->
        <package name="pojo"/>
    </typeAliases>
    

设置日志输出底层执行的代码

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

MyBatis的动态代理的7个规范

  • 1)UsersMapper.xml文件与UsersMapper.java的接口必须同一个目录下。
  • 2)UsersMapper.xml文件与UserMapper.java的接口的文件名必须一致,后缀不管。
  • 3)UsersMapper.xml文件中标签的id值与UserMapper.java的接口中方法的名称完全一致。
  • 4)UsersMapper.xml文件中标签的parameterType属性值与UserMapper.java的接口中方法的参数类型完全一致。
  • 5)UsersMapper.xml文件中标签的resultType值与UserMapper.java的接口中方法的返回值类型完全一致。
  • 6)UsersMapper.xml文件中namespace属性必须是接口的完全限定名称mapper.UsersMapper
  • 7)在SqlMapConfig.xml文件中注册mapper文件时,使用class=接口的完全限定名称mapper.UsersMapper

#{}占位符

  • 传参大部分使用#{}传参,它的底层使用的是PreparedStatement对象,是安全的数据库访问,防止sql注入。

    如何写

  • 1)如果parameterType的类型是简单类型(8种基本(封装)+String),则#{}里随便写。

  • 2)parameterType的类型是实体类的类型,则#{}里只能是类中成员变量的名称,而且区分大小写。

  • 3)字符串凭借,一般用于模糊查询中。建议少用,因为有sql注入的风险。

    也分两种情况,同样的看parameterType的类型

    A、如果parameterType的类型是简单类型,则#{}里随便写,但是分版本,如果是3.5.1及以下的,只写value

    B、如果parameterType的类型是实体类的类型,则${}里只能是类中成员变量的名称

返回主键id

<insert id="create" parameterType="com.sxgn.springcloud.entity.Payment" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO payment(serial) VALUES (#{serial})
</insert>

useGeneratedKeys=“true” 表示给主键设置自增长
keyProperty=“userId” 表示将自增长后的Id赋值给实体类中的userId字段。
parameterType=“com.chenzhou.mybatis.User” 这个属性指向传递的参数实体类

这里提醒下, 中没有resultType属性,不要乱加

<insert id="create">
    <selectKey resultType="java.lang.Long" order="AFTER" keyProperty="id">
        SELECT LAST_INSERT_ID()
    </selectKey>
    INSERT INTO payment(serial) VALUES (#{serial})
</insert>

selectKey中没有resultType属性,但是 标签是有的。

order=“AFTER” 表示先执行插入语句,之后再执行查询语句。

可被设置为 BEFORE 或 AFTER。

如果设置为 BEFORE,那么它会首先选择主键,设置 keyProperty 然后执行插入语句。

如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 元素-这和如 Oracle 数据库相似,可以在插入语句中嵌入序列调用
keyProperty=“userId” 表示将自增长后的Id赋值给实体类中的userId字段。

SELECT LAST_INSERT_ID() 表示MySQL语法中查询出刚刚插入的记录自增长Id

foreach

  • collection:用来指定入参的类型,如果是List集合,则为list,如果是Map集合,则为map,如果是数组,则为array。

    item:每次循环遍历出来的值或对象

    separator:多个值或对象或语句之间的分隔符

    open:整个循环外面的前括号

    close:整个循环外面的后括号

<select id="getByIds" resultType="users">
    select
    <include refid="U"/>
    from users
    where id in
    <foreach collection="array" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</select>

SqlSession工厂对象SqlSessionFactory

方法解释
openSession会默认开启一个事务,但事务不会自动提交,也就意味着需要手动提交该事务,更新操作数据才会持久化到数据库中
openSession(booleanautoCommit)参数为是否自动提交,如果设置为true,那么不需要手动提交事务

Dao层实现

代理开发方式

采用Mybatis的代理开发方式实现DAO层的开发,这种方式是我们后面进入企业的主流。

Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),有mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。

Mapper接口开发需要遵循一下规范:

1、Mapper.xml文件中的namespace于mapper接口的全限定名相同

2、Mapper接口方法名和Mapper.xml中定义的每个statement的id相同

3、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同

4、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同

测试代理方式
package mzj.service;

import mzj.Impl.UserMapperImpl;
import mzj.dao.UserMapper;
import mzj.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class ServiceDemo {
    public static void main(String[] args) throws IOException {
        //UserMapperImpl userMapper = new UserMapperImpl();
        //List<User> userList = userMapper.findAll();
        //System.out.println(userList);

        InputStream resourceAsStream = Resources.getResourceAsStream("mapper/sqlMapConfig.xml");
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> all = mapper.findAll();
        List<User> byId = mapper.findById(2);
        System.out.println(byId);
    }
}

MyBatis映射文件深入

动态sql语句

<select id="findByC" resultType="user" parameterType="user">
    select *
    from user
    <where>
        <if test="id!=0 and id !=''">
            and id=#{id}
        </if>
        <if test="age!=0">
            and age=#{age}
        </if>
    </where>
</select>

<select id="findByD" resultType="user" parameterType="user">
    select *
    from user
    <where>
        <foreach collection="list" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>

sql抽取

<!--sql语句抽取-->
<sql id="selectUser">select *
    from user</sql>

<select id="findAll" resultType="user">
    <include refid="selectUser"/>
</select>

typeHandlers标签

<!--自定义类型处理器-->
<typeHandlers>
    <typeHandler handler="mzj.handler.DateTypeHandler"/>
</typeHandlers>
package mzj.handler;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class DateTypeHandler extends BaseTypeHandler<Date> {
    //将java类型转换成数据库需要的类型
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        long time = parameter.getTime();
        ps.setLong(i,time);
    }

    //将数据库中类型转换成java类型
    //String参数 要转换的字段名称
    //rs 查询出的结果集
    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        //获得结果集中需要的数据(long)转换成Date类型返回
        long aLong = rs.getLong(columnName);
        Date date = new Date(aLong);
        return date;
    }

    //将数据库种类型 转换成java类型
    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        long aLong = rs.getLong(columnIndex);
        Date date = new Date(aLong);
        return date;
    }

    //将数据库中类型转换成java类型
    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        long aLong = cs.getLong(columnIndex);
        Date date = new Date(aLong);
        return date;
    }
}

分页助手

<!--配置分页助手插件-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--<property name="dialect" value="mysql"/>-->
    </plugin>
</plugins>
PageHelper.startPage(1,3);
//这里面的all将数据对象传入
PageInfo<User> userPageInfo = new PageInfo<>(all);
System.out.println("当前页:"+userPageInfo.getPageNum());
System.out.println("每页显示条数:"+ userPageInfo.getPageSize());
System.out.println("总条数:"+ userPageInfo.getTotal());
System.out.println("总页数:"+userPageInfo.getPages());
System.out.println("上一页:"+ userPageInfo.getPrePage());
System.out.println("下一页:"+userPageInfo.getNextPage());
System.out.println("是否是第一个:"+userPageInfo.isIsFirstPage());
System.out.println("是否是第一个:"+userPageInfo.isIsLastPage());

注解开发

@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result一起使用,封装多个结果集
@One:实现一对一结果集封装
@Many:实现一对多结果封装
    @Result({
            @Result(column = "",property = ""),
    })
    @Insert("        insert into user\n" +
            "        values (#{id}, #{username}, #{password}, #{birthday})")
    public void save(User user);
    @Result({
            @Result(column = "",property = ""),
            @Result(
               	id=true,column = "",property = ""),
                    property = "",//要封装得属性名
                    column = "",//根据那个字段去查询user表得数据
                    javaType = User.class,//要封装得实体类型
                    //select属性 代表查询那个接口的方法获得数据
                    one = @One(select = "mzj.dao.UserMapper.findAll")
            )
    })

Springz整合Mybatis

用spring产生mapper

App.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
">
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--配置mybatis-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mapper/sqlMapConfig.xml"/>
    </bean>
    <!--扫描mapper所在的包 为mapper创建实现类-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="mapper"/>
    </bean>
</beans>

sqlMapperConfig.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>
    <!--自定义别名-->
    <typeAliases>
        <typeAlias type="Mzj.domain.User" alias="user"/>
        <typeAlias type="Mzj.domain.Order" alias="order"/>
        <typeAlias type="Mzj.domain.Role" alias="role"/>
    </typeAliases>
    <!--配置分页助手插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
            <!--<property name="dialect" value="mysql"/>-->
        </plugin>
    </plugins>
</configuration>

SpringBoot

maven

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.6.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.7</version>
    <type>pom</type>
</dependency>

测试代码

HelloController

package Mzj.Hellocontroller;

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

@Controller
public class HelloController {
    @ResponseBody
    @RequestMapping("/hello")
    public String hello() {
        return "hello world!";
    }
}

helloSampleController主入口

@SpringBootApplication:springboot应用标注在某个类上说明这个类是SpringBoot的配置类,

SpringBoot就应该运行这个类的main方法来启动SpringBoot应用

package Mzj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class helloSampleController {
    public static void main(String[] args) {
        SpringApplication.run(helloSampleController.class,args);
    }
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

@SpringBootConfiguration:Spring Boot的配置类

  • 标注在某个类上,表示这是一个Spring Boot的配置类。
  • @Configuration:配置类上来标注这个注解;
    • 配置类——配置文件

@EnableAutoConfiguration:开启自动配置功能

以前我们需要配置的东西,springboot帮我们自动配置,@EnableAutoConfiguration告诉SpringBoot开启自动配置功能;这样自动配置才能生效;

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

@AutoConfigurationPackage:自动配置包

  • @Import(AutoConfigurationPackages.Registrar.class)
    

    Spring的底层注解@Import,给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class

    将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面所有组件扫描到spring容器;

    @Import(AutoConfigurationImportSelector.class)
    

    给容器导入组件

    AutoConfigurationImportSelector
    

    导入那些组件的选择器

    将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;

    会给容器中导入非常多的自动配置类(xxxAutoConfiguration);就是个容器中导入这个场景需要的所有组件,并配置好这些组件;

    ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcJRLprP-1653223158146)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220425012403325.png)]

     SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),	getBeanClassLoader())
    

    SpringBoot在启动的时候从类路径下的META-INF/spring.factories获取EnableAutoConfiguration指定的值,这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;

spring-boot-starter-web

spring-boot-starter:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件;

Springboot将所有的功能场景都抽取出来,做成一个个的starts(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器

yam语法

基本语法

k:(空格)v:表示一对键值对(空格必须有)

以空格的缩进来控制层级关系;只要是左对齐得一列数据,都是一个层级的

server:
	port: 8081
	path: /hello

值的写法

字面量:普通的值(数字,字符串,布尔)

k:v:字面直接来写;

字符串默认不同加上单引号或者双引号

“”:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身香表示的意思

‘’:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

对象、map(属性和值)(键值对)
friends:
	lastName: zhangsan
	age: 20

行内写法:

firends: {	lastName: zhangsan,age: 20}
数组(List、set)

用-值表示数组中的一个元素

pets:
	- cat
	- dog

行内写法

pets: [cat,dog.pig]

配置文件注入

配置文件

server:
  port: 8083

person:
  lastName: zhangsan
  age: 18
  boss: false
  birth: 2017/12/12
  maps: {k1: v1,k2: v2}
  lists:
    - lisi
    - liwu
    - liliu

  dog:
    name: 旺财
    age: 3

Javabean

package mzj.sping_boot_01.bean;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * 将配置文件中配置的每一个属性的值映射到这个组件中
 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文  件中相关的配置进行绑定;
 * prefix = "person":配置文件中那个下main的所有属性进行一一映射
 * 只有这个组件是容器中的组件,才能使用这个功能
 */
@Component
@ConfigurationProperties(prefix = "person")
@Data
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;

    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

@Value获取值和@ConfigurationProperties获取值比较

@ConfigurationProperties@Value不支持的代码
功能批量注入配置文件中的属性一个个指定
松散绑定(松散语法)支持不支持@Value(“${person.lastName=张三}”)
SpEl(spring表达式)不支持支持
JSR303数据检验支持不支持

配置-@PropertySource、@ImportResource、@Bean

@ImportResource

Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不嫩挂自动是被;

想让spirng的配置文件生效,加载进来;@ImportResource标注在一个配置类上

@ImportResource(locations = {"classpath:beans.xml"})

SpringBoot推荐给容器中添加组件的方式:使用全注解的方式

1、配置类====Spirng配置文件

配置文件占位符

1、随机数

person.lastName=张三${random.uuid}

2、占位符获取之前配置的值,如果没有可以使用:指定默认值

person.dog.name=${person.hello:hello}_旺财

Profile

  • Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境

1、多profile文件形式:

文件名字格式

application.properties
application-dev.properties
application-prod.properties

application.properties文件内

server.port=8081
#这里指向文件名字的`-`后面的名字
spring.profiles.active=prod

2、多profile文档块模式

server:
  port: 8081
spring:
  profiles:
    active: dev
---
server:
  port: 8083
spring:
  config:
    activate:
      on-profile: dev
---
server:
  port: 8084
spring:
  config:
    activate:
      on-profile: prod

3、激活方式:

  • 命令行 --spring.porfiles.active=dev
  • 配置文件 spring.profiles.active=dev
  • jvm参数 -Dspring.profiles.active=dev

配置文件的加载位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h24t67WV-1653223158146)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220427151834291.png)]

server.port=8081
#配置项目于的访问路径
server.servlet.context-path=/boot02

spring.config.location=F:/properties  #路径

我们还可以通过spring.config.location来改变默认的配置文件位置

项目打包好以后,我们可以使用命令行参数的形式,启动i项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形式互补配置

加载外部配置顺序

由jar包外向jar内进行寻找

优先加载带profile

application-{profile}.properties

自动配置原理

1)、SpirngBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration

2)、@EnableAutoConfiguration作用:

​ 利用AutoConfigurationImportSelector选择器给容器中导入一些组件?selectImports这个方法可以看得到;

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

获取候选的配置

SpringFactoriesLoader.loadFactoryNames
扫描所有jar包类路径下 META-INF/spring.factories
把扫描道德这些文件得内容包装成properties对象
从properties获取到`EnableAutoConfiguration.class`类(类名)对应的值,然后把他们添加在容器中

将类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到容器中

test

HttpEncodingAutoConfiguration为例子

@Configuration(proxyBeanMethods = false) //表示这是一个配置列,以前编写的配置文件一样,也可以给容器中添加组件

@EnableConfigurationProperties(ServerProperties.class) //启动ConfigurationProperties功能

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) //Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置列里面的配置就会生效;  判断当前应用是否是web应用;将配置文件中对应的值和`HttpEncodingAutoConfiguration`绑定起来;闭关吧`HttpEncodingAutoConfiguration`加入到ioc容器中


@ConditionalOnClass(CharacterEncodingFilter.class) //判断给当前项目于有没有这个类`CharacterEncodingFilter`SpringMVC中进行乱码解决的过滤器

@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) //判断配置文件中是否存在某个配置,server.servlet.encoding.enabled 如果不存在,判断也是成立的  即使我们配置文件中不配置server.servlet.encoding.enabled,也是生效的


public class HttpEncodingAutoConfiguration {
    
    //他已经和SpringBoot的配置文件映射了
    private final Encoding properties;
    
    //只有一个有参构造器的情况下,参数的值就会从容器中拿
    public HttpEncodingAutoConfiguration(ServerProperties properties) {
		this.properties = properties.getServlet().getEncoding();
	}

    
    @Bean//添加组件
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
		return filter;
	

一旦这个配置类生效;这个配置列就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;

@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true) //从配置文件中获取指定的值和bean的属性进行绑定

精髓:

1)、SpringBoot启动会加载大量的自动配置类

2)、我们看我们选哟的功能有没有SpringBoot默认写好的自动配置类;

3)、我们再来看这个自动配置类中到底配置了哪些组件;

4)、给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些书信过的值;

xxxAutoConfiguration:自动配置类;

给容器中添加钻进

xxxProperties:封装配置文件中相关属性;

注解细节

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UIa4X6Qs-1653223158146)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220427204637971.png)]

Conditional扩展注解作用(判断是否满足当前指定条件 )

启动debug

debug=true

日志

日志框架

SpringBoot:底层是Spring框架,Spring框架默认使用JCL;

SpringBoot选用还SLFj和logback

SLF4j使用

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pe3AaPY5-1653223158146)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220427210718585.png)]

如何让系统中所有的日志都统一到slf4j;

1、将系统中其他日志框架先排除出去;

2、用中间包来替换原有的日志框架;

3、导入slf4j其他的实现

package mzj.spring_boot_03;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;

import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringBoot03ApplicationTests {
    Logger logger = (Logger) LoggerFactory.getLogger(getClass());
    @Test
    void contextLoads() {
        logger.trace("这个是trace日志");
        logger.debug("这是debug日志");
        logger.info("这是info日志");
        logger.warn("这是warn日志");
        logger.error("这是error日志");
    }

}
server.port=8080
logging.level.mzj=trace
#logging.path=/spring/log
#不指定路径在当前i项目一下生成springboot.log日志
#可以指定
logging.file.name=F:/springboot.log
# 控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} -%msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss}====[%thread] %-5level %logger{50} -%msg%n

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-viPCSSDx-1653223158147)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220428003319877.png)]

Web开发

Spring Boot对静态资源的映射规则

<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉 -->
<resources>
    <resource>
        <directory>src/main/java</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
    </resource>
    <resource>
        <directory>src/main/resources</directory>
        <includes>
            <include>**/*.properties</include>
            <include>**/*.xml</include>
            <include>**/*.yml</include>
            <include>**/*.html</include>
            <include>**/*.js</include>
            <include>**/*.css</include>
            <include>**/*.map</include>
            <include>**/*.jpg</include>
            <include>**/*.png</include>
            <include>**/*.CAB</include>
            <include>**/*.ico</include>
            <include>**/banner.txt</include>
        </includes>
        <filtering>false</filtering>
    </resource>
</resources>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4GNriGoN-1653223158147)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220428171447682.png)]

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
    addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
            ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
            registration.addResourceLocations(resource);
        }
    });
}

1)、所有/webjars/** ,都去classpath:/META-INF/resources/webjars/找资源

容器功能

组件添加

@Configuratoin

@EnableConfigurationProperties()自动导入bean

@ConfigurationProperties (prefix = “”)

@SpringBootApplication
相当于
@SpringbootConfiguration
@EnableAutoConfiguration
@ComponentScan()

静态资源配置原理

  • SpringBoot启动默认配置加载xxxAutoConfiguration(自动配置类)
  • springMVC功能的自动配置类 WebMvcAutoConfiguration,生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
                     ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
  • 配置文件的相关属性和xxx进行了绑定。
//有参构造器所有参数的值都会从容器中确定
//WebProperties webProperties获取和spring.web绑定的所有的值和对象,
//WebMvcProperties mvcProperties获取和spring.web绑定的所有的值和对象,
//ListableBeanFactory beanFactory Spring的beanFactory
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties,
                                      ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,
                                      ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,
                                      ObjectProvider<DispatcherServletPath> dispatcherServletPath,
                                      ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
    this.resourceProperties = webProperties.getResources();
    this.mvcProperties = mvcProperties;
    this.beanFactory = beanFactory;
    this.messageConvertersProvider = messageConvertersProvider;
    this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
    this.dispatcherServletPath = dispatcherServletPath;
    this.servletRegistrations = servletRegistrations;
    this.mvcProperties.checkConfiguration();
}
资源处理的默认规则
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
        return;
    }
    addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
    addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
        registration.addResourceLocations(this.resourceProperties.getStaticLocations());
        if (this.servletContext != null) {
            ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
            registration.addResourceLocations(resource);
        }
    });
}
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true
#    static-path-pattern: /**
# 禁用所有静态资源
  web:
    resources:
      add-mappings: true

欢迎页的默认规则

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
return welcomePageHandlerMapping;
}

自定义请求方式

/**
 * 自定义请求方式
 */
@Configuration(proxyBeanMethods = false) //告诉Springboot这个是配置类
public class myConfig {
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        //methodFilter.setMethodParam("_url");
        return methodFilter;
    }
}

请求映射原理

所有的请求映射都在HandlerMapping中

响应json

spring:
  mvc:
#        参数内容协商
    contentnegotiation:
      favor-parameter: true
http://localhost:8080/u?format=json
@Bean
public WebMvcConfigurer webMvcConfigurer() {
    return new WebMvcConfigurer() {
        //浏览器网页格式
        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            HashMap<String, MediaType> mediaTypes = new HashMap<>();
            mediaTypes.put("json",MediaType.APPLICATION_JSON);
            //指定支持解析哪些参数对应的哪些媒体类型
            ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
            configurer.strategies(Arrays.asList(strategy));
        }
    }
}

Thymeleaf

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pt3oHb7J-1653223158147)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220430214550943.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yoAydn99-1653223158147)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220501182413994.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QPYvV37g-1653223158148)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220501183222303.png)]

可以用id选择器

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69iwAUWR-1653223158148)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220501185859652.png)]

拦截器

package mzj.spring_boot_web.interceptor;

import lombok.extern.slf4j.Slf4j;
import mzj.spring_boot_web.bean.LoginUser;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@Slf4j
public class indexInterceptor implements HandlerInterceptor {
    //在目标方法执行之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestURI = request.getRequestURI();
        log.info("拦截的请求路径是{}", requestURI);
        HttpSession session = request.getSession();
        String username = (String) session.getAttribute("username");
        String password = (String) session.getAttribute("password");
        if (StringUtils.isEmpty(password) || StringUtils.isEmpty(username)) {
            session.setAttribute("msg", "还没登录");
            System.out.println(request.getContextPath() + "/login");
            request.getRequestDispatcher("/loginthtml").forward(request, response);
            return false;
        } else {
            return true;//false 不执行
        }
    }

    //在目标方法执行之后 视图返回之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    //在整个流程都执行完毕后 执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
package mzj.spring_boot_web.config;


import mzj.spring_boot_web.converter.Userconverter;
import mzj.spring_boot_web.interceptor.indexInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.util.UrlPathHelper;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * full(proxyBeanMethods = true) 每次调用都是同一个对象
 * lite(proxyBeanMethods = false) 每次调用都重新调用
 */

/**
 * 自定义请求方式
 */
@Configuration(proxyBeanMethods = false) //告诉Springboot这个是配置类
public class myConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(new indexInterceptor())
            .addPathPatterns("/**")
            .excludePathPatterns("/loginthtml","/BootStrap/**","/Jquery/**","/favicon.ico","/login");
    }

    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        //methodFilter.setMethodParam("_url");
        return methodFilter;
    }

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {

            //@Override
            //public void configurePathMatch(PathMatchConfigurer configurer) {
            //    WebMvcConfigurer.super.configurePathMatch(configurer);
            //    UrlPathHelper urlPathHelper = new UrlPathHelper();
            //    configurer.setUrlPathHelper(urlPathHelper);
            //}
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }


            //自定义参数解析器
            //@Override
            //public void addFormatters(FormatterRegistry registry){
            //    registry.addConverter(new Converter<String, User>() {
            //        @Override
            //        public User convert(String source){
            //            if (!StringUtils.isEmpty(source)){
            //                User user = new User();
            //                String[] split = source.split(",");
            //                user.setUsername(split[0]);
            //                user.setAge(Integer.parseInt(split[1]));
            //                return user;
            //            }
            //            return null;
            //        }
            //    });
            //}


            //postman返回格式
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new Userconverter());
            }

            //浏览器网页格式
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                HashMap<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("json", MediaType.APPLICATION_JSON);
                //指定支持解析哪些参数对应的哪些媒体类型
                ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
                //请求头
                HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy();

                configurer.strategies(Arrays.asList(strategy, headeStrategy));
            }
        };
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CGvY4pa8-1653223158148)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220502160333230.png)]

文件上传功能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GYs33G96-1653223158148)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220502163442276.png)]

spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true
    #        参数内容协商
    contentnegotiation:
      favor-parameter: true

  servlet:
    multipart:
      # 单个文件大小为5MB
      max-file-size: 100MB
      # 总上传的数据大小5MB
      max-request-size: 100MB
#  web:
#    resources:
#      add-mappings: true

# 所有项目的起始路径
#server:
#  servlet:
#    context-path: /world
server:
  port: 8081
package mzj.spring_boot_web.controller.upBook;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
@Slf4j
@Controller
@RequestMapping("/book")
public class UpbookIndex {
    @PostMapping("/upbook")
    public String upbook(
            @RequestParam("photofile") MultipartFile photofile,
            @RequestParam("bookfile") MultipartFile bookfile,
            HttpSession session
            ) throws IOException {
        log.info("上传的信息:phtotofile={},bookfile={}",photofile.getSize(),bookfile.getSize());
        if(!photofile.isEmpty()){
            String originalFilename1 = photofile.getOriginalFilename();
            photofile.transferTo(new File("F:\\"+originalFilename1));
        }
        if(!bookfile.isEmpty()){
            String originalFilename2 = bookfile.getOriginalFilename();
            bookfile.transferTo(new File("F:\\"+originalFilename2));
        }
        session.setAttribute("msg","成功上传文件");
        return "book/bookUp";
    }

    @GetMapping("/upbookhtml")
    public String upBookhtml(Map<String, Object> map,
                              @RequestHeader Map<String, String> headmap) {
        Date date = new Date();
        long time = date.getTime();
        String date2 = new SimpleDateFormat("yyyy年M月d日 H:m:s").format(time);
        map.put("date", date2);
        map.put("name", "邵立林");
        System.out.println("============================================================================================");
        System.out.println("信息头:" + headmap);
        System.out.println("============================================================================================");
        return "book/bookUp";
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EDKGjUsW-1653223158149)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220502181657876.png)]

原生注入

1、使用ServletAPI

2、使用RegistrationBean

启动页面导入扫描组件

@ServletComponentScan(basePackages = "mzj.spring_boot_web")

根据上面原生的来使用

package mzj.spring_boot_web.servlet;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

@Configuration
public class MyRegistConfig {
    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();
        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }
    @Bean
    public FilterRegistrationBean myFilter(){
        MyFilter myFilter = new MyFilter();
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter, myServlet());
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/BootStrap/*"));
        return filterRegistrationBean;
    }
    @Bean
    public ServletListenerRegistrationBean MyListener(){
        MyContextListener myContextListener = new MyContextListener();
        return new ServletListenerRegistrationBean(myContextListener);
    }
}

数据库访问

package mzj.spring_boot_web.config;


import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import mzj.spring_boot_web.converter.Userconverter;
import mzj.spring_boot_web.interceptor.indexInterceptor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.util.UrlPathHelper;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

/**
 * full(proxyBeanMethods = true) 每次调用都是同一个对象
 * lite(proxyBeanMethods = false) 每次调用都重新调用
 */

/**
 * 自定义请求方式
 */
@Configuration(proxyBeanMethods = true) //告诉Springboot这个是配置类
public class myConfig implements WebMvcConfigurer {
    //采集web-jdbc关联监控的数据
    @Bean
    public FilterRegistrationBean webStatFilter() {
        WebStatFilter webStatFilter = new WebStatFilter();
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
        filterRegistrationBean.addInitParameter("exclusions","*.js,*jpg,*.png,*.css,*.ico,/druid/*");
        return filterRegistrationBean;
    }

    //数据库监控
    @Bean
    public ServletRegistrationBean statViewServlet() {
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");
        //sql密码
        registrationBean.addInitParameter("loginUsername","root");
        registrationBean.addInitParameter("loginPassword","936541");
        return registrationBean;
    }

    @ConfigurationProperties("spring.datasource")
    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();
        //druidDataSource.setPassword("");
        //监控打开
        druidDataSource.setFilters("stat,wall");


        return druidDataSource;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(new indexInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/loginthtml", "/BootStrap/**", "/Jquery/**", "/favicon.ico", "/login","/sql");
    }

    public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        //methodFilter.setMethodParam("_url");
        return methodFilter;
    }

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {

            //@Override
            //public void configurePathMatch(PathMatchConfigurer configurer) {
            //    WebMvcConfigurer.super.configurePathMatch(configurer);
            //    UrlPathHelper urlPathHelper = new UrlPathHelper();
            //    configurer.setUrlPathHelper(urlPathHelper);
            //}
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                //生效
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }


            //自定义参数解析器
            //@Override
            //public void addFormatters(FormatterRegistry registry){
            //    registry.addConverter(new Converter<String, User>() {
            //        @Override
            //        public User convert(String source){
            //            if (!StringUtils.isEmpty(source)){
            //                User user = new User();
            //                String[] split = source.split(",");
            //                user.setUsername(split[0]);
            //                user.setAge(Integer.parseInt(split[1]));
            //                return user;
            //            }
            //            return null;
            //        }
            //    });
            //}


            //postman返回格式
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new Userconverter());
            }

            //浏览器网页格式
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                HashMap<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("json", MediaType.APPLICATION_JSON);
                //指定支持解析哪些参数对应的哪些媒体类型
                ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
                //请求头
                HeaderContentNegotiationStrategy headeStrategy = new HeaderContentNegotiationStrategy();

                configurer.strategies(Arrays.asList(strategy, headeStrategy));
            }
        };
    }

}
spring:
  datasource:
    url: jdbc:mysql:///spring
    username: root
    password: 936541
    driver-class-name: com.mysql.cj.jdbc.Driver

  jdbc:
    template:
      query-timeout: 3

  mvc:
    hiddenmethod:
      filter:
        enabled: true
    #        参数内容协商
    contentnegotiation:
      favor-parameter: true

  servlet:
    multipart:
      # 单个文件大小为5MB
      max-file-size: 100MB
      # 总上传的数据大小5MB
      max-request-size: 100MB
#  web:
#    resources:
#      add-mappings: true

# 所有项目的起始路径
#server:
#  servlet:
#    context-path: /world
server:
  port: 8081
  error:
    path: /error

mybatis

配置版

  • 全局配置文件
  • SqlSessionFactory:自动配置好了
  • SqlSession:自动配置了SqlSessionTemplate组合了SqlSession
  • @Import(AutoConfiguredMapperScannerRegistrar.class)
  • Mapper:只要我们写的错做MyBatis的接口标准了@Mapper就会被自动扫描进来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zmnhy0h1-1653223158149)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503103552660.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ey7yxALm-1653223158149)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503103610516.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0vu8D2p-1653223158149)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503103620868.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PzJe1DgM-1653223158150)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503103646382.png)]

注解版

  • 返回插入后的id

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJqcSwdq-1653223158150)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503104826023.png)]

JUnit5

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ug8w3ozy-1653223158150)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503141002521.png)]

package mzj.spring_boot_web;

import org.junit.jupiter.api.*;

import java.util.concurrent.TimeUnit;

@DisplayName("asdas")
public class Testaskd {
    @Test
    @DisplayName("测试displayname注解1")
    void testDisplayName() {
        System.out.println(1);
    }
    @Test
    @DisplayName("测试displayname注解2")
    void test2(){
        System.out.println(2);
    }
    @Timeout(value = 500,unit = TimeUnit.MILLISECONDS)
    @Test
    void testTimeout() throws InterruptedException {
        Thread.sleep(600);
    }
    @BeforeEach
     void testBefor() {
        System.out.println("测试要开始了");
    }
    @AfterEach
     void testAfter(){
        System.out.println("测试结束了");
    }
    @AfterAll
    static void testAfterall(){
        System.out.println("所有测试都要结束了");
    }
    @BeforeAll
    static void testBeforeAll(){
        System.out.println("所有测试都要开始了");
    }
    @RepeatedTest(9)
    @Test
    void test3(){
        System.out.println(2);
    }
}

断言

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eW5cj77T-1653223158150)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503142214026.png)]

参数化测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Sgiqy6e-1653223158150)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503144209855.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EETCWhAI-1653223158151)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503144403718.png)]

自定义starter

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WIa5miU9-1653223158151)(C:\Users\Mzj\AppData\Roaming\Typora\typora-user-images\image-20220503170934024.png)]

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐