Java-Spring:SSM整合

Java SpringMVC-SSM 整合

学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1aE41167Tu?p=1

学习文档: 微信公众号 狂神说 --https://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=3&sn=456dc4d66f0726730757e319ffdaa23e&scene=18&uin=&key=&devicetype=Windows+10+x64&version=63020170&lang=zh_CN&ascene=7&fontgear=2

1、环境要求

环境:

  • IDEA
  • MySQL 5.7.19
  • Tomcat 9
  • Maven 3.6

要求:

  • 需要熟练掌握MySQL数据库,Spring,JavaWeb及MyBatis知识,简单的前端知识;

对应环境的级别

创建一个项目的基本步骤包括

需求分析 ----> 设计数据库 ----> 业务 ----> 前端界面

image-20210818102830000

2、存放书籍数据项目的SSM整合

Mybatis层、Spring层

2.1、数据库环境

需要创建一个存放书籍数据的数据库表,这样才能去调用模型 Model 中的数据,有地方 有数据可调用。

创建数据库表的代码:

CREATE DATABASE `ssmbuild`;

USE `ssmbuild`;

DROP TABLE IF EXISTS `books`;

CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '书id',
`bookName` VARCHAR(100) NOT NULL COMMENT '书名',
`bookCounts` INT(11) NOT NULL COMMENT '数量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT  INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`)VALUES
(1,'Java',1,'从入门到放弃'),
(2,'MySQL',10,'从删库到跑路'),
(3,'Linux',5,'从进门到进牢');

结果:

image-20210818103357430

2.2、基本环境搭建

  1. 我们新建一个Maven项目, 为 ssmbuild。并且添加web的支持;
  2. 导入相关的pom依赖。 需要导入的依赖 jar包有: junit、数据库驱动、数据库连接池;Servlet ,JSP,mybatis,mybatis-spring, spring。 以及需要导入静态资源导出问题。

maven依赖

    <!--maven依赖:unit、数据库驱动、数据库连接池;Servlet ,JSP,mybatis,mybatis-spring, spring-->
    <dependencies>
        <!--Junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!--Servlet - JSP -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--Mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
        <!--Spring-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
    </dependencies>

maven静态资源导出问题:

    <!--静态资源导出问题-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

3.连接数据库。 连接数据库中对应的 ssmbuild 表。

4.建立项目所需要的基本结构和配置框架

  • com.AL.pojo。实体类,属性对应着数据库表的字段
  • com.AL.dao。dao层,属于Model 的数据,进行底层的数据调度
  • com.AL.service。service层,属于Model 的业务,进行底层的业务处理
  • com.AL.controller。 controller层,控制层,servlet,去创建数据模型,访问数据库,并与视图结合后返回给请求者。
  • mybatis-config.xml。这个是 mybatis的配置文件
  • applicationContext.xml 。 总的各个框架的,集合后的核心配置文件。

2.2.1、Mybatis层编写

1、数据库配置文件 database.properties

这个是用来连接数据库的配置文件,表明你连接的哪个数据库。 如果使用的是 Mysql8.0+,能够增加时区配置。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=123456

2、IDEA关联数据库.

3、编写MyBatis的核心配置文件.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <typeAliases>
        <package name="com.AL.pojo"/>
    </typeAliases>
    
    <mappers>
        <mapper resource="com/AL/dao/BookMapper.xml"/>
    </mappers>
    
</configuration>

4、编写数据库对应的实体类 com.AL.pojo.Books。

使用lombok插件,在xml配置文件中导入 maven依赖:

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.18</version>
    <scope>provided</scope>
</dependency>

实体类:class Books。 属性对应着数据库字段名。

并采用Lombok进行注解开发,@data,以及有参无参构造器:@AllArgsConstructor,@NoArgsConstructor

package com.AL.pojo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {

    private int bookID;
    private String bookName;
    private int bookCounts;
    private String detail;

}

5、编写Dao层的 Mapper接口!

package com.AL.dao;

import com.AL.pojo.Books;

import java.util.List;

public interface BookMapper {
    //增加一个Book
    int addBook(Books book);

    //根据id删除一个Book
    int deleteBookById(int id);

    //更新Book
    int updateBook(Books books);

    //根据id查询,返回一个Book
    Books queryBookById(int id);

    //查询全部Book,返回list集合
    List<Books> queryAllBook();
}

6、编写接口对应的 Mapper.xml 文件。需要导入MyBatis的包; 接口实现类

导入MyBatis的包:在这里,我们就能利用Mybatis去做:在BookMaper.xml文件中,可以直接将mybatis-config.xml配置文件更改就能使用:即 configuration改为mapper即可。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.dao.BookMapper">
</mapper>
  • 接口实现类BookMapper.xml (为dao层接口实现类)

    在这里注册了 mapper 映射了 实现类的地址,即BookMapper接口被实现。

    在这个mapper文件中定义了sql语句 对应着接口中的方法:对数据库操作的方法 sql

在什么时候采用 parameterType 什么时候采用 resultType?【对于返回结果就是 resultType,向其中添加修改元素就是 parameterType】

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.dao.BookMapper">
    <!--增加一个Book-->
    <insert id="addBook" parameterType="Books">
      insert into ssmbuild.books(bookName,bookCounts,detail)
      values (#{bookName}, #{bookCounts}, #{detail})
   </insert>

    <!--根据id删除一个Book-->
    <delete id="deleteBookById" parameterType="int">
      delete from ssmbuild.books where bookID=#{bookID}
   </delete>

    <!--更新Book-->
    <update id="updateBook" parameterType="Books">
      update ssmbuild.books
      set bookName = #{bookName},bookCounts = #{bookCounts},detail = #{detail}
      where bookID = #{bookID}
   </update>

    <!--根据id查询,返回一个Book-->
    <select id="queryBookById" resultType="Books">
      select * from ssmbuild.books
      where bookID = #{bookID}
   </select>

    <!--查询全部Book-->
    <select id="queryAllBook" resultType="Books">
      SELECT * from ssmbuild.books
   </select>
</mapper>
  • 在mybatis-config.xml配置文件中注册绑定 我们的dao层接口:这里注册映射文件,采用的是 类方法, 不是绑定接口实现类;

    在前面的 mybatis-config.xml文件中已经进行了注册绑定。【采用 resource = ,绑定类方法】

        <mappers>
            <mapper resource="com/AL/dao/BookMapper.xml"/>
        </mappers>
    

7.编写Service层的接口和实现类
接口:BookService:底下需要去实现,调用dao层

package com.AL.service;

import com.AL.pojo.Books;

import java.util.List;

//BookService:底下需要去实现,调用dao层
public interface BookService {
    //增加一个Book
    int addBook(Books book);
    //根据id删除一个Book
    int deleteBookById(int id);
    //更新Book
    int updateBook(Books books);
    //根据id查询,返回一个Book
    Books queryBookById(int id);
    //查询全部Book,返回list集合
    List<Books> queryAllBook();
}

实现类:调用dao层的操作,设置一个set接口,方便Spring管理【set注入,IOC容器注册实现Bean】

package com.AL.service;

import com.AL.dao.BookMapper;
import com.AL.pojo.Books;

import java.util.List;

public class BookServiceImpl implements BookService {

    //调用dao层的操作,设置一个set接口,方便Spring管理
    private BookMapper bookMapper;

     // 利用set进行动态 实现值的注入。
    public void setBookMapper(BookMapper bookMapper) {
        this.bookMapper = bookMapper;
    }

    public int addBook(Books book) {
        return bookMapper.addBook(book);
    }

    public int deleteBookById(int id) {
        return bookMapper.deleteBookById(id);
    }

    public int updateBook(Books books) {
        return bookMapper.updateBook(books);
    }

    public Books queryBookById(int id) {
        return bookMapper.queryBookById(id);
    }

    public List<Books> queryAllBook() {
        return bookMapper.queryAllBook();
    }
}

Mybatis层写完了, 即底层的 dao层 和 service层, 数据层和 业务层, 模型 Model完成了

回忆spring:

对于spring 的重要的核心是:IOC和AOP。 控制反转和面向切面编程(利用动态代理)。IOC创建对象的方式:构造器注入、set注入、c命名和p命名注入

构造器注入:会默认直接走无参构造器。 下面的示例是直接通过参数名设置赋值。

 <bean id="exampleBean" class="examples.ExampleBean"> 
 <constructor-arg name="years" value="7500000"/> 
 </bean>

set注入:要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 ,

​ 注意点:如果是自己定义的类,那么引入 这个值就是引入,使用 ref,而不是value

 <bean id="address" class="com.AL.pojo.Address"/> <!--我们引入自定义的 Address 类-->
 <bean id="student" class="com.AL.pojo.Student">
     <!--第一种,普通值注入,value-->
     <property name="name" value="小明"/>
     <!--第二种,Bean注入,ref-->
     <property name="address" ref="address"/>
 </bean>

p 命名空间注入:**P(属性: properties)命名空间 , 属性依然要设置set方法.**set方法,即此时的 setName 等.,在这里可以直接注入属性的值:property. 所以说 p命名法对应着 set类型注入

 <!--P(属性: properties)命名空间 , 属性依然要设置set方法
 set方法,即此时的 setName 等. 在这里可以直接注入属性的值:property-->
 <bean id="user" class="com.AL.pojo.User" p:name="鑫鑫" p:age="18"/>

c命名空间注入: c命名法进行依赖注入,类似于构造器方法。 需要在类中创建有参构造器

 <!--C(构造: Constructor)命名空间, 通过构造器注入-->
 <bean id="user2" class="com.AL.pojo.User" c:name="鑫仔" c:age="18"/>

对于具体的dao层、service层的代码一种的IOC变化方式 demo:

dao层:接口和接口实现类:

package com.AL.dao;

public interface UserDao {
   public void getUser();
}
package com.AL.dao;

public class UserDaoImpl implements UserDao {
   public void getUser(){
       System.out.println("默认用户的数据");
   }
}
package com.AL.dao;

public class UserMysqlDaoImpl implements UserDao{
   public void getUser(){
       System.out.println("Mysql用户的数据");
   }
}

service层:接口和接口实现类

package com.AL.service;

public interface UserService {
   public void getUser();
}
package com.AL.service;

import com.AL.dao.UserMysqlDaoImpl;
import com.AL.dao.UserDao;
import com.AL.dao.UserDaoImpl;
import com.AL.dao.UserOracleDaoImpl;


public class UserServiceImpl implements UserService {
/**
    //private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = new UserMysqlDaoImpl();
    //private UserDao userDao = new UserOracleDaoImpl();
*/
   private UserDao userDao;
   // 利用set进行动态 实现值的注入。
   public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
   }
   public void getUser(){
       userDao.getUser();
   }
}

原始的 控制权交给程序猿的方式:

private UserDao userDao = new UserDaoImpl();
private UserDao userDao = new UserMysqlDaoImpl();

IOC(控制反转)的方式,将bean信息交给spring容器进行装配:【在这里,使用set注入实现IOC创建对象】

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

交给spring:xml配置文件中 注入容器bean:

   <bean id="userImpl" class="com.AL.dao.UserDaoImpl"/>
   <bean id="mysqlImpl" class="com.AL.dao.UserMysqlDaoImpl"/>
   <bean id="oracle" class="com.AL.dao.UserOracleDaoImpl"/>
   <bean id="UserServiceImpl" class="com.AL.service.UserServiceImpl">
<!--        <property name="userDao" ref="mysqlImpl"/>-->
       <property name="userDao" ref="oracle"/>
   </bean>

2.2.2、Spring层

Spring层

1、配置Spring整合MyBatis,我们这里数据源使用c3p0连接池;

2、我们去编写Spring整合Mybatis的相关的配置文件;spring-dao.xml

我们原来最开始在 mybatis中 **,需要自己在xml配置文件中引入 **sqlsessionFactoryBuild,创建一个实体工具类,然后去获得一个SqlSession,在spring中,spring层把这部分的工作完成了:

创建的Spring, 注入sqlsession到指定的包下的mapper文件:spring-dao.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
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 配置整合mybatis -->
    <!-- 1.关联数据库文件 -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- 2.数据库连接池 -->
    <!--数据库连接池
        dbcp 半自动化操作 不能自动连接
        c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
    -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 配置连接池属性 -->
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- c3p0连接池的私有属性 -->
        <property name="maxPoolSize" value="30"/>
        <property name="minPoolSize" value="10"/>
        <!-- 关闭连接后不自动commit -->
        <property name="autoCommitOnClose" value="false"/>
        <!-- 获取连接超时时间 -->
        <property name="checkoutTimeout" value="10000"/>
        <!-- 当获取连接失败重试次数 -->
        <property name="acquireRetryAttempts" value="2"/>
    </bean>

    <!-- 3.配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBaties全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
    <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 给出需要扫描Dao接口包 -->
        <property name="basePackage" value="com.AL.dao"/>
    </bean>

</beans>

3、Spring整合service层

spring-service.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">

    <!-- 扫描service相关的bean -->
    <context:component-scan base-package="com.AL.service" />

    <!--BookServiceImpl注入到IOC容器中-->
    <bean id="BookServiceImpl" class="com.AL.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>

一定要将不同的context 整合起来,其中一种方式如下所示:直接导入, applicationContext.xml 配置文件。里面包括的配置信息有:

  • spring-dao.xml:在这里,xml配置文件去完成 sqlSessionFactory的创建,去得到sqlSession然后对数据库表操作CRUD;且扫描dao接口
  • spring-service.xml:将service层的类注册到容器 bean中,定义事务管理。【属性值注入,IOC容器】
  • spring-mvc.xml:即关于springmvc中的dispatcherServlet的配置信息,定义视图解析器,开启注解驱动等。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="spring-dao.xml"/>
    <import resource="spring-service.xml"/>
    <import resource="spring-mvc.xml"/>

</beans>

Spring层搞定!再次理解一下,Spring就是一个大杂烩,一个容器!

在IDEA中开发时,出现的一个问题:

下面的 ref=“bookMapper” 爆红。

image-20220407104557263

<!--BookServiceImpl注入到IOC容器中-->
<bean id="BookServiceImpl" class="com.AL.service.BookServiceImpl">
    <property name="bookMapper" ref="bookMapper"/>
</bean>

这个 ref 引用的是 bookMapper,即是dao层中BookMapper(它在spring-dao.xml中注册到了spring容器:)。

爆红的原因,这说明没有把这个 bookMapper给引入进去。 解决: 在设置中 Project Structure中,把这个几个 xml配置文件添加到 spring中:

image-20220407104932581

2.2.3、SpringMVC层

对于SpringMVC层, 那么就要创建一个 web,增加一个 add framework:

**1.**配置 web.xml 文件。其中包括: 围绕着DispatchServlet进行调度设计、 乱码过滤。

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

   <!--DispatcherServlet-->
   <servlet>
       <servlet-name>DispatcherServlet</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <!--一定要注意:我们这里加载的是总的配置文件,之前被这里坑了!-->  
           <param-value>classpath:applicationContext.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>

   <!--encodingFilter-->
   <filter>
       <filter-name>encodingFilter</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>encodingFilter</filter-name>
       <url-pattern>/*</url-pattern>
   </filter-mapping>
   
   <!--Session过期时间-->
   <session-config>
       <session-timeout>15</session-timeout>
   </session-config>
   
</web-app>

2、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
   https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 配置SpringMVC -->
    <!-- 1.开启SpringMVC注解驱动 -->
    <mvc:annotation-driven />
    <!-- 2.静态资源默认servlet配置-->
    <mvc:default-servlet-handler/>

    <!-- 3.配置jsp 显示ViewResolver视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

    <!-- 4.扫描web相关的bean -->
    <context:component-scan base-package="com.AL.controller" />

</beans>

3、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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <import resource="classpath:spring-dao.xml"/>
    <import resource="classpath:spring-service.xml"/>
    <import resource="classpath:spring-mvx.xml"/>

</beans>

配置文件,暂时结束!Controller 和 视图层编写

2.2.4、Controller 和 视图层编写

方法一:查询全部书籍

1、BookController 类编写 ,

1.定义一个 BookController, 用于去调用 service层:

package com.AL.controller;

import com.AL.pojo.Books;
import com.AL.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

@Controller
@RequestMapping("/book")
public class BookController {

    @Autowired
    @Qualifier("BookServiceImpl")
    private BookService bookService;

    //查询全部的书籍, 并且返回到一个书籍展示页面
    @RequestMapping("/allBook")
    public String list(Model model) {
        List<Books> list = bookService.queryAllBook();
        model.addAttribute("list", list);
        return "allBook";
    }
}

@Autowired 和 @Qualifier注解:

在spring中学习时关于使用注解实现自动装配bean的方式:@Autowired、@Qualifier、@Resource。然后在xml配置中开启注解,即可完成向spring容器中自动装配bean,并且能够获取。

@Autowired 等价于, 名字需要符号 byName

@Qualifier适用于当容器 beans.xml中有多个属性类型一样,但 id 名字不一样的,针对特定对象的:

    @Autowired
    @Qualifier(value = "cat2")
    private Cat cat;
<bean id="cat" class="com.AL.pojo.Cat"/>
<bean id="cat2" class="com.AL.pojo.Cat"/>

2.需要跳转的 allBook层的页面:【显示书籍信息】

image-20220407210922123

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>书籍列表</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>书籍列表 —— 显示所有书籍</small>
                </h1>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-4 column">
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
        </div>
        <div class="col-md-4 column"></div>
        <div class="col-md-4 column">
            <%--查询书籍--%>
            <form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
                <span style="color: blue; font-weight: bold">${error}</span>
                <input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
                <input type="submit" value="查询" class="btn btn-primary">
            </form>
        </div>
    </div>

    <div class="row clearfix">
        <div class="col-md-12 column">
            <table class="table table-hover table-striped">
                <thead>
                <tr>
                    <th>书籍编号</th>
                    <th>书籍名字</th>
                    <th>书籍数量</th>
                    <th>书籍详情</th>
                </tr>
                </thead>

                <%--书籍从数据库中查询出来。然后遍历这个List得到书籍信息:foreach--%>
                <tbody>
                <c:forEach var="book" items="${requestScope.get('list')}">
                    <tr>
                        <td>${book.getBookID()}</td>
                        <td>${book.getBookName()}</td>
                        <td>${book.getBookCounts()}</td>
                        <td>${book.getDetail()}</td>
                        <td>
                            <a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |
                            <a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a>
                        </td>
                    </tr>
                </c:forEach>
                </tbody>
            </table>
        </div>
    </div>
</div>

3.对于默认页面的一个修改, 让其 url路径直接变为我们 controlle的路径, 然后就能进行跳转:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
  </head>
  <body>
<h3>
  <a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>
  </body>
</html>

启动Tmocat,进行测试:查看能否运行:

image-20220408093014950

测试:进行排错的步骤

  1. 查看这个bean注入是否成功
  2. 使用Junit单元测试。查看代码获取bean后 是否能够查询出结果
  3. 能够正常查询的话。说明问题不在底层,出现在了 spring层。就检查 springMVC中问题,是否能够调用bean。
    1. applicationContext.xml中有没有注入bean
    2. web.xml中绑定配置文件,DispatchServlet时配置的是不是 关于service层的xml配置文件?
import com.AL.pojo.Books;
import com.AL.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MyTest {
    @Test
    public void test(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookServiceImpl = (BookService) context.getBean("BookServiceImpl");
        for (Books books : bookServiceImpl.queryAllBook()) {
            System.out.println(books);
        }
    }
}

能够在 java IDEA中输出结果:表明我们的代码没有问题。能够在后台进行调用。

image-20210818145925637

对默认的初始化页面进行修改:显示的色彩,字体的修改 居中,文本格式

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>首页</title>
    <style type="text/css">
      a{
        text-decoration: none;
        color: black;
        font-size: 18px;
      }
      h3 {
        width: 180px;
        height: 38px;
        margin: 100px auto;
        text-align: center;
        line-height: 38px;
        background: deepskyblue;
        border-radius: 5px;
      }
    </style>
  </head>
  <body>
<h3>
  <a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>
  </body>
</html>

结果如下所示:

image-20210818145508710

对于书籍展示页面: 导入bookstrap, 对 book list 进行展开:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>书籍列表</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>书籍列表 —— 显示所有书籍</small>
                </h1>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-4 column">
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
        </div>
        <div class="col-md-4 column"></div>
        <div class="col-md-4 column">
            <%--查询书籍--%>
            <form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
                <span style="color: blue; font-weight: bold">${error}</span>
                <input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
                <input type="submit" value="查询" class="btn btn-primary">
            </form>
        </div>
    </div>

    <div class="row clearfix">
        <div class="col-md-12 column">
            <table class="table table-hover table-striped">
                <thead>
                <tr>
                    <th>书籍编号</th>
                    <th>书籍名字</th>
                    <th>书籍数量</th>
                    <th>书籍详情</th>
                </tr>
                </thead>

                <%--书籍从数据库中查询出来。然后遍历这个List得到书籍信息:foreach--%>
                <tbody>
                <c:forEach var="book" items="${requestScope.get('list')}">
                    <tr>
                        <td>${book.getBookID()}</td>
                        <td>${book.getBookName()}</td>
                        <td>${book.getBookCounts()}</td>
                        <td>${book.getDetail()}</td>
                        <td>
                            <a href="${pageContext.request.contextPath}/book/toUpdateBook?id=${book.getBookID()}">更改</a> |
                            <a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a>
                        </td>
                    </tr>
                </c:forEach>
                </tbody>
            </table>
        </div>
    </div>
</div>

修改后的结果:

image-20210818150300413

方法二:添加书籍功能

跳转到添加书籍页面:

  • 在进入所有书籍展示页面, 需要提供一个 按钮, 对应功能为跳转到 添加书籍页面, 对应的url为 /toAddBook, 调用toAddPaper方法,进行跳转:
    <div class="row">
        <div class="col-md-4 column">
            <a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
        </div>
    </div>
  • 在controller中的代码部分: 增加收到用户此请求后,跳转到增加书籍信息的页面:
//跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPaper() {
    return "addBook";
}
  • 定义添加书籍的页面:当添加书籍页面进行添加的时候, controll需要相应添加这个事件:在添加页面,实行添加动作后,指定跳转页面为 /book/addBook,方法这里选择 post。 注意:这里的名字要对应类中的属性,这样才能添加成功。

    响应动作值value=“添加”,然后跳转:action=“${pageContext.request.contextPath}/book/addBook” method=“post”

在添加的时候,可以定义 required,这样就必须有内容,才能提交:

    <div class="form-group">
        <label>书籍名称:</label>
        <input type="text" name="bookName" class="form-control" required>
    </div>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>

<html>
<head>
    <title>新增书籍</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>新增书籍</small>
                </h1>
            </div>
        </div>
    </div>
    <form action="${pageContext.request.contextPath}/book/addBook" method="post">
        <div class="form-group">
            <label>书籍名称:</label>
            <input type="text" name="bookName" class="form-control" required>
        </div>
        <div class="form-group">
            <label>书籍数量:</label>
            <input type="text" name="bookCounts" class="form-control" required>
        </div>
        <div class="form-group">
            <label>书籍描述:</label>
            <input type="text" name="detail" class="form-control" required>
        </div>
        <div class="form-group">
            <input type="submit" class="form-control" value="添加" required>
        </div>
    </form>

</div>

**相应添加事件,**url路径来到 /book/addBook 这里对应的 controller,去调用的方法, **添加完成后,重定向到开始的所有书籍页面:**此时目前的controller中关于添加书籍的代码为:

//跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPaper() {
    return "addBook";
}

// 添加书籍的请求
@RequestMapping("/addBook")
public String addPaper(Books books) {
    System.out.println(books);
    bookService.addBook(books);
    return "redirect:/book/allBook";  //重定向到我们的@RequestMapping("/allBook")请求
}

成功添加书籍,并返回到 重定向到 展示所有书籍界面:

image-20210818151009671

image-20210818150847020

再次捋一下思路:

  1. 在所有书籍显示页面,增加新增书籍按钮。绑定新增书籍按钮点击提交的路径:新增书籍
  2. /book/toaddBook 路径请求转发到 addBook.jsp页面,在这里进行添加书籍。 表单提交,将书籍属性传递给 books,采用的映射,字段和属性对应。【参数的数据传递】
  3. 添加成功后重定向到 redirect:/book/allBook 所有书籍显示页面。
方法三:修改书籍

在页面添加修改和删除的选项:

点击时,controller得到请求,然后让其响应跳转到修改或删除的指定页面,

在这个页面进行修改和删除, 再选择提交,指定的按钮 动作事件,发送请求再重定向到 所有书籍页面。

对于展示所有书籍页面的按钮,在前面已经增加过了。

controller代码修改: 增加跳转到 修改书籍信息页面, 以及书籍信息页面修改后的重定向:

//跳转到修改页面
@RequestMapping("/toUpdateBook")
public String toUpdateBook(Model model, int id) {
    Books books = bookService.queryBookById(id);
    System.out.println(books);
    model.addAttribute("QBook",books);
    return "updateBook";
}

//修改书籍的请求
@RequestMapping("/updateBook")
public String updateBook(Model model, Books books) {
    System.out.println("updateBook"+ books );
    bookService.updateBook(books);
    return "redirect:/book/allBook";
}

在这里需要设置修改书籍页面的信息:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>修改信息</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 引入 Bootstrap -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">

    <div class="row clearfix">
        <div class="col-md-12 column">
            <div class="page-header">
                <h1>
                    <small>修改书籍</small>
                </h1>
            </div>
        </div>
    </div>

    <form action="${pageContext.request.contextPath}/book/updateBook" method="post">
        <div class="form-group">
            <label>书籍名称:</label>
            <input type="text" name="bookName" class="form-control" value="${QBook.bookName}" required>
        </div>
        <div class="form-group">
            <label>书籍数量:</label>
            <input type="text" name="bookCounts" class="form-control" value="${QBook.bookCounts}" required>
        </div>
        <div class="form-group">
            <label>书籍描述:</label>
            <input type="text" name="detail" class="form-control" value="${QBook.detail}" required>
        </div>
        <div class="form-group">
            <input type="submit" class="form-control" value="修改" required>
        </div>
    </form>

</div>

结果:有问题, 没有覆盖掉 。 添加事务后成功。

有问题,是不是因为事务没有提交的原因。

添加AOP 面向切面编程:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.8.M1</version>
</dependency>

在spring-service.xml配置文件中已经写过了。

方法四:删除书籍

删除书籍,对于这个用户请求,直接重定向到所有书籍展示的页面即可, 不用定义一个删除界面

删除书籍对应的 跳转路径 url 请求:

<a href="${pageContext.request.contextPath}/book/deleteBook/${book.getBookID()}">删除</a>

controller页面:

// 删除书籍
@RequestMapping("/deleteBook/{bookId}")
public String deleteBook(@PathVariable("bookId") int id) {
    bookService.deleteBookById(id);
    return "redirect:/book/allBook";
}
方法五:新增搜索功能

前端所有书籍页面:

增加查询功能:

<div class="col-md-4 column">
    <%--查询书籍--%>
    <form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
        <span style="color: blue; font-weight: bold">${error}</span>
        <input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
        <input type="submit" value="查询" class="btn btn-primary">
    </form>
</div>

在进行代码修改的时候, 从下往上进行:

对于dao层:

  • 接口
// 根据名字查询书籍信息
Books queryBookByName(String bookName);
  • 接口实现类: sql语句,注册绑定:
<select id="queryBookByName" resultType="Books">
    select * from ssmbuild.books where bookName = #{bookName}
</select>

service层也是同样的事情:

  • 接口:
// 根据名字查询书籍信息
Books queryBookByName(String bookName);
  • 接口实现类:
public Books queryBookByName(String bookName){
    return bookMapper.queryBookByName(bookName);
}

Controller 代码 响应请求调用 servlet

//查询书籍
@RequestMapping("/queryBook")
public String queryBook(String queryBookName, Model model){
    Books books = bookService.queryBookByName(queryBookName);
    System.out.println("queryBook=>"+books);
    List<Books> list = new ArrayList<Books>();
    list.add(books);

    if (books==null){
        list = bookService.queryAllBook();
        model.addAttribute("error","未查到");
    }

    model.addAttribute("list", list);
    return "allBook";
}

添加一个功能:显示全部书籍

<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>

想要收到错误信息,在前端页面:

  • 获取 error信息展示
<%--查询书籍--%>
<form action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
    <span style="color: blue; font-weight: bold">${error}</span>
    <input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
    <input type="submit" value="查询" class="btn btn-primary">
</form>

总结

总的代码和流程分析

项目结构图:

image-20210818154048629

从下往上进行修改,先写底层,

image-20210818153912984

配置文件解析

database.properties

  • database.properties:数据库资源配置文件

    • driver:数据库驱动
    • url:数据库表 ssmbuild 的url地址
    • username:数据库管理员的名字;password:数据库管理员的密码
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8
    jdbc.username=root
    jdbc.password=123456
    

mybatis-config.xml

  • mybatis-config.xml:mybatis配置文件

    • 定义关于数据库表和对应的java的pojo类 字段
    • 起别名
    • dao接口的绑定、dao接口实现类的绑定
    <?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>
    
        <typeAliases>
            <package name="com.AL.pojo"/>
        </typeAliases>
    
        <mappers>
            <mapper resource="com/AL/dao/BookMapper.xml"/>
        </mappers>
    
    </configuration>
    
  • 原来学习的:mybatis-config.xml配置文件

    对于mybatis学习中更加完整的一个: 因为直接接触dao层了吗?所以直接添加了数据库的环境配置信息 enviroments。

    <?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">
    <!--XML 配置文件或者预先定义的一个 config,去SqlSessionFactoryBuilder, 配置数据库,便于创建sqlSessionFactory实例-->
    <configuration>
    
    <!--    <properties resource="db.properties">-->
    <!--        &lt;!&ndash;  在 db.properties  属性配置文件中定义了这些 sql 配置,可以不在这里的xml配置文件中定义。-->
    <!--        如果定义的话, 会以 xml资源配置文件中的 (引入外部配置文件) 为基准,优先级比较高-->
    <!--        <property name="username" value="root"/>-->
    <!--        <property name="pwd" value="111"/>-->
    <!--        &ndash;&gt;-->
    <!--    </properties>-->
        <properties resource="db.properties" />
    
        <!--可以给实体类起别名-->
        <typeAliases>
            <typeAlias type="com.AL.pojo.User" alias="User"/>
        </typeAliases>
        <!--    <typeAliases>
            <package name="com.AL.pojo"/>
        </typeAliases>-->
    
        <environments default="development">  <!-- 定义默认的环境配置,使用default选择环境 development-->
            <environment id="development">
                <transactionManager type="JDBC"/> <!--事务管理器-->
                <dataSource type="POOLED"> <!--数据源、默认的连接池-->
    <!--                <property name="driver" value="com.mysql.jdbc.Driver"/>-->
    <!--                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/>-->
    <!--                <property name="username" value="root"/>-->
    <!--                <property name="password" value="123456"/>-->
    
                    <property name="driver" value="${driver}"/>
                    <property name="url" value="${url}"/>
    <!--                <property name="url" value="jdbc:mysql://localhost:3306/jdbc"/>-->
                    <property name="username" value="${username}"/>
                    <property name="password" value="${password}"/>
                </dataSource>
            </environment>
    
            <environment id="test">
                <transactionManager type="JDBC"/> <!--事务管理器-->
                <dataSource type="POOLED"> <!--默认的连接池-->
                    <property name="driver" value="com.mysql.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;charsetEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="123456"/>
                </dataSource>
            </environment>
        </environments>
        <!--关联映射文件-->
        <mappers>
            <mapper resource="com/AL/dao/UserMapper.xml"/>
             <!--绑定接口-->
            <mapper class="com.AL.dao.UserMapper"/>
        </mappers>
    </configuration>
    
    

spring-dao.xml:spring整合mybatis

  • spring-dao.xml层:和dao层 底层数据库连接操作的配置文件

    • spring配置整合mybatis。此时的配置文件包括的内容有:
      1. 关联数据库文件,连接数据库表
      2. 配置数据库连接池。例如dbcp、c3p0的。 可以直接从数据库表的配置文件中读取 value=“${jdbc.driver}”
      3. 配置SqlSessionFactory对象。利用SqlSessionFactory去创建一个SqlSession 用于去完成对dao层数据库的操作
      4. 注入sqlSessionFactory 以及 注入 Dao接口
    • 原来的mybatis-config,xml 配置文件,mybatis中需要从资源文件利用SqlSessionFactoryBuild来得到SqlSessionFactory实例化对象,使用SqlSessionFactory来新建sqlsession。【创建一个实体工具类,然后去获得一个SqlSession,在spring中,spring层把这部分的工作完成了】
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 配置整合mybatis -->
        <!-- 1.关联数据库文件 -->
        <context:property-placeholder location="classpath:database.properties"/>
    
        <!-- 2.数据库连接池 -->
        <!--数据库连接池
            dbcp 半自动化操作 不能自动连接
            c3p0 自动化操作(自动的加载配置文件 并且设置到对象里面)
        -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <!-- 配置连接池属性 -->
            <property name="driverClass" value="${jdbc.driver}"/>
            <property name="jdbcUrl" value="${jdbc.url}"/>
            <property name="user" value="${jdbc.username}"/>
            <property name="password" value="${jdbc.password}"/>
    
            <!-- c3p0连接池的私有属性 -->
            <property name="maxPoolSize" value="30"/>
            <property name="minPoolSize" value="10"/>
            <!-- 关闭连接后不自动commit -->
            <property name="autoCommitOnClose" value="false"/>
            <!-- 获取连接超时时间 -->
            <property name="checkoutTimeout" value="10000"/>
            <!-- 当获取连接失败重试次数 -->
            <property name="acquireRetryAttempts" value="2"/>
        </bean>
    
        <!-- 3.配置SqlSessionFactory对象 -->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <!-- 注入数据库连接池 -->
            <property name="dataSource" ref="dataSource"/>
            <!-- 配置MyBatis全局配置文件:mybatis-config.xml -->
            <property name="configLocation" value="classpath:mybatis-config.xml"/>
        </bean>
    
        <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
        <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!-- 注入sqlSessionFactory -->
            <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
            <!-- 给出需要扫描Dao接口包 -->
            <property name="basePackage" value="com.AL.dao"/>
        </bean>
    
    </beans>
    
  • 对于原来学习的:单独的一个配置,联合 mybatis的xml文件。

    spring-10mybatis这个maven项目中的spring-dao.xml文件内容如下:

    • 原来的mybatis-config,xml 配置文件,mybatis中需要从资源文件利用SqlSessionFactoryBuild来得到SqlSessionFactory实例化对象,使用SqlSessionFactory来新建sqlsession。【创建一个实体工具类,然后去获得一个SqlSession,在spring中,spring层把这部分的工作完成了】
    • 使用了sqlSessionTemplate 去创建一个 sqlSession
    • 另外一种,利用SqlSessionDaoSupport 类 去创建得到的sqlSession
    <?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: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/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">
    
        <!--配置数据源:数据源有非常多,可以使用第三方的, 如: c3p0  dbcp  druid
        也可使使用Spring的:org.springframework.jdbc.datasource
        -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </bean>
    
        <!--配置SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--关联Mybatis-->
            <property name="configLocation" value="classpath:mybatis -config.xml"/>
            <property name="mapperLocations" value="classpath:com/AL/mapper/*.xml"/>
        </bean>
    
        <!--注册sqlSessionTemplate: 就是我们使用的 sqlSession. 关联sqlSessionFactory-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--利用构造器注入 sqlSessionFactory, 因为它没有 set 方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
        <bean id="userMapper" class="com.AL.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    
        <bean id="userMapper2" class="com.AL.mapper.UserMapperImpl2">
            <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        </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="add" propagation="REQUIRED"/>
                <tx:method name="delete" propagation="REQUIRED"/>
                <tx:method name="update" propagation="REQUIRED"/>
                <tx:method name="search*" propagation="REQUIRED"/>
                <tx:method name="get" read-only="true"/>
                <tx:method name="*" propagation="REQUIRED"/>
            </tx:attributes>
        </tx:advice>
    
        <!--配置aop织入事务-->
        <aop:config>
            <aop:pointcut id="txPointcut" expression="execution(* com.AL.dao.*.*(..))"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
        </aop:config>
    </beans>
    

两个spring-dao.xml文件的比较

  1. <!-- 3.配置SqlSessionFactory对象 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBatis全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>
    
    <!-- 4.配置扫描Dao接口包,动态实现Dao接口注入到spring容器中 -->
    <!--解释 :https://www.cnblogs.com/jpfss/p/7799806.html-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 给出需要扫描Dao接口包 -->
        <property name="basePackage" value="com.AL.dao"/>
    </bean>
    
  2. <!--配置SqlSessionFactory-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <!--关联Mybatis-->
            <property name="configLocation" value="classpath:mybatis -config.xml"/>
            <property name="mapperLocations" value="classpath:com/AL/mapper/*.xml"/>
        </bean>
    
        <!--注册sqlSessionTemplate: 就是我们使用的 sqlSession. 关联sqlSessionFactory-->
        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
            <!--利用构造器注入 sqlSessionFactory, 因为它没有 set 方法-->
            <constructor-arg index="0" ref="sqlSessionFactory"/>
        </bean>
    
        <bean id="userMapper" class="com.AL.mapper.UserMapperImpl">
            <property name="sqlSession" ref="sqlSession"/>
        </bean>
    
        <bean id="userMapper2" class="com.AL.mapper.UserMapperImpl2">
            <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        </bean>
    

其实这两个配置文件的作用是一样的。 都要使用SqlSessionFactory 去新建一个sqlSession,完成dao层接口、接口实现类的工作,对数据库操作。

Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring

Mybatis在与Spring集成的时候可以配置MapperFactoryBean来生成Mapper接口的代理. 例如

<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>

MapperFactoryBean 创建的代理类实现了 UserMapper 接口,并且注入到应用程序中。 因为代理创建在运行时环境中(Runtime,译者注) ,那么指定的映射器必须是一个接口,而 不是一个具体的实现类。

上面的配置有一个很大的缺点,就是系统有很多的配置文件时 全部需要手动编写,所以上述的方式已经很用了。

没有必要在 Spring 的 XML 配置文件中注册所有的映射器。相反,你可以使用一个 MapperScannerConfigurer , 它 将 会 查 找 类 路 径 下 的 映 射 器 并 自 动 将 它 们 创 建 成 MapperFactoryBean。

要创建 MapperScannerConfigurer,可以在 Spring 的配置中添加如下代码:

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>

basePackage 属性是让你为映射器接口文件设置基本的包路径。 你可以使用分号或逗号 作为分隔符设置多于一个的包路径。每个映射器将会在指定的包路径中递归地被搜索到。

注 意 , 没 有 必 要 去 指 定 SqlSessionFactory 或 SqlSessionTemplate , 因 为 MapperScannerConfigurer 将会创建 MapperFactoryBean,之后自动装配。但是,如果你使 用了一个 以上的 DataSource ,那 么自动 装配可 能会失效 。这种 情况下 ,你可 以使用 sqlSessionFactoryBeanName 或 sqlSessionTemplateBeanName 属性来设置正确的 bean 名 称来使用。这就是它如何来配置的,注意 bean 的名称是必须的,而不是 bean 的引用,因 此,value 属性在这里替代通常的 ref:

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

MapperScannerConfigurer 支 持 过 滤 由 指 定 的 创 建 接 口 或 注 解 创 建 映 射 器 。 annotationClass 属性指定了要寻找的注解名称。 markerInterface 属性指定了要寻找的父 接口。如果两者都被指定了,加入到接口中的映射器会匹配两种标准。默认情况下,这两个 属性都是 null,所以在基包中给定的所有接口可以作为映射器加载。

被发现的映射器将会使用 Spring 对自动侦测组件(参考 Spring 手册的 3.14.4)默认的命 名策略来命名。也就是说,如果没有发现注解,它就会使用映射器的非大写的非完全限定类 名。但是如果发现了@Component 或 JSR-330 的@Named 注解,它会获取名称。注意你可以 配 置 到 org.springframework.stereotype.Component , javax.inject.Named(如果你使用 JSE 6 的话)或你自己的注解(肯定是自我注解)中,这 样注解将会用作生成器和名称提供器。

接下来让我们看一下MapperScannerConfigurer类的源码 看看是如何自动扫描的。

 1 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
 2     if (this.processPropertyPlaceHolders) {
 3       processPropertyPlaceHolders();
 4     }
 5 
 6     ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
 7     scanner.setAddToConfig(this.addToConfig);
 8     scanner.setAnnotationClass(this.annotationClass);
 9     scanner.setMarkerInterface(this.markerInterface);
10     scanner.setSqlSessionFactory(this.sqlSessionFactory);
11     scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
12     scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
13     scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
14     scanner.setResourceLoader(this.applicationContext);
15     scanner.setBeanNameGenerator(this.nameGenerator);
16     scanner.registerFilters();
17     scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
18   }

把Mapper接口转换成MapperFactoryBean的代码在地17行这个方法里,让我们跟踪进去看一下。

 1   @Override
 2   public Set<BeanDefinitionHolder> doScan(String... basePackages) {
 3     Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
 4 
 5     if (beanDefinitions.isEmpty()) {
 6       logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
 7     } else {
 8       for (BeanDefinitionHolder holder : beanDefinitions) {
 9         GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
10 
11         if (logger.isDebugEnabled()) {
12           logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
13               + "' and '" + definition.getBeanClassName() + "' mapperInterface");
14         }
15 
16         // the mapper interface is the original class of the bean
17         // but, the actual class of the bean is MapperFactoryBean
18         //把接口的类型设置进去
19         definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
20         //设置Bean的真实类型MapperFactoryBean 
21         definition.setBeanClass(MapperFactoryBean.class);
22         //是否把Mapper接口加入到Mybatis的Config当中去
23         definition.getPropertyValues().add("addToConfig", this.addToConfig);
24 
25         boolean explicitFactoryUsed = false;
26         //如果sqlSessionFactoryBeanName的名字不为空 则在Spring容器中查询
27         //适合多数据源
28         if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
29           definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
30           explicitFactoryUsed = true;
31         } else if (this.sqlSessionFactory != null) {
32           definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
33           explicitFactoryUsed = true;
34         }
35 
36         //如果sqlSessionTemplateBeanName的名字不为空 则在Spring容器中查询
37         //适合多数据源
38         if (StringUtils.ha
39         if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
40           if (explicitFactoryUsed) {
41             logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
42           }
43           definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
44           explicitFactoryUsed = true;
45         } else if (this.sqlSessionTemplate != null) {
46           if (explicitFactoryUsed) {
47             logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
48           }
49           definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
50           explicitFactoryUsed = true;
51         }
52 
53         if (!explicitFactoryUsed) {
54           if (logger.isDebugEnabled()) {
55             logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
56           }
57           definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
58         }
59       }
60     }
61     //这个集合返回以后 Spring容器会将里面的所有内容注册到容器中
62     return beanDefinitions;
63   }

spring-service.xml

spring-service.xml配置文件,关于service层的:

  • 将 service中的相关bean注册到spring容器中
  • service层中的bean注入。使用了set注入方式:
  • 事务管理
<?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">

    <!-- 扫描service相关的bean -->
    <context:component-scan base-package="com.AL.service"/>

    <!--BookServiceImpl注入到IOC容器中-->
    <bean id="BookServiceImpl" class="com.AL.service.BookServiceImpl">
        <property name="bookMapper" ref="bookMapper"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

</beans>

在上述的spring中相关的几个xml配置可以看出,将dao层、service层里面的bean都注入到了IOC容器中,到时需要的时候直接从 spring容器中获取即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值