SSM

  • 传统Servlet的不足
    • 同一个类型的业务要进行多个Servlet的编写,单个Servlet功能单一
    • 对象管理不方便,每次操作业务,都会产生很多对象

Java中的主流框架技术

  • Struts
  • Hibernate
  • Spring
  • SpringMVC
  • SpringBoot
  • MyBatis

学习框架的思路

  • 框架的规范
  • 框架的底层实现

Struts2框架的介绍

  • 是一个基于mvc设计思想的web应用框架,它本质就是一个Servlet,在mvc设计思想中,Struts2充当的是一个Controller的角色

Hibernate框架的介绍

  • 开源的对象关系映射框架(ORM)框架
  • 底层就是对JDBC的封装

Java企业级开发的演化

  • Servlet+JavaBean
  • Servlet+JSP+JavaBean
  • Struts2+Spring+Hibernate:SSH
  • Spring+SpringMVC+MyBatis:SSM
  • SpringBoot(Spring全家桶开发)

一、Spring

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

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

官网:https://spring.io/

Spring 的核心

  • IOC:把对象的创建权交给Spring,让Spring帮我们创建并且管理对象
  • DI:给对象的属性注入值
  • AOP:解决了一些面向对象编程不方便解决的问题

Spring 的优点

  • 方便解耦,简化开发,把对象的创建权全部交给了Spring管理
  • 支持AOP编程
  • 声明式事务
  • Spring很方便继承各种优秀的框架
  • Spring还对比较难用的api提供了封装,方便我们使用

1. Spring 程序开发步骤

  • 导入Spring开发的基本包坐标(Maven依赖)
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
  • 编写Dao接口和实现类(Bean)
public interface UserDao {
    public abstract void save();
}

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("save方法");
    }
}
  • 创建Spring核心配置文件(applicationContext.xml)
  • 在Spring配置文件中配置(bean)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl"></bean>
</beans>
  • 使用Spring的API获得Bean实例( new ClassPathXmlApplicationContext("applicationContext.xml"))
public class UserDaoDemo {
    public static void main(String[] args) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) app.getBean("userDao");
        userDao.save();
    }
}

2. Spring 配置文件

①. 基本属性

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

②. 范围配置

  • scope:对象的作用范围
    • singleton:默认值,单例的
    • prototype:多例的
    • request:Web项目中,Spring创建一个Bean对象,将对象存入到request域中
    • session:Web项目中,Spring创建一个Bean对象,将对象存入session域中
    • global session:Web项目中,应用在Portlet环境,如果没有Portlet环境,那么globalSession相当于session
  • 当scope的取值为singleton时
    • Bean的实例化个数:1个
    • Bean的实例化时机:当Spring核心配置文件被加载时,实例化配置的Bean实例
    • Bean的生命周期
      • 对象创建:当应用加载,创建容器时,对象就被创建了
      • 对象运行:只要容器在,对象一直活着
      • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
  • 当scope的取值为prototype时
    • Bean的实例化个数:多个
    • Bean的实例化时机:当调用getBean()方法时实例化Bean
      • 对象创建:当使用对象时,创建新的对象实例
      • 对象运行:只要对象在使用中,就一直活着
      • 对象销毁:当对象长时间不用时,被Java的垃圾回收器回收了

③. 生命周期配置

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

④. Bean实例化三种方式

  • 无参构造方法实例化

    • <bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl"></bean>
      
  • 工厂静态方法实例化

    • <bean id="userDao" class="com.zhiyuan.factory.StaticFactory" factory-method="getUserDao"></bean>
      
  • 工厂实例方法实例化

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

⑤. Bean的依赖注入

  • 程序最终使用的是UserService,而UserService和UserDao都在Spring容器中,所以可以在Spring容器中,将UserDao设置到UserService的内部,这就叫UserService需要UserDao的依赖注入
  • 依赖注入:Dependency Injection(IOC的具体实现)
1. set方法注入
public class UserServiceImpl implements UserService {
  private UserDao userDao;
  public void setUserDao(UserDao userDao){
      this.userDao = userDao;
  }
  public void save(){
      userDao.save();
  }
}
<bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.zhiyuan.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
  • 简化set方法注入

    • 引入命名空间
    xmlns:p="http://www.springframework.org/schema/p"
    
    • 进行依赖注入
    <bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl"></bean>
    <bean id="userService" class="com.zhiyuan.service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean>
    
2. 构造方法注入
public class UserServiceImpl implements UserService {
    private UserDao userDao;

    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }
    public UserServiceImpl() {}
    public void save(){
        userDao.save();
    }
}
<bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.zhiyuan.service.impl.UserServiceImpl">
    <constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
3. 普通数据类型注入
public class UserDaoImpl implements UserDao {
    private String username;
    private int age;
    @Override
    public void save() {
        System.out.println("save方法");
        System.out.println(username+"-"+age);
    }

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

    public void setAge(int age) {
        this.age = age;
    }
}
<bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl">
    <property name="age" value="69"></property>
    <property name="username" value="张三"></property>
</bean>
4. 集合数据类型注入
<bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl">
    <property name="age" value="69"></property>
    <property name="username" value="张三"></property>
    <property name="scoreMap">
        <map>
            <entry key="k1" value="v1"></entry>
            <entry key="k2" value="v2"></entry>
        </map>
    </property>
    <property name="list">
        <list>
            <value>v1</value>
            <value>v2</value>
            <ref bean="beanName"></ref>
        </list>
    </property>
</bean>

⑥. 引入其他配置文件

  • 在实际开发中,Spring的配置内容非常多,这就导致Spring配置文件很繁杂体积很大,所以Spring配置文件支持通过import标签引入外部其他的配置文件

    <import resource="applicationContext-xxx.xml" />
    

3. ApplicationContext继承体系

img

①. ApplicationContext

  • 接口类型,代表应用上下文,可以通过其实例获得Spring容器中的Bean对象

  • 实现类

    • ClassPathXmlApplicationContext

      • 从类的根路径下加载配置文件(推荐)
    • FileSystemXmlApplicationContext

      • 从磁盘路径中加载配置文件,配置文件可以在任意位置
    • AnnotationConfigApplicationContext

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

4. 常用 API

①. getBean()方法

  • getBean(String name):获取指定类名称的Bean对象
  • getBean(Class<T> requiredType):获取指定类类型的Bean对象

5. Spring配置数据源

①. 数据源(数据库连接池:DataSource)

  • DBCP C3P0 Druid

②. 数据库连接池的配置

  • 导入数据库相关依赖
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.4</version>
</dependency>
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
  • Spring配置数据库连接池
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai"></property>
    <property name="user" value="root"></property>
    <property name="password" value="123456"></property>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai"></property>
    <property name="username" value="root"></property>
    <property name="password" value="123456"></property>
</bean>
  • 代码实现
public class DataSourceDemo1 {
    public static void main(String[] args) throws Exception {
        // ComboPooledDataSource ds = new ComboPooledDataSource();
        // Connection conn = ds.getConnection();
        // String sql  = "select * from user";
        // PreparedStatement pstat = conn.prepareStatement(sql);
        // ResultSet rs = pstat.executeQuery();
        // while(rs.next()){
        //     System.out.println(rs.getInt("id"));
        // }
        // ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // DataSource ds = (DataSource)app.getBean("dataSource");
        // Connection conn = ds.getConnection();
        // String sql = "select * from user";
        // PreparedStatement pstat = conn.prepareStatement(sql);
        // ResultSet rs = pstat.executeQuery();
        // while(rs.next()){
        //     System.out.println(rs.getInt("id"));
        // }
        // DruidDataSource ds = new DruidDataSource();
        // ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        // ds.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai");
        // ds.setUsername("root");
        // ds.setPassword("123456");
        // DruidPooledConnection conn = ds.getConnection();
        // String sql = "select * from user";
        // PreparedStatement pstat = conn.prepareStatement(sql);
        // ResultSet rs = pstat.executeQuery();
        // while(rs.next()){
        //     System.out.println(rs.getInt("id"));
        // }
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource ds = (DataSource)app.getBean("dataSource");
        Connection conn = ds.getConnection();
        String sql = "select * from user";
        PreparedStatement pstat = conn.prepareStatement(sql);
        ResultSet rs = pstat.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("id"));
        }
    }
}
public class DataSourceDemo1 {
    public static void main(String[] args) throws Exception {
        // ComboPooledDataSource ds = new ComboPooledDataSource();
        // Connection conn = ds.getConnection();
        // String sql  = "select * from user";
        // PreparedStatement pstat = conn.prepareStatement(sql);
        // ResultSet rs = pstat.executeQuery();
        // while(rs.next()){
        //     System.out.println(rs.getInt("id"));
        // }
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource ds = (DataSource)app.getBean("dataSource");
        Connection conn = ds.getConnection();
        String sql = "select * from user";
        PreparedStatement pstat = conn.prepareStatement(sql);
        ResultSet rs = pstat.executeQuery();
        while(rs.next()){
            System.out.println(rs.getInt("id"));
        }
    }
}

③. 数据库连接池抽取配置文件

<?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:druid.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName"  value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
        <property name="initialSize" value="${initialSize}"/>
        <property name="maxActive" value="${maxActive}"/>
        <property name="maxWait" value="${maxWait}"/>
    </bean>
</beans>
DataSource dataSource = (DataSource)app.getBean("dataSource");
try {
    Connection conn = dataSource.getConnection();
    System.out.println(conn);
} catch (SQLException throwables) {
    throwables.printStackTrace();
}

这里会产生一个严重错误,产生原因是,在applicationContext.xml中,直接使用usernameusername使{username}引入的用户名,并非是我们配置文件中的用户名username,排查发现当我们直接给用户名可以正常工作,但是使用{}引入变量就会造成错误,所以得出一个结论,我们使用${username}调用配置文件的值时,spring会先从默认系统变量中寻找,找到了直接使用默认的,不会继续寻找配置文件中的,被劫糊了,而系统环境变量与实际的数据库username,而两者编码和内容都不一致,导致连接数据库失败

解决办法,不要在配置文件中适用username属性,虽然druid连接池默认应该是username,但是这里使用spring加载,为了避免spring的混乱,所以还是修改一下druid配置文件中username的名字,改成dbusername或者其他,然后在spring配置文件中引入${dbusername}即可

driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
dbusername=root
# 将username改为dbusername
password=123456
initialSize=5
maxActive=10
maxWait=3000

6. Spring注解开发

  • 好处:类比web3.0之前的web开发(配置文件形式的)和web3.0及以后的web开发(注解形式的)
  • 原因:Spring是轻代码重配置的框架,我们需要写的代码其实并不是很多,配置的文件那才是相当的多,配置繁多,影响开发效率,所以注解开发是一种简化开发的解决方案,注解代替xml配置文件即可以简化配置,又可以提高开发效率,何乐而不为

①. Spring原始注解

注解名称 适用范围 注解说明
@Component 用在所有类上 实例化JavaBean
@Controller 用在web层类上 实例化JavaBean(语义化web层)
@Service 用在service层类上 实例化JavaBean(语义化service层)
@Repository 用在dao层类上 实例化JavaBean(语义化dao层)
@Autowired 用在字段上 根据类型进行依赖注入
@Qualifier 结合@Autowired,用在字段上 根据名称进行依赖注入
@Resource 等同于@Autowired+@Qualifier,用在字段上 按照名称进行依赖注入
@Value 用在字段上,一般用在使用${}引入数据库配置文件相关属性上 注入普通属性值
@Scope 用在类上,指定实例化模式是 多例 或 单例,prototypesingleton 标注JavaBean作用范围
@PostConstruct 用在方法上,指定JavaBean对象的初始化方法init-method 标注JavaBean的初始化方法
@PreDestory 用在方法上,指定JavaBean对象的销毁方法destory-method 标注JavaBean的销毁方法

注意:使用注解开发时,需要在applicationContext.xml中配置组件扫描

作用:指定哪个包及其子包下的JavaBean需要进行扫描,需要扫描的注解对应的类,字段和方法

<?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="com.zhiyuan"/>
</beans>

②. Spring新注解

虽然原始注解解决了原来很多问题,但依然有些配置问题无法解决,于是Spring新注解出现了,它采用了一种使用类来描述配置文件的方法来提升我们的工作效率,配置文件烦杂,并不意味着我们不再需要配置文件,所以引入了用类来表示配置文件的解决方案

注解名称 适用范围 注解说明
@Configuration 用在类上 指定当前类为Spring配置类,当创建容器时会从该类上加载注释
@ComponentScan 用在类上 指定Spring在初始化容器时扫描的包,作用等同于<context:component-scan base-package="com.zhiyuan"/>
@Bean 用在方法上 标注将该方法的返回值存储到Spring容器中
@PropertySource 用于加载 .properties配置文件中的配置信息
@Import 用于导入其他配置类
//标志本类为Spring的核心配置类
@Configuration
//设置组件扫描路径(JavaBean的扫描路径),作用等同于<context:component-scan base-package="com.zhiyuan"/>
@ComponentScan("com.zhiyuan")
//引入配置文件
@PropertySource("classpath:druid.properties")
public class DruidConfig {
 @Value("${driverClassName}")
 private String driver;
 @Value("${url}")
 private String url;
 @Value("${dbusername}")
 private String username;
 @Value("${password}")
 private String password;
 @Value("${initialSize}")
 private int initialSize;
 @Value("${maxActive}")
 private int maxActive;
 @Value("${maxWait}")
 private int maxWait;
 @Bean("dataSource")
 public DataSource getDataSource(){
     Properties pros = new Properties();
     DataSource ds = null;
     try {
         pros.setProperty("driverClassName",driver);
         pros.setProperty("url",url);
         pros.setProperty("username",username);
         pros.setProperty("password",password);
         pros.setProperty("initialSize",String.valueOf(initialSize));
         pros.setProperty("maxActive", String.valueOf(maxActive));
         pros.setProperty("maxWait", String.valueOf(maxWait));
         ds = DruidDataSourceFactory.createDataSource(pros);
     } catch (Exception e) {
         e.printStackTrace();
     }
     return ds;
 }
}
//标志本类为Spring的核心配置类
@Configuration
//设置组件扫描路径(JavaBean的扫描路径),作用等同于<context:component-scan base-package="com.zhiyuan"/>
@ComponentScan("com.zhiyuan")
//引入配置文件
@PropertySource("classpath:druid.properties")
//加入其它配置文件,作用等同于<import resource="applicationContext-xxx.xml" />
@Import(DruidConfig.class)
public class SpringConfiguration {

}
public class ConfigTest {
 @Test
 public void test1(){
     ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfiguration.class);
     DataSource dataSource = (DataSource) app.getBean("dataSource");
     try {
         Connection conn = dataSource.getConnection();
         System.out.println(conn);
     } catch (SQLException throwables) {
         throwables.printStackTrace();
     }
 }
}

正常输出对象的hash地址值

③. Spring集成Jnuit

  • 导入SpringTest的依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
  • 使用注解 @RunWith(SpringJUnit4ClassRunner.class) 替换运来的运行期
  • 使用 @ContextConfiguration(classes = {SpringConfiguration.class}) 指定配置文件或配置类
  • 使用 @Autowired 注入需要测试的对象
  • 创建测试方法进行测试
代码实现
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
    @Autowired
    private DataSource dataSource;
    @Test
    public void test1(){
        try {
            Connection conn = dataSource.getConnection();
            System.out.println(conn);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

7. AOP

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

①. 概述

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

②. 作用

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

  • 在不修改源码的情况下对方法的功能进行增强
  • 优点:减少重复代码,提高开发效率,便于维护

③. AOP底层实现

  • 动态代理技术

    • JDK代理:基于接口的动态代理对象

      image-20210215125040875
    • CGLIB代理:基于父类的动态代理技术

      image-20210215125109151

④. 相关概念

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被AOP增强后,就产生一个结果代理类
  • Joinpoint(连接点):被拦截到的点,在spring中指的是方法,因为spring只支持方法类型的连接点
  • Pointcut(切入点):指我们要对哪些Joinpoint进行拦截定义
  • Advice(通知/增强):指拦截到Joinpoint之后所要做的增强
  • Aspect(切面):是切入点和增强的结合
  • Weaving(织入):切入点和增强结合的过程

概念性的东西着重归纳出自己的见解,而不是死记硬背,像这种恶心的概念,也只有记笔记的时候,才会出现

⑤. XML配置AOP详解

  • 切点表达式

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

    • 语法:<aop:增强类型 method="切面类中方法名" pointcut="切点表达式" />
    • 增强类型
      • 前置增强:<aop:before>:在切点方法执行之前执行
      • 后置增强:<aop:after-returning>:在切点方法执行之后执行
      • 环绕增强:<aop:around>:在切点方法之前之后均执行
      • 异常抛出增强:<aop:throwing>:当增强方法出现异常时执行
      • 最终增强:<aop:after>:在所有增强方法执行完毕时执行
  • 代码实现

    • public class Target {
          public void show(){
              System.out.println("show方法");
          }
      }
      
      public class MyAspect {
          public void before(){
              System.out.println("前置增强方法");
          }
          public void afterReturning(){
              System.out.println("后置增强方法");
          }
          public Object around(ProceedingJoinPoint pjp) throws Throwable {
              System.out.println("前置增强方法");
              Object proceed = pjp.proceed();
              System.out.println("后置增强方法");
              return proceed;
          }
          public void afterThrowing(){
              System.out.println("增强方法出现异常");
          }
          public void after(){
              System.out.println("最终增强");
          }
      }
      
      <bean id="target" class="com.zhiyuan.proxy.aop.Target"/>
      <bean id="aspect" class="com.zhiyuan.proxy.aop.MyAspect"/>
      <aop:config>
      <aop:aspect ref="aspect">
          <aop:before method="before" pointcut="execution(* com.zhiyuan.proxy.aop.*.*(..))"/>
          <aop:after-returning method="afterReturning" pointcut="execution(* com.zhiyuan.proxy.aop.*.*(..))"/>
          <aop:around method="around" pointcut="execution(* com.zhiyuan.proxy.aop.*.*(..))"/>
          <aop:after-throwing method="afterThrowing" pointcut="execution(* com.zhiyuan.proxy.aop.*.*(..))"/>
          <aop:after method="after" pointcut="execution(* com.zhiyuan.proxy.aop.*.*(..))"/>
      </aop:aspect>
      </aop:config>
      

⑥. 切点表达式抽取

  • 当多个增强的切点表达式相同时,可以将切点表达式进行抽取,在增强中使用 pointcut-ref 属性替代 pointcut 属性来引用抽取后的切点表达式
<bean id="target" class="com.zhiyuan.proxy.aop.Target"/>
<bean id="aspect" class="com.zhiyuan.proxy.aop.MyAspect"/>
<aop:config>
    <aop:aspect ref="aspect">
        <aop:pointcut id="myPointcut" expression="execution(* com.zhiyuan.proxy.aop.*.*(..))"/>
        <aop:before method="before" pointcut-ref="myPointcut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"/>
        <aop:around method="around" pointcut-ref="myPointcut"/>
        <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut"/>
        <aop:after method="after" pointcut-ref="myPointcut"/>
    </aop:aspect>
</aop:config>

⑦. 注解配置AOP

  • 配置步骤

    • 使用 @Aspect 标注切面类
    • 使用 @增强类型 标注增强方法
    • 在配置文件中配置aop自动代理 <aop:aspectj-autoproxy/>
  • 注解增强类型

    • @Before:用于配置前置增强,指定增强的方法在切入点之前执行
    • @AfterReturning:用于配置后置增强,指定增强的方法在切入点方法之后执行
    • @Around:用于配置环绕增强,指定增强的方法在出现异常时执行
    • @AfterThrowing:用于配置异常抛出通知,指定增强的方法在出现异常时执行
    • @After:用于配置最终通知,无论增强方法执行是否有异常都会执行
  • 代码实现

public interface TargetInterface {
    public abstract void show();
}
@Component("target")
public class Target implements TargetInterface{
    @Override
    public void show(){
        System.out.println("show方法");
    }
}
@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("execution(* com.zhiyuan.proxy.anno.*.*(..))")
    public void before(){
        System.out.println("前置增强方法");
    }
    @AfterReturning("execution(* com.zhiyuan.proxy.anno.*.*(..))")
    public void afterReturning(){
        System.out.println("后置增强方法");
    }
    @Around("execution(* com.zhiyuan.proxy.anno.*.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("前置增强方法");
        Object proceed = pjp.proceed();
        System.out.println("后置增强方法");
        return proceed;
    }
    @AfterThrowing("execution(* com.zhiyuan.proxy.anno.*.*(..))")
    public void afterThrowing(){
        System.out.println("增强方法出现异常");
    }
    @After("execution(* com.zhiyuan.proxy.anno.*.*(..))")
    public void after(){
        System.out.println("最终增强");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启组件扫描-->
    <context:component-scan base-package="com.zhiyuan.proxy.anno"/>
    <!--设置AOP自动代理-->
    <aop:aspectj-autoproxy/>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-anno.xml")

public class AnnoTest {
    @Autowired
    private TargetInterface target;
    @Test
    public void test1(){
        target.show();
    }
}
前置增强方法
前置增强方法
show方法
后置增强方法
最终增强
后置增强方法
  • 抽取切点表达式
    • 在切面内定义方法,在该方法上使用 @Pointcut 注解定义切点表达式,然后在增强注解中进行引用
  • 代码实现
@Component("myAspect")
@Aspect
public class MyAspect {
    @Before("MyAspect.pointcut()")
    public void before(){
        System.out.println("前置增强方法");
    }
    @AfterReturning("pointcut()")
    public void afterReturning(){
        System.out.println("后置增强方法");
    }
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("前置增强方法");
        Object proceed = pjp.proceed();
        System.out.println("后置增强方法");
        return proceed;
    }
    @AfterThrowing("pointcut()")
    public void afterThrowing(){
        System.out.println("增强方法出现异常");
    }
    @After("pointcut()")
    public void after(){
        System.out.println("最终增强");
    }
    @Pointcut("execution(* com.zhiyuan.proxy.anno.*.*(..))")
    public void pointcut(){}
}

8. SpringJdbcTemplate

①. 导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>

②. 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:druid.properties"/>
    <!--配置DataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${driverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${dbusername}"/>
        <property name="password" value="${password}"/>
    </bean>
    <!--配置JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>
public class JdbcTemplateTest {
    @Test
    public void test1(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        JdbcTemplate jdbcTemplate = (JdbcTemplate)app.getBean("jdbcTemplate");
        List<User> list = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<User>(User.class));
        for (User user : list) {
            System.out.println(user);
        }
    }
}

9. Spring的事务控制

①. 编程式事务控制

  • PlatformTransactionManager :Spring的事务管理器
    • TransactionStatus getTransaction(TransactionDefination defination):获取事务状态信息
    • void commit(TransactionStatus status):提交事务
    • void rollback(TransactionStatus status):回滚事务
  • TransactionDefination:事务信息对象
    • int getIsolationLevel():获取事务的隔离级别
    • int getPropogationBehavior:获取事务的传播行为
    • int getTimeout():获取超时时间
    • boolean isReadOnly():是否只读
  • 事务隔离级别
    • ISOLATION_DEFAULT:默认
    • ISOLATION_READ_UNCOMMITTED:读未提交
    • ISOLATION_READ_COMMITTED:读已提交
    • ISOLATION_REPEATABLE_READ:可重复读
    • ISOLATION_SERIALIZABLE:串行化
  • 事务传播行为
    • REQUIRED:如果当时没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,一般选择这一项(默认值)
    • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
    • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
    • REQUES_NEW:新建事务,如果当前在事务中,就把当前事务挂起
    • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
    • NEVER:以非事务方式运行,如果当前存在事务,抛出异常
    • NESTED:如果当前存在事务,则在嵌套事务内执行,如果当前没有实物,则执行 REQUIRED 类似的操作
    • 超时时间:默认值为-1,没有超时限制,单位为秒可设置超时时间
    • 是否只读:建议查询时设置为只读
  • TransactionStatus:事务状态信息
    • boolean hasSavepoint():是否存储回滚点
    • boolean isCompleted():事务是否完成
    • boolean isNewTransaction():是否是新事务
    • boolean isRollbackOnly():事务是否回滚

②. 声明式事务控制

  • Spring声明式事务控制底层就是AOP

  • 基于xml配置实现

    • <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:context="http://www.springframework.org/schema/context"
             xmlns:aop="http://www.springframework.org/schema/aop"
             xmlns:tx="http://www.springframework.org/schema/tx"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
              http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
          <!--加载配置文件-->
          <context:property-placeholder location="classpath:druid.properties"/>
          <!--配置DataSource-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
              <property name="driverClassName" value="${driverClassName}"/>
              <property name="url" value="${url}"/>
              <property name="username" value="${dbusername}"/>
              <property name="password" value="${password}"/>
          </bean>
          <!--配置JdbcTemplate-->
          <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
              <property name="dataSource" ref="dataSource"/>
          </bean>
          <bean id="userDao" class="com.zhiyuan.dao.impl.UserDaoImpl">
              <property name="jdbcTemplate" ref="jdbcTemplate"/>
          </bean>
          <bean id="userService" class="com.zhiyuan.service.impl.UserServiceImpl">
              <property name="userDao" ref="userDao"/>
          </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="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/>
              </tx:attributes>
          </tx:advice>
          <!--配置事务的织入-->
          <aop:config>
              <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zhiyuan.service.impl.*.*(..))"/>
          </aop:config>
      </beans>
      
  • 基于注解配置实现

    • 使用 @Transactional 在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml的配置方式
    • 注解使用在类上,那么该类下所有方法都是用同一套注解参数配置
    • 使用在方法上,不同的方法可以采用不同的事务参数配置
    • xml配置文件中要开启事务的注解驱动 <tx:annotation-driven>
    • 事务控制配置建议使用xml配置

10. Spring与web环境的集成

①. ApplicationContext的获取

  • 使用 ServletContextListener 监听web应用的启动,在 ServletContext 被创建时,创建 ApplicationContext 对象并存储到 ServletContext 域中,这样就可以在当前项目的任意位置获取到Spring容器

  • Spring提供了一个监听器 ContextLoaderListener 就是对上述功能的封装,该监听器内部加载Spring配置文件,创建了Spring容器对象,并存储到ServletContext域中,提供一个工具类供使用者获得Spring容器对象

  • 使用步骤

    • 导入 spring-web 坐标
    • web.xml 中配置 ContextLoaderListener 监听器
    • 使用 WebApplicationContextUtils 获得应用上下文对象 ApplicationContext
  • 代码实现

    • <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>5.0.5.RELEASE</version>
      </dependency>
      
    • <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
               version="4.0">
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:applicationContext.xml</param-value>
          </context-param>
          <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
      </web-app>
      
    • ServletContext servletContext = this.getServletContext();
      ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
      

二、SpringMVC

  • SpringMVC是一种基于Java实现MVC设计模式的请求驱动类型的轻量级web框架,属于SpringFrameWork的后续产品,已经融合在 SpringWebFlow 中
  • 它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无序实现任何接口,同时它还支持RESTful 编程风格的请求

RestFul请求规范:

get:查询数据

post:添加数据

put:修改数据

delete:删除数据

1. 开发步骤

①. 导入SpringMVC的依赖

②. 配置SpringMVC核心控制器DispatcherServlet

③. 编写Controller业务和View页面

④. 将Controller使用注解配置到Spring容器中

⑤. 配置spring-mvc.xml文件(配置组件扫描)

⑥. 客户端发起请求测试

2. 代码实现

  • 导入SpringMVC依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.5.RELEASE</version>
</dependency>
  • 配置Servlet
<!--配置SpringMVC的前端控制器-->
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  • 编写Controller
@Controller
public class UserController extends HttpServlet {
    @RequestMapping("/show")
    public String show(){
        System.out.println("show地址映射");
        return "new.jsp";
    }
}
  • 配置SpringMVC配置文件组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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="com.zhiyuan.controller"/>
</beans>

3. 流程图示

image-20210222094937825

4. 执行流程

image-20210303095145331

①. 用户发送请求到前端控制器DispatcherServlet

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

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

④. DisptcherServlet调用HandlerAdaptor处理器适配器

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

⑥. Controller执行完成返回ModelAndView

⑦. HandlerAdaptor将controller执行结ModelAndView返回给DispatcherServlet

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

⑨. ViewReslover解析后返回具体View

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

5. SpringMVC注解解析

①. @RequestMapping

  • 用在类上
    • 映射模块路径:请求URL的一级访问目录,如果类上不加,默认为根目录
  • 用在方法上
    • 映射方法路径:请求URL的二级访问目录,与类上的目录组合完成指定方法的访问
  • 属性
    • value:用于指定请求的URL
    • method:用于指定请求的方式 值建议使用RequestMethod枚举类型
    • params:用于指定限制请求参数的条件,它支持简单的表达式,要求请求参数的key和value必须和配置的一模一样

6. SpringMVC的xml配置

<!--开启Controller注解扫描-->
<context:component-scan base-package="com.zhiyuan.controller">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--注册处理器映射器-->
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--注册处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                        <value>text/plain;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
            <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                        <value>text/plain;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </list>
    </property>
</bean>
<!--注册内部视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

7. SpringMVC的相关组件

  • 前端控制器:DisptcherServlet(SpringMVC提供的分发请求的Servlet,需要我们在web.xml中配置)
  • 处理器映射器:HandlerMapping(SpringMVC提供的映射容器)
  • 处理器适配器:HandlerAdapter(SpringMVC提供的处理器适配器)
  • 处理器:Handler(需要我们写的Controller)
  • 视图解析器:ViewResolver(SpringMVC提供的资源视图解析器)
  • 视图:View

8. SpringMVC数据响应方式

①. 页面跳转

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

    @RequestMapping("/test")
    public String test(){
        return "index.jsp";
    }
    
  • 通过ModelAndView对象返回

    @RequestMapping("/test")
    public ModelAndView test(){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("new");
        modelAndView.addObject("username","zhiyuan");
        return modelAndView;
    }
    
    @RequestMapping("/test2")
    public ModelAndView test2(ModelAndView modelAndView){
    	modelAndView.setViewName("new");
        modelAndView.addObject("username","zhiyuan");
        return modelAndView;
    }
    
    @RequestMapping("/test3")
    public Model test3(Model model){
        model.addAttribute("username","张三");
        return model;
    }
    
    @RequestMapping("/test4")
    public String test4(HttpServletRequest request,HttpServletResponse response){
        request.setAttribute("username","张三");
        return "index.jsp";
    } 
    

②. 回写数据

  • 直接返回字符串

    @RequestMapping("/test3")
    @ResponseBody
    public String test3(HttpServletResponse response){
        response.setContentType("text/html;charset=utf-8");
        return "直接回写字符串";
    }
    
  • 返回对象或集合

    <!--配置处理器映射器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean id="stringConverter" class="org.springframework.http.converter.StringHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/html;charset=UTF-8</value>
                            <value>application/json;charset=UTF-8</value>
                            <value>text/plain;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
                <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>text/html;charset=UTF-8</value>
                            <value>application/json;charset=UTF-8</value>
                            <value>text/plain;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </list>
        </property>
    </bean>
    <!--开启扫描注解驱动-->
    <mvc:annotation-driven/>
    
    @RequestMapping("/test")
    @ResponseBody
    public Person test(){
        Person person = new Person(); 
        person.setAge(25);
        person.setGender("male");
        person.setName("张三");
        return person;
    }
    

9. SpringMVC获得请求数据

①. 获得请求参数

  • 基本类型参数
  • POJO类型参数(JavaBean)
  • 数组类型参数
  • 集合类型参数

②. 获得一个基本类型参数

@RequestMapping("/test")
@ResponseBody
public void test(String username,String age,String gender){
    System.out.println(username + "\t" + age + "\t" + gender);
}
image-20210304155103994
张三	26	male

③. 获得一个POJO类型参数

@RequestMapping("/test4")
@ResponseBody
public void test4(User user){
    System.out.println(user);
}
image-20210304160053808
User{username='张三', gender='male', age=26}
@RequestMapping("/test4")
@ResponseBody
public void test4(@RequestBody User user){
    System.out.println(user);
}
{
	"username":"zhiyuan",
	"gender":"male",
	"age":26
}
User{username='zhiyuan', gender='male', age=26}

④. 获得一个数组类型参数

@RequestMapping("/test5")
@ResponseBody
public void test5(String[] id){
    System.out.println(Arrays.asList(id));
}
image-20210304162014425
[111, 222]

⑤. 获得一个集合类型参数(一)

package com.zhiyuan.domain;
import java.util.List;
public class VO {
    private List<User> userList;

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

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

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("VO{");
        sb.append("userList=").append(userList);
        sb.append('}');
        return sb.toString();
    }
}

@RequestMapping("/test6")
@ResponseBody
public void test6(VO vo){
    System.out.println(vo);
}
image-20210304165235811
VO{userList=[User{username='??????', gender='male', age=26}, User{username='??????', gender='female', age=22}]}

⑥. 获得一个集合类型参数(二)

<%@ page contentType="text/html;charset=utf-8" language="java"%>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Ajax请求</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.1.1.js"></script>
</head>
<body>

<script>
    var userList = new Array();
    userList.push({username:"zhangsan",age:26});
    userList.push({username:"lisi",age:28});
    $.ajax({
        type:"POST",
        url:"${pageContext.request.contextPath}/test7",
        data:JSON.stringify(userList),
        contentType:"application/json;charset=utf-8"
    });
</script>
</body>
</html>
@RequestMapping("/test7")
@ResponseBody
public void test7(@RequestBody List<User> userList){
    System.out.println(userList);
}

⑦. 开启静态资源访问

<!--静态资源的访问-->
<mvc:resources mapping="/js/**" location="/js/">
<mvc:resources mapping="/img/**" location="/img/">
<mvc:resources mapping="/css/**" location="/css/">    
<!--交由tomcat去寻找对应静态资源-->    
<mvc:default-servlet-handler/>    

⑧. 配置全局乱码过滤器

<!--配置全局编码过滤器-->
<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>

⑨. 参数绑定注解

@RequestMapping("/test5")
@ResponseBody
public void test5(@RequestParam("id") String[] ids){
    System.out.println(Arrays.asList(ids));
}
  • @RequestParam
    • value:与请求参数名称进行绑定
    • required:请求参数是否必须携带,默认为true,不携带参数请求URL会报错
    • defaultValue:请求参数未写时默认的值

10. Restful风格

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

  • GET:用于获取数据
  • POST:用户添加数据
  • PUT:用于修改数据
  • DELETE:用于删除数据

①. Restful风格请求参数的获取

@RequestMapping("/test8/{username}")
@ResponseBody
public void test8(@PathVariable("username") String username, HttpServletRequest request){
    System.out.println(request.getMethod()+":"+username);
}
GET:zhangsan
POST:zhangsan
PUT:zhangsan
DELETE:zhangsan

因为每个方法都需要添加 @ResponseBody

所以Spring为我们提供了一个解决方案 @RestController

通过源码可知 @RestController = @Controller + @ResponseBody

11. SpringMVC自定义类型转换器

SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置

但是并不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,来如:日期类型的数据就需要自定义转换器

步骤

  • 定义转换器类实现 Converter 接口
  • 在配置文件中声明转换器
  • <annotation-driven> 中引用转换器
public class DateConverter implements Converter<String,Date>{
    public Date convert(String source) {
        System.out.println(source);
        Date date = null;
        try {
            date = new SimpleDateFormat("yyyy-MM-dd").parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}
<mvc:annotation-driven conversion-service="conversionService"/>
<!--声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.zhiyuan.controller.DateConverter"/>
        </list>
    </property>
</bean>
@RequestMapping("/test9")
@ResponseBody
public void test9(Date date){
    System.out.println(date);
}

12. SpringMVC获取请求头数据

①. @RequestHeader

  • 使用 @RequestHeader 获取请求头信息,相当于web阶段学习的 request.getHeader(name)
  • value:请求头名称
  • required:是否必须携带某请求头
@RequestMapping("/test10")
@ResponseBody
public void test10(@RequestHeader(value = "User-Agent",required = true) String userAgent){
    System.out.println(userAgent);
}

②. @CookieValue

  • 使用 @CookieValue 可以获取指定Cookie的值
  • value:指定cookie的名称
  • required:是否必须携带某cookie
@RequestMapping("/test11")
@ResponseBody
public void test11(@CookieValue(value = "JSESSIONID",required = true) String sessionId){
    System.out.println(sessionId);
}

13. SpringMVC文件上传

①. 简单上传表单

<form action="/test12" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username"/><br>
    头像:<input type="file" name="upload"><br>
    <input type="submit" value="提交">
</form>

②. 导入依赖

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.4</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.2.2</version>
</dependency>

③. 配置文件上传解析器

<!--配置文件上传解析器-->
<!--此id必须为multipartResolver,是固定写法,不一样就是错-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--上传文件编码类型-->
    <property name="defaultEncoding" value="UTF-8"/>
    <!--上传单个文件大小-->
    <property name="maxUploadSizePerFile" value="5242880"/>
    <!--上传文件总大小-->
    <property name="maxUploadSize" value="5242880"/>
</bean>

④. 编写文件上传处理代码

@RequestMapping(value = "/test12",method = RequestMethod.POST)
@ResponseBody
public void test12(@RequestParam(value = "username",required = false) String username,@RequestParam(value="upload",required=false) MultipartFile upload){
    System.out.println(username);
    System.out.println(upload);
    String originalFilename = upload.getOriginalFilename();
    upload.transferTo(new File("F:\\upload\\"+ MyUtils.getUUID() + originalFilename));
}

14. SpringMVC多文件上传

①. 简单上传表单

<form action="/test13" method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username"/><br>
    头像:<input type="file" name="upload"><br>
    头像:<input type="file" name="upload"><br>
    头像:<input type="file" name="upload"><br>
    头像:<input type="file" name="upload"><br>
    <input type="submit" value="提交">
</form>

②. 编写文件上传处理代码

@RequestMapping("/test13")
@ResponseBody
public void test13(@RequestParam(value = "username",required = false) String username,@RequestParam(value = "upload",required = false) MultipartFile [] uploads) throws IOException {
    System.out.println(username);
    for (MultipartFile upload : uploads) {
        String originalFilename = upload.getOriginalFilename();
        upload.transferTo(new File("F:\\upload\\" + MyUtils.getUUID() + originalFilename));
    }
}

15. SpringMVC拦截器

拦截器Interceptor相当于web阶段的过滤器Filter

区别 过滤器 拦截器
使用范围 servlet规范中的一部分,任何Web工程都可以使用 SpringMVC自己的部分,需要使用SpringMVC框架才能使用
拦截范围 在web.xml中配偶之url-pattern映射为/*时,可以拦截所有资源 只拦截控制器方法,对静态资源不进行拦截

①. 自定义拦截器

  • 创建拦截器类实现 HandlerInterceptor

  • 配置拦截器

  • 代码实现

    public class MyInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("被拦截的方法执行之前执行preHandle方法");
            //放行请求
            return true;
        }
    
        @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方法");
        }
    }
    
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.zhiyuan.interceptor.MyInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
    

②. 拦截器方法

  • preHandle():在请求映射的方法被调用之前,调用该方法,方法返回值为布尔型,用于控制请求是否放行,为 true 放行,为 false 不放行
  • postHandle():在请求映射方法执行之后,DispatcherServlet视图渲染之前被调用,可以在方法中操作修改ModelAndView对象
  • afterCompletion():在整个请求结束后被调用,也就是在DispatcherServlet渲染视图之后执行

16. SpringMVC异常处理

  • 编译时异常和运行时异常:前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试来减少运行时异常的发生

  • 异常处理思路

    • 通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行处理,如下图:
    image-20210310131401638
  • 异常处理两种方式

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

①. 简单异常处理器

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

②. 自定义异常处理器

public class MyException extends Exception{}
public class MyExceptionResolver implements HandlerExceptionResolver {
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        ModelAndView modelAndView = new ModelAndView();
        if(ex instanceof MyException){
            modelAndView.addObject("info","自定义异常");
        }else if (ex instanceof ClassCastException){
            modelAndView.addObject("info","类型转换异常");
        }else{
            modelAndView.addObject("info","其他异常");
        }
        return modelAndView;
    }
}
<!--配置自定义异常处理器-->
<bean class="com.zhiyuan.resolver.MyExceptionResolver"/> 

三、案例练习

1. Spring环境搭建

①. 配置 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--1. 配置组件扫描-->
    <context:component-scan base-package="com.zhiyuan"/>
    <!--2. 加载数据库配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--3. 配置数据库连接池对象-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${c3p0.driverClass}"/>
        <property name="jdbcUrl" value="${c3p0.jdbcUrl}"/>
        <property name="user" value="${c3p0.user}"/>
        <property name="password" value="${c3p0.password}"/>
    </bean>
    <!--4. 配置JdbcTemplate对象-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>

②. 配置 spring-mvc.xml

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

    <!--1. 配置mvc注解驱动-->
    <mvc:annotation-driven/>
    <!--2. 交给tomcat去寻找静态资源-->
    <mvc:default-servlet-handler/>
    <!--3. 配置内部视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!--配置文件上传解析器-->
    <!--此id必须为multipartResolver,是固定写法,不一样就是错-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--上传文件编码类型-->
        <property name="defaultEncoding" value="UTF-8"/>
        <!--上传单个文件大小-->
        <property name="maxUploadSizePerFile" value="5242880"/>
        <!--上传文件总大小-->
        <property name="maxUploadSize" value="5242880"/>
    </bean>
</beans>

③. 配置 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
  <!--配置全局编码过滤器-->
  <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>
  <!--配置Spring容器-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <!--配置SpringMVC的前端控制器-->
  <servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

④. 配置 jdbc.properties

c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql://localhost:3306/test?useUnicode=ture&characterEncoding=utf8&useSSL=false&serverTimezone=Hongkong
c3p0.user=root
c3p0.password=123456
c3p0.initialPoolSize=10
c3p0.maxPoolSize=100
c3p0.checkoutTimeout=3000

⑤. 配置 pom.xml

<dependencies>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.21</version>
</dependency>
<dependency>
  <groupId>c3p0</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.1.2</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.10</version>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.0.1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>javax.servlet.jsp-api</artifactId>
  <version>2.2.1</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.0</version>
</dependency>
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>
<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.3</version>
</dependency>
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.2</version>
</dependency>
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>1.7.7</version>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>5.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.servlet.jsp.jstl</groupId>
  <artifactId>jstl-api</artifactId>
  <version>1.2</version>
</dependency>
</dependencies>

2. 业务代码

四、 MyBatis

1. MyBatis简介

  • MyBatis是一个优秀的基于Java的持久层框架,它内部封装了JDBC,是开发者只需要关住 SQL语句本身,而不需要花费精力器处理加载驱动、创建Connection连接对象、创建Statement对象等繁杂的过程
  • MyBatis通过XML和注解的方式将要执行的各种Statement对象配置起来,并通过Java对象和Statement中SQL语句的动态参数进行映射最终生成可执行的SQL语句
  • 最后MyBatis框架执行SQL并将结果映射为Java对象并返回,采用ORM思想解决了实体和数据库的映射问题,对JDBC进行了封装,屏蔽了JDBC的API底层访问细节,使我们不用与JDBC的API打交道就可以完成对数据库的持久化操作

2. MyBatis开发步骤

①. 添加MyBatis的坐标

②. 创建user数据表

③. 编写User实体类

④. 编写映射文件UserMapper.xml

⑤. 编写核心文件SqlMapConfig.xml

⑥. 编写持久层代码

3. MyBatis查询数据

只有一个参数时建议使用 ${value} ,用在like模糊查询

<!--UserMapper.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="userMapper">
    <select id="findAll" resultType="com.zhiyuan.mybatis.entity.User">
        select * from user
    </select>
</mapper>
<!--SqlMapConfig.xml-->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost/test?serverTimezone=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/zhiyuan/mapper/UserMapper.xml"/>
    </mappers>
</configuration>
@Test
public void test1() throws IOException {
    //读取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //创建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql返回结果
    List<User> userList = sqlSession.selectList("userMapper.findAll");
    //输出结果
    System.out.println(userList);
    //释放资源
    sqlSession.close();
}

4. MyBatis插入数据

MyBatis默认事务不提交,增删改操作无效,需要使用 sqlSession.commit(); 提交事务

<insert id="save" parameterType="com.zhiyuan.mybatis.entity.User">
    insert into user values (#{id},#{name},#{age},#{gender})
</insert>
@Test
public void test2() throws IOException {
    //读取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //创建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql返回结果
    int insert = sqlSession.insert("userMapper.save", new User(8, "zhiyuan008", 26, "male"));
    sqlSession.commit();
    System.out.println("受影响的行数为"+insert);
    //释放资源
    sqlSession.close();
}

5. Mybatis修改数据

<update id="change" parameterType="com.zhiyuan.mybatis.entity.User">
    update user set name=#{name}, gender=#{gender} where id=#{id}
</update>
@Test
public void test3() throws IOException {
    //读取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //创建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql返回结果
    int update = sqlSession.update("userMapper.change", new User(8, "lisi", 99, "female"));
    sqlSession.commit();
    System.out.println("受影响的行数为"+update);
    //释放资源
    sqlSession.close();
}

6. MyBatis删除数据

<delete id="delete" parameterType="java.lang.Integer">
    delete from user where id=#{id}
</delete>
@Test
public void test4() throws IOException {
    //读取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //创建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //执行sql返回结果
    int delete = sqlSession.delete("userMapper.delete", 8);
    sqlSession.commit();
    System.out.println("受影响的行数为"+delete);
    //释放资源
    sqlSession.close();
}

7. MyBatis核心配置文件

①. environments 标签

  • 数据库环境的配置,支持多配置
<!--指定默认的环境名称-->
<environments default="development">
    <!--指定当前环境的名称-->
    <environment id="development">
        <!--指定事务管理类型为JDBC-->
        <transactionManager type="JDBC"/>
        <!--指定当前数据源类型为连接池-->
        <dataSource type="POOLED">
            <!--数据源配置的基本参数-->
            <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost/test?serverTimezone=Asia/Shanghai&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>
  • 事务管理器

    • JDBC
    • MANAGED
  • 数据源

    • UNPOOLED:每次被请求时打开连接
    • POOLED:每次请求时从连接池中获取连接
    • JNDI:应用容器使用

②. mappers 标签

  • 加载映射
<mappers>
    <mapper resource="com/zhiyuan/mapper/UserMapper.xml"/>
</mappers>
  • 使用相对于类路径的资源引用: <mapper resource="com/zhiyuan/mapper/UserMapper.xml">
  • 使用完全限定资源定位符:<mapper url="file:///var/mapper/UserMapper.xml">
  • 使用映射器接口实现类的完全限定类名:<mapper class="com.zhiyuan.builder.UserMapper">
  • 将包内的映射器接口实现全部注册为映射器:<package name="com.zhiyuan.builder">

③. properties 标签

  • 加载外部properties配置文件
<properties resource="jdbc.properties"/>
<!--指定默认的环境名称-->
<environments default="development">
    <!--指定当前环境的名称-->
    <environment id="development">
        <!--指定事务管理类型为JDBC-->
        <transactionManager type="JDBC"/>
        <!--指定当前数据源类型为连接池-->
        <dataSource type="POOLED">
            <!--数据源配置的基本参数-->
            <property name="driver" value="${driverClassName}"/>
            <property name="url" value="${jdbcUrl}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

④. typeAliases 标签

  • 为指定类型设置别名,方便映射配置
<typeAliases>
	<typeAlias type="com.zhiyuan.mybatis.entity.User" alias="user"/>
</typeAliases>
  • 必须按照规定的顺序配置typeAliases标签,否则会报错

The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".

<?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="userMapper">
    <select id="findAll" resultType="user">
        select * from user
    </select>
    <insert id="save" parameterType="user">
        insert into user values (#{id},#{name},#{age},#{gender})
    </insert>
    <update id="change" parameterType="user">
        update user set name=#{name}, gender=#{gender} where id=#{id}
    </update>
    <delete id="delete" parameterType="int">
        delete from user where id=#{id}
    </delete>
</mapper>

8. MyBatis的API

  • Resources.getResourceAsStream("SqlMapConfig.xml")

    读取核心配置文件到IO流

  • new SqlSessionFactoryBuilder().build(InputStream is)

    通过加载核心配置文件创建SqlSessionFactory对象

  • SqlSessionFactory.openSession(boolean autoCommit)

    获取SqlSession对象,给定参数设定是否自动提交事务,不给参数默认不自动提交

  • SqlSession对象方法

    • SqlSession.selectOne(String statement,Object parameter)
    • SqlSession.selectList(String statement,Object parameter)
    • SqlSession.insert(String statement,Object parameter)
    • SqlSession.update(String statement,Object parameter)
    • SqlSession.delete(String statement,Object parameter)
    • SqlSession.commit()
    • SqlSession.rollback()

9. MyBatis的代理开发方式

①. 开发规范

  • Mapper.xml文件中的namespace应该与Mapper接口的全限定名相同
  • Mapper接口方法名应该和Mapper.xml中定义的每个statement的id相同
  • Mapper接口方法的输入参数类型应该和Mapper.xml中定义的每个sql的parameterType相同
  • Mapper接口方法的输出参数类型应该和Mapper.xml中定义的每个sql的resultType相同

②. 代码实现

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhiyuan.mybatis.dao.UserDao">
    <select id="findAll" resultType="user">
        select * from user
    </select>
</mapper>    
public interface UserDao {
    public abstract List<User> findAll();
}
public class ServiceDemo {
    public static void main(String[] args) throws IOException {
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml"));
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        List<User> userList = userDao.findAll();
        System.out.println(userList);
    }
}

10. MyBatis映射文件深入

①. 动态SQL

public interface UserDao {
    public abstract List<User> findAll();
    public abstract List<User> findByIds(List<Integer> list);
    public abstract List<User> findByCondition(User user);
}
<select id="findByIds" parameterType="list" resultType="user">
    select * from user
    <where>
        <foreach collection="list" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
<select id="findByCondition" parameterType="user" resultType="user">
    select * from user
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="name!=null">
            and name=#{name}
        </if>
        <if test="age!=0">
            and age=#{age}
        </if>
        <if test="gender!=null">
            and gender=#{gender}
        </if>
    </where>
</select>

②. SQL语句的抽取

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

③. 小结

  • <select>:查询数据
  • <insert>:插入数据
  • <update>:修改数据
  • <delete>:删除数据
  • <where>:条件
  • <if>:判断
  • <foreach>:循环
  • <sql>:sql抽取

11. MyBatis核心文件深入

①. typeHandlers 标签

将结果集中的值转换成指定Java类型

  • BooleanTypeHandler
  • ByteTypeHandler
  • ShortTypeHandler
  • IntegerTypeHandler
  • LongTypeHandler
  • 开发步骤
    • 定义转换类继承自 BaseTypeHandler<T>
    • 实现方法,其中 setNonNullParameter 是java程序设置数据到数据库的回调方法,getNullableResult是查询时mysql字符串类型转为Java的Type类型的方法
    • 在MyBatis核心配置文件中进行注册
    • 测试转换是否正确
public class DateTypeHandler extends BaseTypeHandler<Date> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        long time = parameter.getTime();
        ps.setLong(i,time);
    }

    @Override
    public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return new Date(rs.getLong(columnName));
    }

    @Override
    public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return new Date(rs.getLong(columnIndex));
    }

    @Override
    public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return new Date(cs.getLong(columnIndex));
    }
}
<typeHandlers>
    <typeHandler handler="com.zhiyuan.mybatis.handler.DateTypeHandler"/>
</typeHandlers>

②. plugins 标签

  • MyBatis可以使用第三方插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
  • 开发步骤:
    • 导入通用PageHelper的坐标
    • 在MyBatis核心配置文件中配置PageHelper
    • 测试分页数据获取
  • 导入依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.jsqlparser/jsqlparser -->
<dependency>
  <groupId>com.github.jsqlparser</groupId>
  <artifactId>jsqlparser</artifactId>
  <version>3.2</version>
</dependency>
  • 注册分页插件
<!--注册分页插件-->
<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--pagehelper4.0之后不需要配置此项,否则会出错-->
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>
  • 代码实现
@Test
public void test5() throws IOException {
    //读取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //创建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    PageHelper.startPage(1,3);
    List<User> userList = userDao.findAll();
    System.out.println(userList);
    PageInfo<User> userPageInfo = new PageInfo<>(userList);
    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());
    sqlSession.close();
}

12. MyBatis的多表操作

①. 一对一查询模型

  • 根据指定订单号查询订单和用户信息
创建订单Order对象
  • package com.zhiyuan.mybatis.entity;
    
    import java.util.Date;
    
    public class Order {
        private int id;
        private String ordertime;
        private double total;
        private User user;
    
        public Order() {}
    
        public Order(int id, String ordertime, double total, User user) {
            this.id = id;
            this.ordertime = ordertime;
            this.total = total;
            this.user = user;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getOrdertime() {
            return ordertime;
        }
    
        public void setOrdertime(String ordertime) {
            this.ordertime = ordertime;
        }
    
        public double getTotal() {
            return total;
        }
    
        public void setTotal(double total) {
            this.total = total;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        @Override
        public String toString() {
            final StringBuffer sb = new StringBuffer("Order{");
            sb.append("id=").append(id);
            sb.append(", ordertime=").append(ordertime);
            sb.append(", total=").append(total);
            sb.append(", user=").append(user);
            sb.append('}');
            return sb.toString();
        }
    }
    
配置映射文件
  • <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zhiyuan.mybatis.dao.OrderDao">
        <resultMap id="orderMap" type="order">
            <id column="oid" property="id"/>
            <result column="ordertime" property="ordertime"/>
            <result column="total" property="total"/>
            <result column="uid" property="user.id"/>
            <result column="name" property="user.name"/>
            <result column="age" property="user.age"/>
            <result column="gender" property="user.gender"/>
        </resultMap>
        <select id="findAll" resultMap="orderMap">
            select o.id oid,u.id uid,ordertime,total,name,age,gender from orders o,user u where o.uid=u.id
        </select>
    </mapper>
    
优化配置文件
  • <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zhiyuan.mybatis.dao.OrderDao">
        <resultMap id="orderMap" type="order">
            <id column="oid" property="id"/>
            <result column="ordertime" property="ordertime"/>
            <result column="total" property="total"/>
            <association property="user" javaType="user">
                <id column="uid" property="id"/>
                <result column="name" property="name"/>
                <result column="age" property="age"/>
                <result column="gender" property="gender"/>
            </association>
        </resultMap>
        <select id="findAll" resultMap="orderMap">
            select o.id oid,u.id uid,ordertime,total,name,age,gender from orders o,user u where o.uid=u.id
        </select>
    </mapper>
    

②. 一对多查询模型

  • 根据用户id获取用户所有订单信息
创建用户User对象
package com.zhiyuan.mybatis.entity;

import java.util.List;
public class User {
    private int id;
    private String name;
    private int age;
    private String gender;

    private List<Order> orderList;

    public User() {}

    public User(int id, String name, int age, String gender) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

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

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

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

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

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("User{");
        sb.append("id=").append(id);
        sb.append(", name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append(", gender='").append(gender).append('\'');
        sb.append(", orderList=").append(orderList);
        sb.append('}');
        return sb.toString();
    }
}
配置UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhiyuan.mybatis.dao.UserDao">
    <sql id="selectUser">select * from user</sql>
    <resultMap id="userMap" type="user">
        <id column="uid" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="gender" property="gender"/>
        <collection property="orderList" ofType="order">
            <id column="oid" property="id"/>
            <result column="ordertime" property="ordertime"/>
            <result column="total" property="total"/>
        </collection>
    </resultMap>
    <select id="findAllOrder" resultMap="userMap">
        select o.id oid,u.id uid,ordertime,total,name,age,gender from orders o,user u where o.uid=u.id
    </select>
</mapper>
测试结果
@Test
public void test7() throws IOException {
    //读取核心配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
    //创建会话工厂
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    //创建会话对象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    List<Order> allOrder = userDao.findAllOrder();
    System.out.println(allOrder);
}

③. 多对多查询模型

  • 查询一个用户对应的多个角色

④. 小结

  • MyBatis多表配置方式
    • 一对一配置:使用 <resultMap> 做配置
    • 一对多配置:使用 <resultMap>+<collection> 做配置
    • 多对多配置:使用 <resultMap>+<collection> 做配置

13. MyBatis的注解开发

①. @Insert 实现插入

②. @Update 实现更新

③. @Delete 实现删除

④. @Select 实现查询

⑤. @Result 实现结果集封装

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

⑦. @One 实现一对一结果集封装

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

14. 导出资源失败问题

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

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

15. 数据库和实体类映射业内共识

  • 表名小写:User.java --- user EduUser --- edu_user
  • 《阿里Java开发规范》中的约定
    • 不得使用大写字母,不得连写两个单词;
    • 表必备三字段:id, gmt_create, gmt_modified;
      • 其中 id 必为主键,类型为 unsigned bigint、单表时自增、步长为 1;分表时改为从TDDL Sequence 取值,确保分表之间的全局唯一。gmt_create, gmt_modified 的类型均为date_time 类型;
    • 用尽量少的存储空间来存 数一个字段的数据;
      • 能用int的就不用char或者varchar;
      • 能用tinyint的就不用int;
      • 能用varchar(20)的就不用varchar(255);
      • 时间戳字段尽量用int型,如gmt_create:表示从 ‘1970-01-01 08:00:00’ 开始的int秒数,采用英文单词的过去式;gmtCreated:表示datetime类型的时间,即形如 '1980-01-01 00:00:00’的时间串,Java中对应的类型为Timestamp
    • 不得使用外键与级联,一切外键概念必须在应用层解决。
      • 说明:(概念解释)学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
    • 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

16. MyBatis的逆向创建POJO

<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.7</version>
</dependency>

几乎没人用了

五、SSM框架整合

1. 原始整合方式

①. 导入pom.xml依赖

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.5</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.21</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet.jsp.jstl</groupId>
      <artifactId>javax.servlet.jsp.jstl-api</artifactId>
      <version>1.2.2</version>
    </dependency>
    <dependency>
      <groupId>taglibs</groupId>
      <artifactId>standard</artifactId>
      <version>1.1.2</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
    <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.6</version>
    </dependency>
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.1</version>
    </dependency>
  </dependencies>

②. 创建实体类Account.java

③. 编写AccountMapper.java

public interface AccountMapper {
    public abstract void add(Account account);
    public abstract List<Account> findAll();
}

④. 编写AccountService.java

public interface AccountService {
    public abstract void add(Account account);
    public abstract List<Account> findAll();
}

⑤. 编写AccountServiceImpl.java

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    private static AccountMapper accountMapper = MyUtils.getSqlSession().getMapper(AccountMapper.class);
    @Override
    public void add(Account account) {
        accountMapper.add(account);
    }

    @Override
    public List<Account> findAll() {
        return accountMapper.findAll();
    }
}

⑥. 编写工具类MyUtils.java

public class MyUtils {
    private static SqlSession sqlSession;
    private MyUtils(){}
    static{
        InputStream resourceAsStream = null;
        try {
            resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            sqlSession = new SqlSessionFactoryBuilder().build(resourceAsStream).openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        return sqlSession;
    }
}

⑦. 编写AccountController.java

@RestController
@RequestMapping("/account")
public class AccountController {
    @Autowired
    private AccountService accountService;
    @PostMapping(value = "/add",produces = "text/html;charset=utf-8")
    @ResponseBody
    public String add(Account account){
        accountService.add(account);
        return "保存成功";
    }

    @RequestMapping("/findAll")
    public ModelAndView findAll(){
        List<Account> accountList = accountService.findAll();
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("accountList",accountList);
        modelAndView.setViewName("accountlist");
        return modelAndView;
    }
}

⑧. 配置Spring+SpringMVC+MyBatis

Spring:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns: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="com.zhiyuan.ssm">
        <!--取消对Controller注解的扫描,交由SpringMVC来管理-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>
SpringMVC:spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       ">
    <!--配置组件扫描,主要扫描Controller-->
    <context:component-scan base-package="com.zhiyuan.ssm.controller"/>
    <!--配置mvc注解驱动-->
    <mvc:annotation-driven/>
    <!--交由tomcat寻找静态资源-->
    <mvc:default-servlet-handler/>
    <!--配置内部资源视图解析器-->
    <bean id="resourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>
MyBatis:AccountMapper.xml + SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhiyuan.ssm.mapper.AccountMapper">
    <insert id="add" parameterType="account">
        insert into account (name,balance) values (#{name},#{balance})
    </insert>
    <select id="findAll" resultType="account">
        select * from account
    </select>
</mapper>
<?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="com.zhiyuan.ssm.entity.Account" alias="account"/>-->
        <package name="com.zhiyuan.ssm.entity"/>
    </typeAliases>
    <!--配置环境-->
    <environments default="developement">
        <environment id="developement">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverClassName}"/>
                <property name="url" value="${jdbcUrl}"/>
                <property name="username" value="${dbusername}"/>
                <property name="password" value="${dbpassword}"/>
            </dataSource>
        </environment>
    </environments>
    <!--加载映射-->
    <mappers>
        <mapper resource="com\zhiyuan\ssm\mapper\AccountMapper.xml"/>
    </mappers>
</configuration>

2. 优化整合方式

①. 导入pom.xml依赖

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>5.2.1.RELEASE</version>
</dependency>
<dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
</dependency>
<dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.5</version>
</dependency>

②. 将SqlSessionFactory配置到Spring容器中

<!--加载配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据库连接池信息-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driverClassName}"/>
    <property name="jdbcUrl" value="${jdbcUrl}"/>
    <property name="user" value="${dbusername}"/>
    <property name="password" value="${dbpassword}"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!--引入其他配置信息-->
    <property name="configLocation" value="classpath:SqlMapConfig-Spring.xml"/>
</bean>
<!--扫描所有mapper创建对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.zhiyuan.ssm.mapper"/>
</bean>
<?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="com.zhiyuan.ssm.entity.Account" alias="account"/>-->
        <package name="com.zhiyuan.ssm.entity"/>
    </typeAliases>
    <!--加载映射-->
    <mappers>
        <mapper resource="com\zhiyuan\ssm\mapper\AccountMapper.xml"/>
    </mappers>
</configuration>

③. 书写AccountController.java

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;
    @Override
    public void add(Account account) {
        accountMapper.add(account);
    }

    @Override
    public List<Account> findAll() {
        return accountMapper.findAll();
    }
}

④. 声明式事务控制

<!--配置平台事务管理对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务增强-->
<tx:advice id="txAdvice">
    <tx:attributes>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--事务的织入-->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.zhiyuan.ssm.service.impl.*.*(..))"/>
</aop:config>

六、分布式框架

软件架构的发展过程:单体架构 --- 垂直架构 --- SOA架构 --- 微服务架构

单体架构

  • 特点:全部功能几种在一个项目内(All in one)
  • 优点:架构简单,前期开发成本低,开发周期短,适合小型项目
  • 缺点:
    • 全部功能集成在一个工程中,对于代行项目不宜开发、扩展和维护
    • 技术栈受限,只能用一种语言开发
    • 系统性能扩展只能通过扩展集群节点,成本高

垂直架构

  • 特点:按照业务进行分割,形成小的单体项目
  • 优点:技术栈可扩展
  • 缺点:
    • 功能集中在一个项目中,不利于开发、扩展和维护
    • 系统扩张只能通过集群方式
    • 项目之间功能冗杂、数据冗余、耦合性强

SOA架构

  • 特点:将重复功能或模块抽取成组件的形式,对外提供服务,在项目与服务之间使用ESB(企业服务总线)的形式作为通信的桥梁
  • 优点:
    • 重复功能或模块抽取为服务,提高开发效率
    • 可重用性高,可维护性高
  • 缺点:
    • 各系统之间有义务不同,很难确认功能或模块是重复的
    • 抽取服务的粒度大
    • 系统和服务之间耦合度高

微服务架构

  • 特点:将系统服务层完全独立出来,抽取为一个个的微服务,抽取的粒度更细,遵循单一原则,采用轻量级框架协议传输
  • 优点:
    • 服务拆分粒度更细,有利于提高开发效率
    • 可以针对不同服务指定对应的优化方案
    • 适用于互联网时代,产品迭代周期更短
  • 缺点:
    • 粒度太大导致服务太多,维护成本高
    • 分布式系统开发的技术成本高,对团队的挑战大

1. Apache Dubbo

Apache Dubbo是一款高性能的Java RPC框架,其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。

RPC:remote procedure call(远程过程调用)

官网:https://dubbo.apache.org/zh/

Apache Dubbo提供了三大核心能力:

  • 面向接口的远程方法调用
  • 智能容错和负载均衡
  • 服务自动注册和发现

七、Vue学习

1. 简介

Vue 读音与View类似,是一套用于构建用户界面的 渐进式框架 ,与其他框架不同的是,Vue被设计为可以自定向上逐层应用,Vue的和辛苦只关注视图层,不仅易上手,还便于与第三方库或既有项目整合,另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue也完全能够为复杂的单页应用提供驱动

  • 渐进式:可以选择性的使用该框架中的一个或一些组件,这些组件的使用也不需要将框架全部组件都应用,而且用了这些组件也不要求你的系统全部都使用该框架
  • 前端主流框架
    • Vue.js
    • React.js
    • AngularJS
  • 官网:https://cn.vuejs.org/

2. 搭建示例工程

  • Vue的使用方式
    • 可以引用在线的Vue.js
    • 可以离线下载Vue.js
    • npm包资源管理器,可以下载Vue.js
  • 步骤
    • npm init -y:初始化
    • npm install vue --save :局部安装vue模块

3. 演示双向绑定与事件处理

①. 插值引用 {{key}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第一课</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <h2>{{name}}学Vue</h2>
    </div>
    <script type="text/javascript">
        var app = new Vue({
           el:"#app",
           data:{
               name:"絷缘"
           }
        });
    </script>
</body>
</html>

②. 双向绑定 v-model

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第一课</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <input type="text" v-model="day"/>
        <h2>{{name}}学Vue,学了{{day}}天</h2>
    </div>
    <script type="text/javascript">
        var app = new Vue({
           el:"#app",
           data:{
               name:"絷缘",
               day:20
           }
        });
    </script>
</body>
</html>

③. 事件处理 v-on:事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>第一课</title>
    <script src="node_modules/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <input type="text" v-model="day"/>
        <button v-on:click="day++">点我加一</button>
        <h2>{{name}}学Vue,学了{{day}}天</h2>
    </div>
    <script type="text/javascript">
        var app = new Vue({
           el:"#app",
           data:{
               name:"絷缘",
               day:20
           }
        });
    </script>
</body>
</html>

4. Vue的生命周期以及钩子函数

①. 生命周期

  • 创建实例:每个Vue应用都是通过Vue函数创建一个新的Vue实例开始的
var app = new Vue({
	//在构造函数中传入一个对象,并在对象中声明各种Vue所需数据和方法	
});
  • 装载模板:每个Vue实例都需要关联一段html,Vue会基于当前模板进行视图渲染,关联模板通过 el 实现
var app = new Vue({
    //这样Vue就与id为app的元素关联上了,Vue就可以基于id为app的元素进行渲染数据了
   el:"#app" 
});
  • 渲染模板:当Vue实例被创建时,它会尝试获取在data中定义的所有属性,用于视图的数据渲染,并且监视data中属性的变化,当data发生变化,所有与之绑定的视图都将重新渲染,这就是响应式系统

②. 钩子函数

  • Vue为生命周期中的每个状态都设置了钩子函数(监听函数),每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用
Vue 实例生命周期
  • beforeCreate():Vue实例创建之前执行代码
  • created():Vue实例被创建后执行代码,常用于初始化数据
  • beforeMount
  • mounted
  • updated
  • beforeUpdate
  • destroyed
  • beforeDestroy

钩子函数不要使用箭头函数的方式编写

因为箭头函数并没有 thisthis 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致

Uncaught TypeError: Cannot read property of undefined

Uncaught TypeError: this.myMethod is not a function 之类的错误。

5. 插值 {{}}

  • 指令:是指带有v- 前缀的特殊属性,例如 v-model

  • 插值表达式:{{key}}

  • 用于替换插值表达式:v-text v-html

    • v-text:将数据输出到元素内部,如果输出的数据有HTML代码,作为普通文本输出
    • v-html:将数据输出到元素的内部,如果输出的数据有HTML代码,会被渲染到页面上

6. 双向绑定 v-model

  • 因为是双向绑定,意味着数据的改变会影响到视图的渲染,而视图的改变同样会影响到数据的改变,这就要求视图中的元素具备可修改数据的功能,所以可以双向绑定的元素是固定有限的
    • input
    • select
    • textarea
    • checkbox
    • radio
    • components

7. 事件绑定 v-on

  • 格式:v-on:click="num++"

  • 简写为:@click="num++"

  • 实例代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>第一课</title>
        <script src="node_modules/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <input type="text" v-model="day"/>
            <button v-on:click="day++">点我加一</button>
            <h2>{{name}}学Vue,学了{{day}}天</h2>
            <button @click="decrement">点我减一{{num}}</button>
        </div>
        <script type="text/javascript">
            var app = new Vue({
               el:"#app",
               data:{
                   name:"絷缘",
                   day:20,
                   num:0
               },
               created() {
                   this.num = 100;
               },
               methods:{
                   decrement(){
                       this.num--;
                   }
               }
            });
        </script>
    </body>
    </html>
    
  • 事件修饰符

    • .stop:阻止事件冒泡
    • .prevent:阻止默认事件发生
    • .capture:使用事件捕获模式
    • .self:只有元素自身触发事件才执行(冒泡或捕获的都不执行)
    • .once:只执行一次
  • 事件冒泡测试

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>事件冒泡测试</title>
        <script src="./node_modules/vue/dist/vue.js"></script>
    </head>
    <body>
        <div id="app">
            <div style="width: 100px;height: 100px;background-color: #666666;" @click="print('div被点击')">
                <button @click.stop="print('按钮被点击')">事件冒泡</button>
                <a href="https://www.baidu.com" target="_blank" @click.prevent="print('a被点击')">前往百度</a>
            </div>
        </div>
        <script type="text/javascript">
            var app = new Vue({
               el:"#app",
               data:{
                 name:"絷缘"
               },
               methods:{
                   print(str){
                       console.log(str);
                   }
               }
            });
        </script>
    </body>
    </html>
    

    8. 数组、对象遍历

    • 指令:v-for

    • 实例代码:

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>集合数据遍历渲染</title>
          <script src="./node_modules/vue/dist/vue.js"></script>
      </head>
      <body>
          <div id="app">
              <table width="500" cellspacing="0" cellpadding="0" border="1">
                  <thead>
                  <tr>
                      <th>序号</th>
                      <th>userId</th>
                      <th>userName</th>
                      <th>userAge</th>
                      <th>userGender</th>
                  </tr>
                  </thead>
                  <tbody>
                  <tr v-for="(user,index) in users">
                      <th>{{index}}</th>
                      <th>{{user.userId}}</th>
                      <th>{{user.userName}}</th>
                      <th>{{user.userAge}}</th>
                      <th>{{user.userGender}}</th>
                  </tr>
                  </tbody>
              </table>
          </div>
          <script type="text/javascript">
              var app = new Vue({
                  el:"#app",
                  data:{
                      users:[
                          {"userId":1,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":2,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":3,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":4,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":5,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":6,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":7,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":8,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":9,"userName":"絷缘","userAge":23,"userGender":"male"},
                          {"userId":10,"userName":"絷缘","userAge":23,"userGender":"male"},
                      ]
                  }
              });
          </script>
      </body>
      </html>
      
      image-20210313121405486
  • 为消除数据被增删时对视图渲染产生的影响,我们需要使用一个特殊的语法 :key="",通过key与集合元素中单个元素的唯一属性进行绑定,保证数据的唯一性,从 而使数据增删时,视图渲染不受影响

  • :key=""v-bind:key="" 的简写

8. 指令 v-if v-show

①. v-if v-else-if v-else

  • DOM动态渲染,对元素进行动态添加或删除

②. v-show

  • 通过元素的css display:none 对元素做显示或隐藏

9. 指令 v-bind

八、SpringBoot

1. 什么是SpringBoot

①. 概述

  • SpringBoot称为搭建程序的脚手架,或者说是便捷搭建基于Spring工程的脚手架

②. 作用

  • 帮助开发人员快速地构建庞大的Spring项目,并且尽可能地减少一切xml文件的配置,做到开箱即用,让开发人员将注意力放在业务上而不是配置上

③. 解决的问题

  • 繁杂的配置文件
  • 混乱的依赖管理

④. 主要特点

  • 创建独立的Spring应用,为所有Spring开发者提供一个非常快速地、广泛接受的入门体验
  • 直接嵌入应用服务器,如tomcat、jetty、undertow等,不需要去部署war包
  • 提供固定的启动器依赖去简化组件配置,实现开箱即用
  • 自动地配置Spring和其他有需要的第三方依赖
  • 提供了一些代行项目中常见的非功能性特性,如内嵌服务器、安全、指标、健康检测、外部配置等
  • 绝对没有代码生成,也无需xml配置

2. SpringBoot配置文件

①. 配置文件类型

  • properties
  • yml / yaml

②. yaml:以数据为核心的配置文件

  • 基本语法

    • 大小写敏感
    • 数据值前必须由空格分隔
    • 使用空格缩进表示层级关系,相同缩进表示同一级
  • 数据格式

    • 对象

      • server:
        	port: 8080
        
      • server: {port: 8080}
        
    • 数组

      • language:
        	- zh-cn
        	- en
        	- jp
        
      • language: [zh-cn,en,jp]
        
    • 纯量

      • username: 'zhiyuan\n' #单引号内原样输出
        username: "zhiyuan\n" #双引号内可以识别转义字符
        
  • 参数引用

    • 通过$符号可以引用已经存在于yaml配置文件中的纯量参数

      url: "jdbc:mysql://localhost:3306/test?serverTimeZone=HongKong"
      
      jdbc:
      	url: ${url}
      

③. SpringBoot内部配置加载顺序

  • file:./config/:从当前项目路径中的config目录中加载配置文件
  • file:./:从当前项目的根目录中加载配置文件
  • classpath:/config/:classpath的config目录中加载配置文件
  • classpath:/:classpath的根目录中加载配置文件

3. SpringBoot入门

①. 步骤

  • 创建maven项目

  • 添加SpringBoot父级依赖

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.4.2</version>
    </parent>
    
  • 添加SpringBoot启动器依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
  • 设置JDK版本信息

    <properties>
        <java.version>1.8</java.version>
    </properties>
    
  • 编写启动引导类

    /**
     * Spring Boot工程都有一个启动引导类,这是工程的入口,并在引导类上添加 @SpringBootApplication 注解
     */
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class,args);
        }
    }
    
  • 编写简单Controller

    @RestController
    public class TestController {
    
        @GetMapping("/hello")
        public String hello(){
            return "Hello!SpringBoot!";
        }
    }
    
  • 成功访问

    image-20210313144218537

②. Java代码方式配置

  • Java配置主要靠Java类和一些注解,比较常用的注解有

    • @Configuration:声明一个类作为配置类,代替xml文件
    • @Bean:声明在方法上,将方法返回值加入Bean容器,代替bean标签
    • @Value:属性注入
    • @PropertySource:指定外部属性文件
  • 实现配置数据库连接池

    • 导入依赖
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.4</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.21</version>
    </dependency>
    
    • 创建配置文件
    jdbc.driverClassName=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/springbootdb?serverTimezone=Asia/Shanghai
    jdbc.username=root
    jdbc.password=123456
    
    • 创建配置类
    @Configuration
    @PropertySource("classpath:jdbc.properties")
    public class JdbcConfig {
        @Value("${jdbc.driverClassName}")
        String driverClassName;
        @Value("${jdbc.url}")
        String url;
        @Value("${jdbc.username}")
        String username;
        @Value("${jdbc.password}")
        String password;
    
        @Bean
        public DataSource getDataSource(){
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(this.driverClassName);
            druidDataSource.setUrl(this.url);
            druidDataSource.setUsername(this.username);
            druidDataSource.setPassword(this.password);
            return druidDataSource;
        }
    }
    
    • 根据类型注入属性
    @RestController
    public class TestController {
    
        @Autowired
        private DataSource ds;
    
        @GetMapping("/hello")
        public String hello(){
            System.out.println(ds);
            return "Hello!SpringBoot!";
        }
    }
    
    • 控制台打印
    {
    	CreateTime:"2021-03-13 15:02:01",
    	ActiveCount:0,
    	PoolingCount:0,
    	CreateCount:0,
    	DestroyCount:0,
    	CloseCount:0,
    	ConnectCount:0,
    	Connections:[
    	]
    }
    
    

③. SpringBoot属性注入方式

  • @ConfigurationProperties

    • 该注解可以将SpringBoot中的配置文件(application.properties、application.yml)中的配置项读取到一个对象中
  • 步骤

    • 创建名为application的properties文件
    jdbc.driverClassName=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/springbootdb?serverTimezone=Asia/Shanghai
    jdbc.username=root
    jdbc.password=123456
    
    • 创建JdbcProperties配置类
    @ConfigurationProperties(prefix = "jdbc")
    public class JdbcProperties {
        private String driverClassName;
        private String url;
        private String username;
        private String password;
    
        public String getDriverClassName() {
            return driverClassName;
        }
    
        public void setDriverClassName(String driverClassName) {
            this.driverClassName = driverClassName;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    }
    
    • 导入依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    
    • 创建JdbcConfig配置类
    @Configuration
    @EnableConfigurationProperties(JdbcProperties.class)
    public class JdbcConfig {
        @Bean
        public DataSource getDataSource(JdbcProperties jdbcProperties){
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(jdbcProperties.getDriverClassName());
            druidDataSource.setUrl(jdbcProperties.getUrl());
            druidDataSource.setUsername(jdbcProperties.getUsername());
            druidDataSource.setPassword(jdbcProperties.getPassword());
            return druidDataSource;
        }
    }
    
  • 优化

    • 移除 JdbcProperties 配置类
    • 直接在 JdbcConfig 的方法上加注解
    @Configuration
    public class JdbcConfig {
    
        @Bean
        @ConfigurationProperties(prefix = "jdbc")
        public DataSource getDataSource(){
            return new DruidDataSource();
        }
    }
    

④. 多个yml文件配置(多个properties文件配置)

  • 单个yaml文件中的多个yaml配置

    • 单个yaml文件可以使用 --- 对多个配置进行分类

      • ---
        # 开发环境配置信息
        server:
        	port: 8080
        spring:
        	profiles: dev
        ---
        # 测试环境配置信息
        server:
        	port: 8080
        spring:
        	profiles: test
        ---
        # 生产环境配置信息
        server:
        	port: 8080
        spring:
        	profiles: pro
        
    • # 激活对应配置信息
      spring:
      	profiles:
      		actice: pro
      
  • 多个yml文件的配置 / 多个properties文件配置

    • SpringBoot中允许多个 yml / properties 配置文件,这些配置文件的名称必须为 application-***.yml application-***.properties并且这些配置文件必须要在 application.yml application.properties中激活之后才可以使用

    • 如果properties和yml配置文件同时存在SpringBoot项目中,那么这两种文件都有效,在两个配置文件中如果存在同名的配置项,以properties文件为主

    • 激活其他配置文件的方式

      • # yaml激活写法
        spring:
          profiles:
            active: dev
        
      • # properties文件激活写法
        spring.profiles.active=pro
        
  • 以上都是使用profile的配置方式激活对应配置文件,profile的激活方式共有以下几种:

    • 配置文件
    • 虚拟机参数 -Dspring.profile.active=pro
    • 命令行参数 --spring.profile.active=pro
      • java -jar 项目打包后的文件名 --spring.profile.active=pro

⑤. lombok应用

lombok是一个插件工具类包,提供了一些注解 @Data @Getter @Setter 等这些注解去简化实体类中的构造方法、get/set等方法的编写

  • 步骤
  1. 在IDEA中安装lombok插件
  2. 添加lombok对应的依赖
  3. 使用lombok的注解改造实体类
  • 注解

    • @Data:自动提供getter、setter、hashCode、equals、toString方法
    • @Getter:自动提供getter方法
    • @Setter:自动提供setter方法
    • @Slf4j:自动在bean中提供log变量
  • 实现

    • 导入依赖
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
        <scope>provided</scope>
    </dependency>
    
    • 创建实体类
    @Data
    @Slf4j
    public class User {
        private int id;
        private String username;
        private String password;
        private Date jointime;
    }
    

⑥. 修改SpringMVC端口和静态资源

  • 修改Tomcat端口

    server:
      port: 80
      servlet:
      	context-path: /hello
    
  • 访问项目中的静态资源

    • classpath:/META-INF/resources/
    • classpath:/resources/
    • classpath:/static/
    • classpath:/public/

⑦. SpringMVC拦截器配置

  • 日志记录级别
logging:
  level:
  com.dnc: debug
  org.springframework: info
  • 编写自定义拦截器
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("MyInterceptor的preHandle方法");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.debug("MyInterceptor的postHandle方法");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.debug("MyInterceptor的afterCompletion方法");
    }
}
  • 编写拦截器配置类,重写方法添加拦截器功能
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Bean
    public MyInterceptor myInterceptor(){
        return new MyInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //添加拦截器,拦截所有请求
        registry.addInterceptor(myInterceptor()).addPathPatterns("/*");
    }
}

⑧. 事务和连接池

  • 事务配置

    • 添加事务相关依赖,mysql相关依赖
    • 编写业务UserService使用事务注解 @Transactional
  • 数据库连接池 hikari 配置

  • SpringBoot集成的默认数据库连接池,只需要我们配置就好了,据说效率比druid高

  • 步骤

    • <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      
    • 数据库连接池默认使用hikari,在配置文件中配置如下就可以了

      spring:
        datasource:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/springbootdb?serverTimezone=Asia/Shanghai
          username: root
          password: 123456
      

⑨. SpringBoot整合MyBatis

  • 添加启动器依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.3</version>
    </dependency>
    
  • 配置MyBatis:实体类别名包,日志,映射文件等

    mybatis:
      #配置实体类别名包路径
      type-aliases-package: com.dnc.entity
      #配置映射文件路径
      mapper-locations: classpath:mapper/*.xml
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  • 配置MapperScan

    @SpringBootApplication
    @MapperScan("com.dnc.mapper")
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class,args);
        }
    }
    

⑩. SpringBoot通用Mapper

  • 添加启动器依赖

    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>2.1.5</version>
    </dependency>
    
  • 改造UserMapper继承 Mapper

  • 修改启动引导类Application中的Mapper扫描注解

  • 修改User实体类添加JPA注解

  • 改造UserService实现业务功能

此处注意一个问题,实体类中所有成员属性的类型必须为引用类型,基本类型会导致SQL无法执行

3. SpringBoot整合测试

http://localhost/user/1

{
  "id": 1,
  "username": "絷缘",
  "password": "123456",
  "jointime": "2021-03-13 23:31:23"
}

4. SpringBoot整合Junit

①. 添加启动器依赖 spring-boot-starter-test

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

②. 编写测试代码

@RunWith(SpringRunner.class)
@SpringBootTest(classes=Application.class)
public class UserServiceTest {

    @Autowired
    private UserService userService;
    @Test
    public void queryById() {
        User user = userService.queryById(1);
        System.out.println(user);
    }
}

//输出结果
User(id=1, username=絷缘, password=123456, jointime=Sat Mar 13 23:31:23 CST 2021)

5. SpringBoot整合Redis

①. 添加启动器依赖 spring-boot-starter-data-redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

②. 配置application.yml中修改redis的连接参数

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springbootdb?serverTimezone=Asia/Shanghai
    username: root
    password: 123456
  redis:
    host: localhost
    port: 6379

③. 编写测试类引用RedisTemplate操作redis中的五种数据类型

  • string
  • hash
  • list
  • set
  • sorted set
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTest {

    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public void test(){
        //操作string类型
        redisTemplate.opsForValue().set("username","絷缘");
        redisTemplate.boundValueOps("name").set("絷缘");
        System.out.println("username=" + redisTemplate.opsForValue().get("username"));
        System.out.println("name=" + redisTemplate.opsForValue().get("name"));
        //操作hash散列
        redisTemplate.boundHashOps("user").put("username","zhiyuan");
        redisTemplate.boundHashOps("user").put("nickname","絷缘");
        redisTemplate.boundHashOps("user").put("password","123456");
        System.out.println("username=" + redisTemplate.opsForHash().get("user","username"));
        System.out.println("nickname=" + redisTemplate.opsForHash().get("user","nickname"));
        System.out.println("password=" + redisTemplate.opsForHash().get("user","password"));
        Set userkeys = redisTemplate.boundHashOps("user").keys();
        System.out.println("userkeys=" + userkeys);
        List uservalues = redisTemplate.boundHashOps("user").values();
        System.out.println("uservalues=" + uservalues);
        //操作list集合
        redisTemplate.boundListOps("person").leftPush("personA");
        redisTemplate.boundListOps("person").leftPush("personB");
        redisTemplate.boundListOps("person").leftPush("personC");
        List person = redisTemplate.boundListOps("person").range(0, -1);
        System.out.println("person=" + person);
        //操作set集合
        redisTemplate.boundSetOps("animals").add("dog","cat","pig","horse");
        Set animals = redisTemplate.boundSetOps("animals").members();
        System.out.println("animals=" + animals);
        //操作sortedset集合
        redisTemplate.boundZSetOps("student").add("studentA",22);
        redisTemplate.boundZSetOps("student").add("studentB",25);
        redisTemplate.boundZSetOps("student").add("studentC",21);
        redisTemplate.boundZSetOps("student").add("studentD",26);
        Set student = redisTemplate.boundZSetOps("student").range(0, -1);
        System.out.println("student=" + student);
    }
}
//输出结果
username=絷缘
name=絷缘
username=zhiyuan
nickname=絷缘
password=123456
userkeys=[username, nickname, password]
uservalues=[zhiyuan, 絷缘, 123456]
person=[personC, personB, personA, personC, personB, personA, personC, personB, personA]
animals=[dog, pig, cat, horse]
student=[studentC, studentA, studentB, studentD]

6. SpringBoot项目部署

目标:将SpringBoot项目使用maven指令打成jar包并运行测试

  1. 将项目中的资源、配置、依赖包打到一个jar包中:使用 mavenpackage 命令
  2. 部署jar包:java -jar 包名
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>com.xzy.youpin.Application</mainClass>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
  1. 安装插件 JBLSpringBootAppGen,可以自动创建 Application.javaapplication.yml

九、thymeleaf

十、 SpringSecurity

RBAC(Role-Based-Access-Control):基于角色的权限管理

认证 + 授权

1. 步骤

①. HttpBasic模式的认证

②. FormLogin模式的认证

  • 编写login.html文件
  • 创建配置类继承 WebSecurityConfigurerAdapter
  • 重写 configure(HttpSecurity http) 配置登录验证逻辑
  • 重写 configure(AuthenticationManagerBuilder auth) 实现内存身份验证
  • 运行验证

③. 前后端分离架构的认证

十一、MyBatis-Plus

1. 导包

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>mybatis-plus-latest-version</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

2. 配置log4j

log4j.rootLogger=DEBUG,A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.Layout.ConversionPattern=[%t] [%c]-[%p] %m%n

3. SpringBoot整合Mybatis-Plus

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>Latest Version</version>
</dependency>

queryWrapper.lt()——小于
queryWrapper.le()——小于等于
queryWrapper.gt()——大于
queryWrapper.ge()——大于等于
queryWrapper.eq()——等于
queryWrapper.ne()——不等于
queryWrapper.betweeen(“age”,10,20)——age在值10到20之间
queryWrapper.notBetweeen(“age”,10,20)——age不在值10到20之间
queryWrapper.like(“属性”,“值”)——模糊查询匹配值‘%值%’
queryWrapper.notLike(“属性”,“值”)——模糊查询不匹配值‘%值%’
queryWrapper.likeLeft(“属性”,“值”)——模糊查询匹配最后一位值‘%值’
queryWrapper.likeRight(“属性”,“值”)——模糊查询匹配第一位值‘值%’
queryWrapper.isNull()——值为空或null
queryWrapper.isNotNull()——值不为空或null
queryWrapper.in(“属性”,条件,条件 )——符合多个条件的值
queryWrapper.notIn(“属性”,条件,条件 )——不符合多个条件的值
queryWrapper.or()——或者
queryWrapper.and()——和
queryWrapper.orderByAsc(“属性”)——根据属性升序排序
queryWrapper.orderByDesc(“属性”)——根据属性降序排序
queryWrapper.inSql(“sql语句”)——符合sql语句的值
queryWrapper.notSql(“sql语句”)——不符合SQL语句的值
queryWrapper.esists(“SQL语句”)——查询符合SQL语句的值
queryWrapper.notEsists(“SQL语句”)——查询不符合SQL语句的值

原文作者:絷缘
作者邮箱:zhiyuanworkemail@163.com
原文地址:https://zhiyuandnc.github.io/pILxU31oU/
版权声明:本文为博主原创文章,转载请注明原文链接作者信息