ssm


Spring

Spring是什么

Spring是分层的Java SE/EE应用full-stack(全栈,指web和服务端都有模板)轻量级开源框架,以IoC(Inverse Of Control:反转控制)和AOP(Aspect Oriented Programming:面向切面编程)为内核


AOP:面向切面编程,切面是将共有的业务抽取出来,然后形成服务


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

杂谈

将sql语句输出到控制台

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

image-20220309094726887

Spring快速入门

1.1Spring程序开发步骤

image-20220309095227685

  • 导入Spring开发的基本包坐标
  • 编写Dao接口和实现类
  • 创建Spring核心配置文件
  • 在Spring配置文件中配置UserDaoImpl
  • 使用Spring的API获得Bean案例

Spring配置文件

1.1Bean标签基本配置

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

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

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

1.2Bean标签范围配置

scope:指对象的作用范围,取值如下:

singleton:默认值,单例的

prototype:多例的

request:WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中

session:WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中

global session:WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession相当于session


1.当scope的取值为singleton时

Bean的实例化个数:1个

Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例

Bean的生命周期:

  • 对象创建:当应用加载,创建容器时,对象就被创建了
  • 对象运行:只要容器在,对象就一直活着
  • 对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2.当scope的取值为prototype时

Bean的实例化个数:多个

Bean的实例化时机:当调用getBean()方法时实例化Bean

  • 对象创建:当使用对象时,创建新的对象实例
  • 对象运行:只要对象在使用中,就一直活着
  • 对象销毁:当对象长时间不使用时,被Java的垃圾回收器回收了

1.3Bean声明周期配置

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

1.4Bean实例化三种方式

  • 无参构造方法实例化
  • 工厂静态方法实例化
  • 工厂实例方法实例化

1.5Bean的依赖注入分析

目前UserService实例和UserDao实例都存在于Spring容器中,当前的做法是在容器外部获得UserService实例和UserDao实例,然后在程序中进行结合

image-20220314085206069


因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是UserService,所以可以在Spring容器中,将UserDao设置到UserService内部

1.6Bean的依赖注入概念

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

在编写程序时,通过控制反转,把对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IOC解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法

那这种业务层和持久层的依赖关系,在使用Spring之后,就让Spring来维护了。简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取


怎么将UserDao注入到UserService内部呢
  • 构造方法
  • set方法

1.7Bean的依赖注入方式

1.set方法注入

P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:首先,需要引入P命名空间:

1
xmlns:p="http://www.springframework.org/schema/p"

其次,需要修改注入方式

1
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:userDao-ref="userDao"></bean>
2.构造方法注入
1
2
3
4
5
6
7
//Java部分
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}

public UserServiceImpl() {
}
1
2
3
4
<!-- 配置文件部分 -->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>

1.8Bean的依赖注入的数据类型

上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入


注入数据的三种数据类型

  • 普通数据类型

    • ```xml
      <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
      
          <property name="username" value="zhangsan"></property>
          <property name="age" value="18"></property>
      </bean>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43

      * 引用数据类型

      * 集合数据类型

      * ```xml
      <bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl">
      <property name="strList">
      <list>
      <value>aa</value>
      <value>bb</value>
      <value>cc</value>
      </list>
      </property>
      <!-- Map集合-->
      <property name="userMap">
      <map>
      <entry key="u1" value-ref="user1"></entry>
      <entry key="u2" value-ref="user2"></entry>

      </map>
      </property>

      <!-- properties-->
      <property name="properties">
      <props>
      <prop key="p1">pp1</prop>
      <prop key="p2">pp2</prop>
      <prop key="p3">pp3</prop>

      </props>
      </property>
      </bean>
      <bean id="user1" class="com.itheima.domain.User">
      <property name="name" value="tom"></property>
      <property name="addr" value="beijing"></property>
      </bean>

      <bean id="user2" class="com.itheima.domain.User">
      <property name="name" value="lucy"></property>
      <property name="addr" value="tianjing"></property>
      </bean>

1.9引入其他配置文件(分模块开发)

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

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

Spring的重点配置

标签

  • id属性:在容器中Bean实例的唯一标识,不允许重复

  • class属性:要实例化的Bean的全限定名

  • scope属性:Bean的作用范围,常用是Singleton(默认)和prototype

  • 标签:属性注入

    • name属性:属性名称

    • value属性:注入的普通属性值

    • ref属性:注入的对象引用值

    • 标签

    • map标签

    • 标签

  • 标签

标签:导入其他的Spring分文件

Spring相关API

1.1ApplicationContext的继承体系

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

1.2ApplicationContext的实现类

  • ClassPathXmlApplicationContext:它是从类的根路径下加载配置文件,推荐使用这种
  • FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置
  • AnnotationConfigApplicationContext:当使用注解配置容器对象时,需要使用此类来创建Spring容器,它用来读取注解

1.3getBean()方法使用

1
2
3
4
5
6
7
8
9
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}

public<T> T getBean(Class<T> requiredType) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}

其中,当参数的数据类型是字符串是字符串时,表示根据Bean的id从容器中获取Bean实例,返回是Object,需要强转。当参数的数据类型是Class类型时,表示根据类型从容器中匹配Bean实例,当容器中相同类型的Bean有多个时,则此方法会报错

1.4知识要点

Spring的重点API

ApplicationContext app = new ClasspathXmlApplicationContext(“xml文件”);

app.getBean(“id”);

app.getBean(Class);

Spring配置数据源

1.1数据源(连接池)的作用

  • 数据源(连接池)是提高程序性而出现的
  • 事先实例化数据源,初始化部分连接资源
  • 使用连接资源时从数据源中获取
  • 使用完毕后将连接资源归还给数据源
常见的数据源(连接池)

DBCP、C3P0、BoneCP、Druid等

数据源的开发步骤
  • 1.导入数据源的坐标和数据库驱动坐标
  • 2.创建数据源对象
  • 3.设置数据源的基本连接数据
  • 4.使用数据源获取连接资源和归还连接资源

1.2数据源的手动创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package com.itheima.test;

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

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ResourceBundle;

public class DataSourceTest {
@Test
// 测试手动创建 c3p0 数据源
public void test1() throws PropertyVetoException, SQLException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUser("root");
dataSource.setPassword("zt20020806");
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}

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

@Test
// 测试手动创建 c3p0 数据源(加载properties配置文件)
public void test3() throws PropertyVetoException, SQLException {
// 1.读取配置文件
ResourceBundle rb = ResourceBundle.getBundle("jdbc");
String driver = rb.getString("jdbc.driver");
String url = rb.getString("jdbc.url");
String username = rb.getString("jdbc.username");
String password = rb.getString("jdbc.password");

// 2.创建数据源对象,设置连接参数
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
Connection connection = dataSource.getConnection();

System.out.println(connection);
connection.close();
}
}

1.3Spring配置数据源

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Spring配置文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="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"></property>
<property name="user" value="root"></property>
<property name="password" value="zt20020806"></property>

</bean>
</beans>
1
2
3
4
5
6
7
8
9
10
//Java测试方法
@Test
// 测试Spring容器产生数据源对象
public void test4() throws SQLException {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = app.getBean(DataSource.class);
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}

1.4抽取jdbc配置文件

applicationContext.xml加载jdbc.properties配置文件获得连接信息


首先,需要引入context命名空间和约束路径

  • 命名空间:xmlns:context=”http://www.springframework.org/schema/context"

  • 约束空间:

    1
    2
    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
2
3
4
5
6
7
8
9
10
<!--    加载外部properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${idbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>

</bean>
Spring容器加载properties文件
1
2
<context:property-placeholder location="xx.properties" />
<property name="" value="${key}" />

Spring注解开发

1.1Spring原始注解

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


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

image-20220316113325022

注意:

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别配置的类、字段和方法

1
2
<!--注解的组件扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
1
2
//    @Autowired //按照数据类型从Spring容器中进行匹配
// @Qualifier("userDao") //是按照id值(名称)从容器中进行匹配的 但是主要此处@Qualifier结合@Autowired一起使用

1.2Spring新注解

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


image-20220322204949889


以下是实例

核心类:SpringConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itheima.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.*;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

//标志该类是Spring的核心配置类
@Configuration
//<context:component-scan base-package="com.itheima"></context:component-scan>相当于组件扫描
@ComponentScan("com.itheima")
// <import resource="" />
@Import({DataSourceConfiguration.class})
public class SpringConfiguration {

}

引入的类:DataSourceConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.itheima.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

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

@Value("${jdbc.url}")
private String url;

@Value("${jdbc.username}")
private String username;

@Value("${jdbc.password}")
private String password;

@Bean("dataSource")//Spring会将当前方法的返回值以指定名称存储到Spring容器中
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}

Spring集成Junit

1.1原始Junit测试Spring的问题

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

1
2
ApplicationContext app = new ClassPathXmlApplicationContext("bean.xml");
IAccountService as = app.getBean("accountService",IACCountService.class);

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


1.2上述问题解决思路

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

1.3Spring集成Junit步骤

  • 1.导入spring集成Junit的坐标
  • 2.使用@Runwith注解替换原来的运行期
  • 3.使用@ContextConfiguration指定配置文件或配置类
  • 4.使用@AutoWire注入需要测试的对象
  • 5.创建测试方法进行测试

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.itheima.test;

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

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

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:applicationContext.xml")

//以下是全注解方式
@ContextConfiguration(classes = {SpringConfiguration.class})
public class SpringJunitTest {
@Autowired
private UserService userService;

@Autowired
private DataSource dataSource;

// 测试方法
@Test
public void test1() throws SQLException {
userService.save();
// 测试DataSource
System.out.println(dataSource.getConnection());
}


}

Spring与Web环境集成

环境搭建

注意:报500时可能是没添加坐标sping-core

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>

1.1ApplicationContext应用上下文获取方式

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


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

自定义ContextLoaderListener1

web.xml配置

1
2
3
4
5
6
7
8
9
10
11
12
<!--    配置监听器-->
<listener>
<listener-class>com.itheima.listener.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>UserServlet</servlet-name>
<servlet-class>com.itheima.web.UserServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserServlet</servlet-name>
<url-pattern>/userServlet</url-pattern>
</servlet-mapping>

applicationContext配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?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">


<!-- 加载外部properties配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>


<!-- druid数据库连接池-->
<bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>

<!-- 配置Dao-->
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>

<!-- 配置service-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>

<!-- 配置组件扫描-->
<context:component-scan base-package="com.itheima"></context:component-scan>
</beans>

UserDaoImpl就继承了一个save方法,输出用的

UserServiceImpl类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.itheima.service.impl;

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

public class UserServiceImpl implements UserService {
private UserDao userDao;

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

ContextLoaderListener类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.itheima.listener;

import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

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

public class ContextLoaderListener implements ServletContextListener{
// 上下文初始化方法
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();


// 读取web.xml中的全局参数
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");

ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
// 将Spring的应用上下文对象存储到ServletContext域中
servletContext.setAttribute("app",app);
System.out.println("Spring容器创建完毕");
}

// 上下文销毁方法
@Override
public void contextDestroyed(ServletContextEvent sce) {

}
}

UserServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.itheima.web;

import com.itheima.listener.WebApplicationContextUtils;
import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ServletContext servletContext = this.getServletContext();
ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
UserService userService = app.getBean(UserService.class);

userService.save();
}
}
自定义ContextLoaderListener2

除了下面的变动,其他与上面一致

UserServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.itheima.web;

import com.itheima.listener.WebApplicationContextUtils;
import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ServletContext servletContext = this.getServletContext();
// ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);

userService.save();
}
}

WebApplicationUtils类

1
2
3
4
5
6
7
8
9
10
11
package com.itheima.listener;

import org.springframework.context.ApplicationContext;

import javax.servlet.ServletContext;

public class WebApplicationContextUtils {
public static ApplicationContext getWebApplicationContext(ServletContext servletContext) {
return (ApplicationContext) servletContext.getAttribute("app");
}
}

web.xml新增配置

1
2
3
4
5
<!--    全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>

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

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


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

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

web.xml

1
2
3
4
5
6
7
8
9
10
<!--    全局初始化参数-->
<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>

UserServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.itheima.web;

import com.itheima.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();

ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);

userService.save();
}
}

Spring的AOP简介

1.1什么是AOP

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

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

1.2AOP的作用及其优势

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

1.3AOP的底层实现

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

1.4AOP的动态代理技术

常用的动态代理技术

  • JDK代理:基于接口的动态代理技术(缺点,必须要有接口)
  • image-20220330095959339
  • cglib代理:基于父类的动态代理技术
  • image-20220330100039779

1.5JDK代理:

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.itheima.proxy.jdk;

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

public class ProxyTest {
public static void main(String[] args) {
// 创建目标对象
Target target = new Target();

// 增强对象
Advice advice = new Advice();

// 返回值 就是动态生成的代理对象
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),// 目标对象类加载器
target.getClass().getInterfaces(),// 目标对象相同的接口字节码对象数组
new InvocationHandler() {
// 调用代理对象的任何方法 实质执行的都是invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
advice.before();// 前置增强
Object invoke = method.invoke(target,args); //执行目标方法
advice.afterRunning();// 后置增强
return invoke;
}
}
);

// 调用代理对象方法
proxy.save();
}
}

Advice类:

1
2
3
4
5
6
7
8
9
10
11
12
package com.itheima.proxy.jdk;

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

public void afterRunning() {
System.out.println("后置增强");
}
}

TargetInterface类:

1
2
3
4
5
6
package com.itheima.proxy.jdk;

public interface TargetInterface {
public void save();
}

Target实现类:

1
2
3
4
5
6
7
8
9
package com.itheima.proxy.jdk;

public class Target implements TargetInterface{

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

1.6cglib的动态代理

Target类:

1
2
3
4
5
6
7
8
9
package com.itheima.proxy.jdk;

public class Target{

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

Advice类:

1
2
3
4
5
6
7
8
9
10
11
package com.itheima.proxy.jdk;

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

public void afterRunning() {
System.out.println("后置增强");
}
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.itheima.proxy.cglib;

import com.itheima.proxy.jdk.TargetInterface;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

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

public class ProxyTest {
public static void main(String[] args) {
// 创建目标对象
final Target target = new Target();

// 增强对象
final Advice advice = new Advice();

// 返回值 就是动态生成的代理对象 基于cglib

// 1.创建增强器
Enhancer enhancer = new Enhancer();
// 2.设置父类(目标)
enhancer.setSuperclass(Target.class);
// 3.设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
advice.before();// 执行前置
Object invoke = method.invoke(target,args);// 执行目标
advice.afterRunning();// 执行后置
return invoke;
}
});
// 4.创建代理对象
Target proxy = (Target) enhancer.create();
proxy.save();
}
}

1.7AOP相关概念

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


AOP常用的术语如下:

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

1.8AOP开发明确的事项

1.需要编写的内容

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

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

3.AOP底层使用哪种代理方式

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

1.9知识要点

  • AOP:面向切面编程
  • AOP底层实现:基于JDK动态代理和基于cglib的动态代理
  • AOP的重要概念:
    • Pointcut(切入点):被增强的方法
    • Advice(通知/增强):封装增强业务逻辑的方法
    • Aspect(切面):切点+通知
    • Weaving(织入):将切点与通知结合的过程
  • 开发的明确事项:
    • 谁是切点(切点表达式配置)
    • 谁是通知(切面类中的增强方法)
    • 将切点和通知进行织入配置

基于XML的AOP开发

1.1快速入门

  • 导入AOP相关坐标
    • 之前导入的spring-context中包括了aop坐标
    • 第三方,主张使用aspectjweaver
  • 创建目标接口和目标类(内部有切点)
  • 创建切面类(内部有增强方法)
  • 将目标类和切面类的对象创建权交给spring
  • 在applicationContext.xml中配置织入关系
  • 测试代码

1.2XML配置AOP详解

1.切点表达式的写法

表达式语法:

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

例如:

1
2
3
4
5
execution(public void com.itheima.aop.Target.method())
execution(void com.itheima.aop.Target.*(..))<!-- Target下面只要没有返回值的方法都被增强 -->
execution(* com.itheima.aop.*.*(..))<!-- aop包下的任意类的任意返回值方法被增强 -->
execution(* com.itheima.aop..*.*(..))<!-- aop包及其任意子包下的任意类的任意返回值方法被增强 -->
execution(* *..*.*(..))<!-- 任意包下任意返回值方法 -->
2.通知的类型

通知的配置语法:

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

image-20220331152944629

3.切点表达式的抽取

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

1
2
3
4
5
<!-- 声明切点表达式 -->
<aop:pointcut id="myPointcut" expression="execution(* com.itheima.aop.*.*(..))"></aop:pointcut>

<!-- 引用切点表达式 -->
<aop:around method="around" pointcut-ref="myPointcut"/>
知识要点
  • aop织入的配置

    • ```xml
      aop:config
      <aop:aspect ref="切面类">
          <aop:before method="通知方法名称" pointcut="切点表达式"></aop:before>
      </aop:aspect>
      
      1
      2
      3
      4
      5
      6
      7

      * 通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知

      * 切点表达式的写法:

      * ```xml
      execution([修饰符] 返回值类型 包名.类名.方法名(参数))

基于注解的AOP开发

1.1快速入门

基于注解的AOP开发步骤:

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

1.2注解配置AOP详情

1.注解通知的类型

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

image-20220401161329110

2.切点表达式的抽取

同xml配置aop一样,我们可以将切点表达式抽取。抽取方式是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后再在增强注解中进行引用。具体如下:

1
2
3
4
5
6
7
8
9
//    @After("pointcut()")
@After("MyAspect.pointcut()")
public void after() {
System.out.println("最终增强");
}

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

1.3知识要点

  • 注解aop开发步骤
    • 1.使用@Aspect标注切面类
    • 2.使用@通知注解标注通知方法
    • 3.在配置文件中配置aop自动代理<aop:aspectj-autoproxy/>

编程式事务控制相关对象

1.1PlatformTransactionManager

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

image-20220401162911509

1.2TransactionDefinition

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

image-20220401163445149

1.事务隔离级别

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

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

1.3TransactionStatus

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

image-20220402185513048

1.4知识要点

  • PlatformTransactionManager
  • TransactionDefinition
  • TransactionStatus

基于XML的声明式事务控制

1.1什么是声明式事务控制

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

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

1.2声明式事务控制的实现

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

  • 谁是切点?
  • 谁是通知?
  • 谁是切面?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--    配置事务平台管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 通知 事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 设置事务的属性信息-->
<tx:attributes>
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false"></tx:method>
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false" />
</tx:attributes>
</tx:advice>

<!-- 配置事务的aop织入-->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.itheima.service.impl.*.*(..))"></aop:advisor>
</aop:config>

1.3切点方法的事务参数的配置

1
2
3
4
5
6
<!--    通知 事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

其中,<tx:method>代表切点方法的事务参数的配置,例如:

1
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false" />
  • name:切点方法名称
  • isolation:事务隔离级别
  • propogation:事务的传播级别
  • timeout:超时时间
  • read-only:是否只读

1.4知识要点

  • 平台事务管理器
  • 事务通知的配置
  • 事务aop织入的配置

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

注解配置声明式事务控制解析
  • 1.使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,例如隔离级别、传播行为等
  • 2.注解使用在类上,那么该类下的所有方法都是用同一套注解参数配置
  • 3.使用在方法上,不同的方法可以采用不同的事务参数配置
  • 4.xml配置文件中要开启事务的注解驱动<tx:annotation-driven/>

注解声明式事务控制的配置要点

  • 平台事务管理器配置
  • 事务通知的配置(@Transactional注解配置)
  • 事务注解驱动的配置<tx:annotation-driven/>

SpringMVC

SpringMVC简介

1.1SpringMVC概述

SpringMVC是一种基于java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SringFrameWork的后续产品,已经融合在Spring Web Flow中


SpringMVC已经称为目前最主流的MVC框架之一,并且随着Spring3.0的发布,全面超越Struts2,成为最优秀的MVC框架。它通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求

1.2SpringMVC快速入门

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

开发步骤:
  • 1.导入SpringMVC相关坐标
  • 2.配置SpringMVC核心控制器DispatcherServlet
  • 3.创建Controller类和视图页面
  • 4.使用注解配置Controller类中业务方法的映射地址
  • 5.配置SpringMVC核心文件spring-mvc.xml
  • 6.客户端发起请求测试
代码实现:
  • 1.导入SpringMVC相关坐标

    • ```xml
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-webmvc</artifactId>
              <version>5.0.5.RELEASE</version>
          </dependency>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18

      * 2.配置SpringMVC核心控制器DispatcherServlet

      * ```xml
      <!-- 配置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>
  • 3.创建Controller类和视图页面

    • ```java
      package com.itheima.controller;import org.springframework.stereotype.Controller;
      import org.springframework.web.bind.annotation.RequestMapping;@Controller
      public class UserController {
      @RequestMapping("/quick")
      public String save() {
          System.out.println("Controller save running......");
          return "success.jsp";
      }
      
      }
      1
      2
      3
      4
      5

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

      * ```java
      @RequestMapping("/quick")
  • 5.配置SpringMVC核心文件spring-mvc.xml

    • ```xml <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.itheima.controller"></context:component-scan>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69

      #### 1.3SpringMVC流程图示

      ![image-20220404101543807](C:\Users\15135\AppData\Roaming\Typora\typora-user-images\image-20220404101543807.png)

      #### 1.4SpringMVC执行流程

      ![image-20220405105145375](C:\Users\15135\AppData\Roaming\Typora\typora-user-images\image-20220405105145375.png)

      * 1.用户发送请求到前端控制器DispatcherServlet
      * 2.DispatcherServlet收到请求调用HandlerMapping处理器映射器
      * 3.处理映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)并返回给DispatcherServlet
      * 4.DispatcherServlet调用HandlerAdapter处理器适配器
      * 5.HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
      * 6.Controller执行完成返回ModelAndView
      * 7.HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
      * 8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器
      * 9.ViewReslover解析后返回具体View

      #### 1.4SpringMVC注解解析

      ##### @RequestMapping

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

      位置:

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

      属性:

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

      例如:

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

      ##### MVC命名空间引入

      命名空间:
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc"

      约束地址:

      ​ http://www.springframework.org/schema/context

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

      ​ http://www.springframework.org/schema/mvc

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

      ##### 组件扫描

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

      ```xml
      <!-- Controller的组件扫描 -->
      <context:component-scan base-package="com.itheima">
      <!--只扫注解-->
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
      <!--只有注解不扫-->
      <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
      </context:component-scan>

1.5SpringXML的XML配置解析

1.视图解析器

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

1
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

翻看该解析器源码,可以看到该解析器的默认设置,如下:

1
2
3
4
REDIRECT_URL_PREFIX = "redirect:" // 重定向前缀
FORWARDD_URL_PREFIX = "forward:" // 转发前缀(默认值)
prefix = ""; // 视图名称前缀
suffix = ""; // 视图名称后缀

1.6知识要点

SpringMVC的相关组件
  • 前端控制器:DispatcherServlet
  • 处理器映射器:HandlerMapping
  • 处理器适配器:HandlerAdapter
  • 处理器:Handler
  • 视图解析器:View Resolver
  • 视图:View
SpringMVC的注解和配置
  • 请求映射注解:@RequestMapping
  • 视图解析器配置:
    • REDIRECT_URL_PREFIX = “redirect:” // 重定向前缀
      FORWARDD_URL_PREFIX = “forward:” // 转发前缀(默认值)
      prefix = “”; // 视图名称前缀
      suffix = “”; // 视图名称后缀

SpringMVC的数据响应

1.1SpringMVC的数据响应方式

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

1.2页面跳转

1.返回字符串形式

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

image-20220406105049803

返回带有前缀的字符串:

转发:forward:/WEV-INF/views/index.jsp

重定向:redirect:/index.jsp

2.返回ModelAndView对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.itheima.controller;

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

import javax.servlet.http.HttpServletRequest;

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

@RequestMapping("/quick5")
public String save5(HttpServletRequest request) {
request.setAttribute("username","我是胖头鱼");
return "success";
}


@RequestMapping("/quick4")
public String save4(Model model) {
model.addAttribute("username","我是username");
return "success";
}

@RequestMapping("/quick3")
public ModelAndView save3(ModelAndView modelAndView) {
modelAndView.addObject("username","张三");
modelAndView.setViewName("success");
return modelAndView;
}

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

// 返回字符串
// 请求地址 http://localhost:8080/user/quick
@RequestMapping("/quick")
public String save() {
System.out.println("Controller save running......");
return "success";
}
}

1.3回写数据

1.直接返回字符串

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

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

    • ```java
      @RequestMapping(“/quick4”)
      public void quickMethod4(HttpServletResponse response) throws IOException {
      response.getWriter().print("hello world");
      
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9

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

      * ```java
      @RequestMapping("/quick7")
      @ResponseBody
      public String quickMethod5() throws IOException {
      return "hello SpringMVC!!";
      }
2.直接返回json格式字符串
  • 1.导入json转换工具坐标

    • ```xml
          <dependency>
              <groupId>com.fasterxml.jackson.core</groupId>
              <artifactId>jackson-core</artifactId>
              <version>2.12.2</version>
          </dependency>
          <dependency>
              <groupId>com.fasterxml.jackson.core</groupId>
              <artifactId>jackson-databind</artifactId>
              <version>2.12.2</version>
          </dependency>
          <dependency>
              <groupId>com.fasterxml.jackson.core</groupId>
              <artifactId>jackson-annotations</artifactId>
              <version>2.12.2</version>
          </dependency>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16

      * 2.UserController类实现

      * ```java
      @RequestMapping("/quick8")
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public String save8() throws IOException {
      User user = new User();
      user.setUsername("lisa");
      user.setAge(30);
      // 使用json的转换工具将对象转换成json格式字符串再返回
      ObjectMapper objectMapper = new ObjectMapper();
      String json = objectMapper.writeValueAsString(user);

      return json;
      }
3.返回对象或集合

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

1
2
3
4
5
6
7
8
<!--    配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</list>
</property>
</bean>

UserController类

1
2
3
4
5
6
7
8
9
    @RequestMapping("/quick9")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
// 期待SpringMVC自动将User转换成json格式字符串
public User save9() throws IOException {
User user = new User();
user.setUsername("lisa");
user.setAge(30);
return user;
}

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

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

在SpringMVC的各个组件中,处理器映射、处理器适配器、视图解析器称为SpringMVC的三大组件。使用<mvc:annotation-driven/>自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),可用在Spring-xml.xml配置文件中使用<mvc:annotation-driven/>代替注解处理器和适配器的配置

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

1.4知识要点

SpringMVC的数据响应方式

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

SpringMVC请求数据

1.1获得请求参数

客户端请求参数的格式是:name=value&name=value…

服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:

  • 基本类型参数
  • POJO类型参数
  • 数组类型参数
  • 集合类型参数

1.2获得基本类型参数

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

1
http:localhost:8080/itheima_springmvc/quick9?username=zhangsan&age=12
1
2
3
4
5
6
@RequestMapping("/quick10")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public void save10(String username, int age) throws IOException {
System.out.println(username);
System.out.println(age);
}

1.3获得POJO类型参数

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

1
http:localhost:8080/itheima_springmvc/quick9?username=zhangsan&age=12
1
2
3
4
5
@RequestMapping("/quick11")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public void save11(User user) throws IOException {
System.out.println(user);
}

1.4获得数组类型参数

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

1
http://localhost:8080/user/quick12?strs=aaa&strs=bbb
1
2
3
4
5
@RequestMapping("/quick12")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public void save12(String[] strs) throws IOException {
System.out.println(Arrays.asList(strs));
}

1.5获得集合类型参数

方法1
  • 1.创建VO类

    • ```java
      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() {
          return "VO{" +
                  "userList=" + userList +
                  '}';
      }
      
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9

      * 2.创建测试方法

      * ```java
      @RequestMapping("/quick13")
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public void save13(VO vo) throws IOException {
      System.out.println(vo);
      }
  • 3.创建jsp页面

    • ```jsp
      <%@ page contentType=”text/html;charset=UTF-8” language=”java” %> Title
      <%-- 表明是第几个User对象的username age--%>



      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34

      ##### 方法2

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

      当使用ajax提交时,可以指定contentType为json形式,那么在方法参数位置使用@RequestBody可以直接接收集合数据而无需使用POJO进行包装

      ---

      * 1.ajax.jsp页面的创建(记得在webapp文件夹下面创建js文件夹引入jquery)

      * ```jsp
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
      <title>Title</title>
      <script src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
      <script>
      var userList = new Array();
      userList.push({username:"张三",age:12});
      userList.push({username:"李四",age:21});

      $.ajax({
      type: "POST",
      url: "${pageContext.request.contextPath}/user/quick14",
      data: JSON.stringify(userList),
      contentType: "application/json;charset=utf-8"
      });
      </script>
      </head>
      <body>

      </body>
      </html>
  • 2.创建测试类(记得加@RequestBody)

    • ```java
      @RequestMapping("/quick14")
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public void save14(@RequestBody List<User> userList) throws IOException {
          System.out.println(userList);
      }
      
      1
      2
      3
      4
      5

      如果读不到jquery文件记得在SpringMVC配置文件中加入一句:<mvc:resources mapping="/js/**" location="/js/" />开放资源的访问,

      mapping代表映射地址,location代表哪个目录下的资源对外开放

      mvc:default-servlet-handler/交给tomcat来找静态资源
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24

      如果还是读不到记得在生命周期中clean一下

      #### 1.6请求数据乱码问题

      当post请求时,数据会出现乱码,我们可以设置一个过滤器赖进行编码的过滤

      #### 1.7参数绑定注解@RequestParam

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

      ```jap
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
      <title>Title</title>
      </head>
      <body>
      <form action="${pageContext.request.contextPath}/user/quick15" method="post">
      <input type="text" name="name"><br />
      <input type="submit" value="提交">
      </form>
      </body>
      </html>
1
2
3
4
5
@RequestMapping("/quick15")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public void save15(@RequestParam(value = "name") String username) throws IOException {
System.out.println(username);
}

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

  • value:请求参数名称
  • required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
  • defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
1
2
3
4
5
@RequestMapping("/quick15")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public void save15(@RequestParam(value = "name", required = false, default = "itcast") String username) throws IOException {
System.out.println(username);
}

1.8获得Restful风格的参数

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


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

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

例如:

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

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

1.9自定义类型转换器

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

    • ```java
      import java.text.ParseException;
      import java.text.SimpleDateFormat;
      import java.util.Date;public class DataConverter implements Converter<String, Date> {
      @Override
      public Date convert(String s) {
      
      // 将日期字符串转化成真正的日期对象 返回
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
          Date date = null;
          try {
              date = sdf.parse(s);
          } catch (ParseException e) {
              e.printStackTrace();
          }
          return date;
      }
      
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12

      * 2.在配置文件中声明转换器

      * ```xml
      <!-- 声明转换器-->
      <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
      <property name="converters">
      <list>
      <bean class="com.itheima.converter.DataConverter"></bean>
      </list>
      </property>
      </bean>
  • 3.在<annotation-driven>中引用转换器

    • ```xml
      <mvc:annotation-driven conversion-service=”conversionService”/>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26

      #### 1.10获取Servlet相关API

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

      * HttpServletRequest
      * HttpServletResponse
      * HttpSession

      #### 1.11获得请求头

      ##### 1.@RequestHeader

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

      @Requestheader注解的属性如下:

      * value:请求头的名称
      * required:是否必须携带此请求头

      ```java
      @RequestMapping("/quick19")
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public void save19(@RequestHeader(value = "User-Agent",required = false) String user_agent) throws IOException {
      System.out.println(user_agent);
      }
2.@CookieValue

使用@CookieValue可以获得指定Cookie的值

@CookieValue注解的属性如下:

  • value:指定cookie的名称
  • required:是否必须携带此cookie
1
2
3
4
5
@RequestMapping("/quick20")
@ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
public void save20(@CookieValue(value = "JSESSIONID",required = false) String jsessionid) throws IOException {
System.out.println(jsessionid);
}

1.12文件上传

1.文件上传客户端三要素
  • 表单项type = “file”
  • 表单的提交方式是post
  • 表单的enctype属性是多部分表单形式,以及enctype = “multipart/form-data”
1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/user/quick21" method="post" enctype="multipart/form-data">
名称:<input type="text" name="username"><br />
文件:<input type="file" name="upload"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
2.文件上传原理
  • 当form表单修改为多部分表单时,request.getParameter()将失效
  • enctype = “application/x-www-form-urlencoded”时,form表单的正文内容格式是:key=value&key=value&key=value
  • 当form表单的enctype取值为Multipart/form-data时,请求正文内容就变成多部分形式
3.单文件上传步骤
  • 导入fileupload和io坐标

    • ```xml commons-fileupload commons-fileupload 1.3.1 commons-io commons-io 2.3
      1
      2
      3
      4
      5
      6
      7
      8
      9

      * 配置文件上传解析器

      * ```xml
      <!-- 配置文件上传解析器-->
      <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
      <property name="defaultEncoding" value="UTF-8"></property>
      <property name="maxInMemorySize" value="500000"></property>
      </bean>
  • 编写文件上传代码

    • ```java
      @RequestMapping(“/quick21”)
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public void save21(String username, MultipartFile upload) throws IOException {
      System.out.println(username);
      System.out.println(upload);
      
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
        
      * 保存到服务器

      * ```java
      @RequestMapping("/quick21")
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public void save21(String username, MultipartFile upload) throws IOException {
      System.out.println(username);
      // 获得上传文件名称
      String originalFilename = upload.getOriginalFilename();
      upload.transferTo(new File("D:\\upload\\" + originalFilename));
      }

1.13多文件上传

  • 页面代码

    • ```jsp
      名称:
      文件1:
      文件2:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      * 上传保存代码

      * ```java
      @RequestMapping("/quick22")
      @ResponseBody // 告知SpringMVC框架 不进行视图跳转 直接进行数据响应
      public void save22(String username, MultipartFile[] upload) throws IOException {
      System.out.println(username);
      // 获得上传文件名称
      for (MultipartFile multipartFile : upload) {
      String originalFilename = multipartFile.getOriginalFilename();
      multipartFile.transferTo(new File("D:\\upload\\" + originalFilename));
      }
      }

1.14知识要点

MVC实现数据请求方式
  • 基本类型参数
  • POJO类型参数
  • 数组类型参数
  • 集合类型参数
MVC获取数据细节
  • 中文乱码问题
  • @RequestParam和@pathVariable
  • 自定义类型转换器
  • 获得Servlet相关api
  • @RequestHeader和CookieValue
  • 文件上传

Spring JdbcTemplate基本使用

1.1SJdbcTemplate概述

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

1.2JdbcTemplate开发步骤

  • 导入spring-jdbc和spring-tx坐标
  • 创建数据库表和实体
  • 创建JdbcTemplate对象
  • 执行数据库操作

具体操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//    创建数据源对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/数据库");
dataSource.setUser("root");
dataSource.setPassword("root");

JdbcTemplate jdbcTemplate = new JdbcTemplate();
// 设置数据源对象 知道数据库在哪
jdbcTemplate.setDataSource(dataSource);
// 执行操作
int tom = jdbcTemplate.update("insert into account values(?,?)", "Tom", 5000);
System.out.println(tom);
}

1.3Spring产生JdbcTemplate对象

我们可以将JdbcTemplate的创建权交给Spring,将数据源DataSource的创建权也交给Spring,在Spring容器内部将数据源DataSource注入到JdbcTemplate模板对象中,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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="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/数据库"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>

<!-- jdbc模板对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>

测试:

1
2
3
4
5
6
7
@Test
public void test2() {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate = app.getBean(JdbcTemplate.class);
int row = jdbcTemplate.update("insert into account values(?,?)", "zhangsan", 5000);
System.out.println(row);
}

抽取jdbc配置

1
2
3
4
5
6
7
8
9
10
<!--    加载jdbc.properties-->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

<!-- 数据源对象-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${user.password}"></property>
</bean>

1.4知识要点

  • 导入spring-jdbc和spring-tx坐标
  • 创建数据库表和实体
  • 创建JdbcTemplate对象
    • JdbcTemplate jdbcTemplate = new JdbcTemplate();
    • jdbcTemplate.setDataSource(dataSource);
  • 执行数据库操作
    • 更新操作:
      • jdbcTemplate.update(sql,params);
    • 查询操作:
      • jdbcTemplate.query(sql,Mapper,params);
      • jdbcTemplate.query(sql,Mapper,params);

Spring练习

1.1Spring环境搭建步骤

  • 1.导入工程(Project&Module)
  • 2.导入静态页面(JSP或HTML)
  • 3.导入所需坐标
  • 4.创建包结构(controller、service、dao、domain、utils)
  • 5.导入数据库脚本(sql)
  • 6.创建POJO类
  • 7.创建配置文件(applicationContext.xml、spring-mvc.xml、jdbc.properties、log4j.properties )

1.2角色列表的展示步骤分析

  • 点击角色管理菜单发送请求到服务器端(修改角色管理菜单的url地址)
  • 创建RoleController和showList()方法
  • 创建RoleService和showList()方法
  • 创建RoleDao和findAll()方法
  • 使用JdbcTemplate完成查询操作
  • 将查询数据存储到Model中
  • 转发到role-list.jsp页面进行展示

1.3角色添加的步骤分析

  • 1.点击列表页面新建按钮跳转到角色添加页面
  • 2.输入角色信息,点击保存按钮,表单数据提交服务器
  • 3.编写RoleController的save()方法
  • 4.编写RoleService的save()方法
  • 5.编写RoleDao的save()方法
  • 6.使用JdbcTemplate保存Role数据到sys_role
  • 7.跳转回角色列表页面
id问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    public Long save(User user) {

// 创建PreparedStatementCreator
PreparedStatementCreator creator = new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
// 使用原始的jdbc完成有个Prepared Statement的组建
PreparedStatement preparedStatement = connection.prepareStatement("insert into sys_user values(?,?,?,?,?)", PreparedStatement.RETURN_GENERATED_KEYS);
preparedStatement.setObject(1,null);
preparedStatement.setString(2,user.getUsername());
preparedStatement.setString(3,user.getEmail());
preparedStatement.setString(4,user.getPassword());
preparedStatement.setString(5,user.getPhoneNum());
return preparedStatement;
}
};

// 创建keyHolder
GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(creator,keyHolder);

// 获得生成的主键
long userId = keyHolder.getKey().longValue();

// jdbcTemplate.update("insert into sys_user values(?,?,?,?,?)", null,user.getUsername(),user.getEmail(),user.getPassword(),user.getPhoneNum());

// 返回当前保存用户的id,该id是数据库自动生成的
return userId;
}

SpringMVC拦截器

1.1拦截器(interceptor)的作用

SpringMVC的拦截器类似于Servlet开发中的过滤器,用于处理器进行预处理和后处理


将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按之前定义的顺序被调用。拦截器也是AOP思想的具体实现。

1.2拦截器和过滤器区别

image-20220425085055708

1.3拦截器快速入门

自定义拦截器很简单,只有如下三步:

  • 创建拦截器类实现HandlerInterceptor接口
  • 配置拦截器
  • 测试拦截器的拦截效果

1.4代码实现

创建拦截器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.itheima.interceptor;

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

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class MyInterceptor1 implements HandlerInterceptor {
// 在目标方法执行之前执行
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
System.out.println("preHandle...");
String param = request.getParameter("param");
if("yes".equals(param)) {
return true;
} else {
request.getRequestDispatcher("/error.jsp").forward(request,response);
return true;// 返回true代表放行 返回false代表不放行

}
}


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


// 在整个流程都执行完毕吼执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}

配置拦截器:

1
2
3
4
5
6
7
8
<!--    配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!-- 对哪些资源执行拦截操作-->
<mvc:mapping path="/**" />
<bean class="com.itheima.interceptor.MyInterceptor1"></bean>
</mvc:interceptor>
</mvc:interceptors>

1.5拦截器方法说明

image-20220425094916039

1.6知识要点

自定义拦截器步骤

  • 创建拦截器类实现HandlerInterceptor接口
  • 配置拦截器
  • 测试拦截器的拦截效果

1.7案例-用户登录权限控制

需求:用户在没有登录的情况下,不能对后台菜单进行访问操作,点击菜单跳转到登录页面,只有用户登录成功后才能进行后台功能的操作

SpringMVC异常处理

1.1异常处理的思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。


系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,如下:

image-20220425170413283

1.2异常处理的两种方式

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

1.3简单异常处理器SimpleMappingExceptionResolver

SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置

1
2
3
4
5
6
7
8
9
10
<!--    配置简单映射异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error" /> <!-- 默认错误视图 -->
<property name="exceptionMappings">
<map>
<entry key="com.itheima.exception.MyException" value="error" />
<entry key="ClassCastException" value="error" />
</map>
</property>
</bean>

1.4自定义异常处理步骤

  • 1.创建异常处理器类实现HandlerExceptionResolver

    • ```java
      package com.itheima.exception;import org.springframework.web.servlet.HandlerExceptionResolver;
      import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;public class MyExceptionResolver implements HandlerExceptionResolver {// 参数Exception 异常对象
      // 返回值 ModelAndView 跳转到错误视图信息
      @Override
      public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
          ModelAndView modelAndView = new ModelAndView();
          if(e instanceof MyException) {
              modelAndView.addObject("info","自定义异常");
          } else if (e instanceof ClassCastException) {
              modelAndView.addObject("info","类转换异常");
          }
          modelAndView.setViewName("error");
          return modelAndView;
      }
      
      }
      1
      2
      3
      4
      5

      * 2.配置异常处理器

      * ```xml
      <bean class="com.itheima.resolver.MyExceptionResolver" />
  • 3.编写异常页面

  • 4.测试异常跳转

Mybatis

pojo

找不到类,打开右上角maven按钮,刷新或重新加载

ORM
  • O:对象,pojo包中的某个类
  • R:关系,关系型数据库中的某张表
  • M:映射,映射文件(xxxMapper.xml),将数据库的pojo包中的某个类一一对应(映射)

MyBatis开发步骤

  • 1.添加MyBatis的坐标
  • 2.创建User数据表
  • 3.编写User实体类
  • 4.编写映射文件UserMapper.xml
  • 5.编写核心文件SqlMapConfig.xml
  • 6.编写测试类

1.1编写二映射文件UserMapper.xml

1
2
3
4
5
6
7
8
9
<?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="edu.hniu.pojo.User"><!-- namespace命名随意 -->
<select id="listAllUser" resultType="edu.hniu.pojo.User">
select * from user
</select>
</mapper>

1.2编写核心文件SqlMapConfig.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED"><!--POOLED:使用Mybatis自带的数据库连接池来管理数据库连接-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--mapping文件路径配置-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>

1.3编写测试代码

1
2
3
4
5
6
7
8
9
10
11
12
//加载核心配置文件
Reader reader = reader = Resources.getResourceAsReader(resource);
//获取sqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//获取sqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//执行sql语句
List<Object> listAllUser = session.selectList("listAllUser");
//打印结果
System.out.println(listAllUser);
//释放资源
sqlSession.close();

Mybatis映射文件概述

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 映射文件DTD约束头 -->
<?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">

<!-- namespace命名空间,与下面语句的id一起组成查询的标识 -->
<mapper namespace="edu.hniu.pojo.User">
<select id="listAllUser" <!-- 语句的id标识,与上面的命名空间一起组成查询标识 -->resultType="edu.hniu.pojo.User"><!-- 查询结果对应的实体类型-->
select * from user
</select>
</mapper>

Mybatis的增删改查

1.Mybatis的插入操作

mybatis执行更新操作,提交事务
1
sqlSession.commit();
插入操作注意事项
  • 插入语句使用insert标签
  • 在映射文件中使用parameterType属性指定要插入的数据类型
  • Sql语句中使用#{实体属性名}方式引用实体中的属性值
  • 插入操作使用的API是sqlSession.insert(“命名空间.id”,实体对象);
  • 插入操作涉及数据库变化,所以要使用sqlSession提交事务,即sqlSession.commit();
UserMapper映射文件
1
2
3
4
5
6
7
8
<?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="edu.hniu.pojo.User">
<insert id="save" parameterType="edu.hniu.pojo.User">
insert into user values(#{id},#{username},#{age})
</insert>
测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package edu.hniu.test;

import edu.hniu.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

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

public class UserTest2 {
public static void main(String[] args) {
// 模拟user对象
User user = new User();
user.setName("李四");
user.setAge(34);

String resource = "mybatis-config.xml";
Reader reader = null;

try {
reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
session.insert("save",user);
// mybatis执行更新操作,提交事务
session.commit();
session.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}

2.MyBatis的修改数据操作

2.1编写UserMapper映射文件
1
2
3
4
5
6
7
8
9
<?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="edu.hniu.pojo.User">
<update id="update" parameterType="edu.hniu.pojo.User">
update user set username=#{username},age=#{age} where id=#{id}
</update>
</mapper>
测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package edu.hniu.test;

import edu.hniu.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.Reader;

public class UserTest3 {
public static void main(String[] args) {
// 模拟user对象
User user = new User();
user.setId(4);
user.setName("王五");
user.setAge(36);

String resource = "mybatis-config.xml";
Reader reader = null;

try {
reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
session.update("edu.hniu.pojo.User.update",user);
// mybatis执行更新操作,提交事务
session.commit();
session.close();

} catch (IOException e) {
e.printStackTrace();
}
}
}
修改操作注意问题
  • 修改语句使用update标签
  • 修改操作使用的API是sqlSession.update(“命名空间.id”,实体对象);
删除操作注意问题
  • 删除语句使用delete标签
  • sql语句中使用#{任意字符串}方式引用传递的单个参数
  • 删除操作使用的API是sqlSession.delete(“命名空间.id”,Object)

Mybatis核心配置文件概述

1.MyBatis核心配置文件层级关系

  • Configuration配置
    • properties属性
    • settings设置
    • typeAliases类型别名
    • typeHandlers类型处理器
    • objectFactory对象工厂
    • plugins插件
    • environments环境
      • environment环境变量
        • transactionManager事务管理器
        • dataSource数据源
    • databaseIdProvider数据库厂商标识
    • mappers映射源

2.Mybatis常用配置解析

2.1environments标签

image-20220301190115675

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

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

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

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

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

  • 使用相对于类路径的资源引用,例如:
  • 使用完全限定资源定位符(URL),例如:
  • 使用映射器接口实现类的完全限定类名,例如:
  • 将包内的映射器接口实现全部注册为映射器,例如:
2.3Properties标签

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

image-20220301193506586

2.4typeAliases标签

类型别名是为Java类型设置一个短的名字,原本的类型名称配置如下

image-20220301194242356

mybatis框架已经为我们设置好一些常用的类型的别名

Sting,long,int,double,boolean

Mybatis相应API

1.1SqlSession工厂构造器SqlSessionFactoryBulider

常用API:SqlSessionFactory build(InputStream inputStream)

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

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


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

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

设置数据库字符集编码为utf-8

接?characterEconding=UTF8

Mybatis的Dao层实现

1.1传统开发方式

编写UserDao接口
1
2
3
public interface UserDao {
List<User> findAll() throws IOException;
}

1.2代理开发方式

代理开发方式介绍

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

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

课堂笔记
1
// session.getMapper:通过seesion对象的getMapper()方法,获取到一个经过代理以后的UserMapper对象

Mapper接口开发需要遵循以下规范:
  • 1.Mapper.xml文件中的namespace与mapper接口的全限定名相同
  • 2.Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
  • 3.Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同
  • 4.Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultTyper的类型相同
编写UserMapper接口

image-20220302090802996

1.3知识小结

  • 手动对Dao进行实现:传统开发方式

  • 代理方式对Dao进行实现:

    • ```jav
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28

      ### Mybatis映射文件深入

      #### 1.1动态sql语句

      ##### 1.动态sql语句概述

      ![image-20220302095715398](C:\Users\15135\AppData\Roaming\Typora\typora-user-images\image-20220302095715398.png)

      ##### 2.动态SQL之<if>

      我们根据实体类的不同取值,使用不同的SQL语句进行查询。比如在id、如果不为空时可以根据id查询,如果username不为空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到

      ```xml
      <select id="findByCondition" parameterType="user" resultType="user">
      select * from user
      <where>
      <if test="id!=0">
      and id=#{id}
      </if>
      <if test="username!=null">
      and username=#{username}
      </if>
      <if test="password!=null">
      and password=#{password}
      </if>
      </where>
      </select>
3.动态SQL之

循环执行SQL的拼接操作,例如:SELECT * FROM USER WHERE id IN (1,2,5)

1
2
3
4
5
6
7
8
9
    <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>
<!-- open为开头,close为结尾,collection表示集合,item表示每一项,separator表示为分隔符 -->
4.SQL片段的抽取

SQL中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<sql id="selectUser"> select * from user</sql>

<select id="findByCondition" parameterType="user" resultType="user">
<include refid="selectUser"></include>
<where>
<if test="id!=0">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
5.动态SQL之

image-20220305143152590

6.动态SQL之
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<insert id="save" parameterType="user">
insert into userinfo values(#{id},
<trim suffixOverrides="," suffix=")">
<choose>
<when test="username=null or username=''" >
'default',
</when>
<otherwise>
#{username},
</otherwise>
</choose>
<choose>
<when test="password=null or username=''">
'default',
</when>
<otherwise>
#{password},
</otherwise>
</choose>
<choose>
<when test="realName=null or realName=''">
'default'
</when>
<otherwise>
#{realName}
</otherwise>
</choose>
</trim>
</insert>

Mybatis核心配置文件的深入

1.1typeHandlers标签

无论是Mybaits在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。下表描述了一些默认的类型处理器(截取部分)

image-20220305143505524

你可以重写类型处理器或创建你自己的类型处理器来处理不支持或非标准的类型。具体做法为:实现org.apache.ibatis.type.TypeHandler接口,或继承一个很遍历的类org.apache.ibatis.type.BaseTypeHandler,然后可以选择性地将它映射到一个JDBC类型


开发步骤:
  • 1.定义转换类型继承类BaseTypeHandler
  • 2.覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时,mysql字符串类型转换成java的Type类型的方法
  • 3.在MyBatis核心配置文件中进行注册
  • 4.测试转换是否正确

1.2plugins标签

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

开发步骤
  • 1.导入通用的PageHelper的坐标
  • 2.在mybatis核心配置文件中配置PageHelper插件
  • 3.测试分页数据获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 导入通用PageHelper坐标 -->

<!-- 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>


<!-- 分页-->
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>

1.3知识小结

MyBatis核心配置文件常用标签
  • 1.properties标签:该标签可以加载外部的properties文件
  • 2.typeAliases标签:设置类型别名
  • 3.environments标签:数据源环境配置标签
  • 4.typeHandlers标签:配置自定义类型处理器
  • 5.plugins标签:配置MyBatis的插件

MyBatis的多表操作

1.1一对一查询

一对一查询的模型

用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

一对一查询的需求:查询一个订单,与此同时查询出该订单所需的用户

注意事项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    <resultMap id="orderMap" type="order">
<!-- 手动指定字段与实体属性的映射关系
column:数据表的字段名称
properties:实体的属性名称
-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
<!-- <result column="uid" property="user.id"></result>-->
<!-- <result column="username" property="user.username"></result>-->
<!-- <result column="password" property="user.password"></result>-->
<!-- <result column="birthday" property="user.birthday"></result>-->

<!-- property:当前实体(order)中的属性名称(private User user)
javaType:当前实体(order)中的属性的类型(User)
-->

<association property="user" javaType="user">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
</association>
</resultMap>

<select id="findAll" resultMap="orderMap">
select *,o.id oid from orders o,user u where o.uid=u.id
</select>

1.2一对多查询的模型

用户和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户

一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<resultMap id="userMap" type="user">
<id column="uid" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>


<!-- resultMap的id值为select的resultMap的resultMap的值 -->



<!-- 配置集合信息
property:集合名称
ofType:当前集合中的数据类型
-->
<collection property="orderList" ofType="order">
<!-- 封装order的数据-->
<id column="oid" property="id"></id>
<result column="ordertime" property="ordertime"></result>
<result column="total" property="total"></result>
</collection>
</resultMap>

<select id="findAll" resultMap="userMap">
select *,o.id oid from user u,orders o where u.id=o.id
</select>//

1.3多对多查询

多对多查询的模型

用户和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用

多对多查询的需求:查询用户同时查询出该用户的所有角色

1.4知识小结

Mybatis多表配置方式:
  • 一对一配置:使用做配置
  • 一对多配置:使用 + 做配置
  • 多对多配置:使用 + 做配置

MyBatis的注解开发

MyBatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了,我们先围绕一些基本的CRUD来学习,再学医复杂映射多表操作


  • Insert:实现新增
  • Update:实现更新
  • Delete:实现删除
  • Select:实现查询
  • Result:实现结果集封装
  • Results:可以与@Result一起使用,封装多个结果集
  • One:实现一对一结果集封装
  • Many:实现一对多结果集封装
UserMapper接口内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@Insert("insert into user values(#{id},#{username},#{password},#{birthday})")
public void save(User user);

@Update("update user set username=#{username},password=#{password} where id=#{id}")
public void update(User user);

@Delete("delete from where id=#{id}")
public void delete(int id);

@Select("select * from user where id=#{id}")
public User findById(int id);

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

MyBatis的注解实现复杂映射开发

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


  • @Results:代替的是标签该注解中可以使用单个@Result注解,也可以使用@Result结合,使用格式:@Results({@Result(),@Result()})或@Results(@Result())

  • @Result:代替了标签和标签,@Result中属性介绍

    • column:数据库的列名
    • property:需要装配的属性名
    • one:需要使用的@One注解(@Result(one=@One)())
    • many:需要使用的@Many注解(@Result(many=@many)())
  • @One(一对一):代替了标签,是多表查询的关键,在注解中用来指定子查询返回单一对象,@One注解属性介绍:

    • select:指定用来多表查询的sqlmapper
    • 使用格式:@Result(column=””,property=””,one=@One(select=””))
  • @Many(多对一):代替了标签,是多表查询的关键,在注解中用来指定子查询返回对象集合

    • 使用格式:@Result(property=””,column=””,many=@Many(select=””))
一对一注解开发代码部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 这里是OrderMapper实现查询所有用户以及用户订单
@Select("select *,o.id oid from orders o, user u where o.uid=u.id")
@Results({
@Result(column = "oid",property = "id"),
@Result(column = "ordertime",property = "ordertime"),
@Result(column = "total",property = "total"),
@Result(column = "uid",property = "user.id"),
@Result(column = "username",property = "user.username"),
@Result(column = "password",property = "user.password"),
@Result(column = "birthday",property = "user.birthday")


})
public List<Order> findAll();

以下是测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.itheima.test;

import com.itheima.domain.Order;
import com.itheima.domain.User;
import com.itheima.mapper.OrderMapper;
import com.itheima.mapper.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;

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

public class MybatisTest2 {

private OrderMapper mapper;

@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
mapper = sqlSession.getMapper(OrderMapper.class);
}

@Test
public void testFindAll() {
List<Order> all = mapper.findAll();
for (Order order : all) {
System.out.println(order);
}

}


}

SSM框架整合

原始整合方式

  • 1.创建数据表
  • 2.创建Maven工程
  • 3.导入Maven坐标
  • 4.编写实体类
  • 5.编写Mapper接口
  • 6.编写Service接口
  • 7.编写Service接口实现
  • 8.编写Controller
  • 9.编写添加页面
  • 10.编写列表页面
  • 11.编写相应配置文件
    • Spring配置文件:applicationContext.xml
    • SpringMVC配置文件:spring-mvc.xml
    • MyBatis映射文件:AccountMapper.xml
    • MyBatis核心文件:sqlMapConfig.xml
    • 数据库连接信息文件:jdbc.properties
    • Web.xml文件:web.xml
    • 日志文件:log4j.xml
  • 12.添加测试账户
  • 13.测试账户列表

Spring整合MyBatis

1.整合思路

1
2
3
4
5
6
7
// 将Session工厂交给Spring容器管理,从容器中获得执行操作的Mapper实例即可
SqlSession sqlSession = MyBatisUtils.openSession();
AccountMapper accountMapper = sqlSession.getMapper(AccountMapper.class);
accountMapper.save(account);
//将事务的控制交给Spring容器进行声明式事务控制
sqlSession.commit();
sqlSession.close();

2.将sqlSessionFactory配置到Spring容器中

1


文章作者: Water monster
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Water monster !
评论
  目录