spring 在jsp循环controller传来的数据_手把手教会你使用Spring框架(三)

7872763849386cecd3da5fb18fa00d24.png

>>>Spring框架完整视频教程<<<

>>> 学习交流群 < < <

一、Spring整合Mybatis

1. 使用Servlet+MVC+Mybatis+Jsp完成用户登录

  1. 在mysql中创建t_user用户信息表(id,name,pwd).
  2. 使用idea中使用Maven搭建一个基本的war类型的项目 项目名称为 01-springioc-mybatis-login
  3. 在web项目中配置SpringIOC+Mybatis+Jdbc的依赖
  4. 在web项目中创建login.jsp登录页面,并完善发送登录请求
  5. 在web项目中创建UserServlet,并完善代码处理登录请求
  6. 在web项目中创建UserService业务,并完善代码处理登录业务
  7. 在web项目中创建UserMapper,并完善代码根据用户名和密码 查询用户信息

2. service层使用Spring获取mapper对象

问题:

目前我们开发功能的流程中,在service层会手动创建SQLSession对象,并使用SQLSession对象获取Mapper接口的实例化对象,但是我们真正使用的是Mapper接口的对象,目前的代码编写方式极大的影响了开发效率,而且mybatis层和service 层之间的耦合性非常高

解决:

使用SpringIOC技术实现service层和mybatis层的解耦:说白了就是让Spring容器帮我们获取Mapper层接口的实例化 对象,我们直接从Spring容器中获取使用即可.

实现:

① 配置SpringIOC的依赖以及Spring整合Mybatis的依赖

② 在src下创建并配置Spring的配置文件

  • 配置DataSource数据源的bean
  • 配置SQLSessionFactory的工厂bean(DataSource的bean)
  • 配置mapper扫描bean对象(SQLSessionFactory对象,扫 描路径)

③ 创建Spring容器对象,并获取Mapper接口的实例化对象完成数据库操作

3. controller层使用Spring解耦service层

问题:

在业务层使用Spring容器对象获取Mapper接口实例化对象后实现了service层和mybatis层的解耦,但是在controller层我们依然在Servlet 中直接创建Service对象,耦合性过高.

解决:

将service对象配置为bean对象,Servlet中从Spring容器中获取Service对象,完成功能开发.

实现:

  1. 在applicationcontext.xml文件中配置service的bean
  2. 在servlet中的service方法中创建Spring容器对象
  3. 在servlet中的service方法中从Spring容器中获取 service对象
  4. 使用service对象完成功能处理

4. MVC项目中使用SpringIOC解耦的代码优化

问题:

目前的整合中,tomcat服务在接收到请求后,会创建一个线程来处理请求,每个线程之间都是相互独立运行.流程如下:

  1. 浏览器发起登录请求
  2. tomcat服务器获取请求,创建新的线程处理请求
  3. 调用请求的Servlet对象中的service方法
  4. Servlet中的service方法被执行,service方法进栈.然后创建Spring容器对象,内 存堆中就会出现一个Spring容器对象s1,而s1被创建的时候,会加载spring的配 置文件,根据配置文件创建对象(dataSource,factory,mapper,us.......).
  5. 获取Spring容器中的业务层对象us
  6. 调用us对象的登录业务方法
  7. 登录业务方法进栈,又会创建一个新的Spring容器对象s2,而s2被创建的时候,又 加载解析Spring的配置文件,又创建dataSource,factory,us,mapper的bean对 象.
  8. 在业务的登录方法中获取s2容器对象中的mapper对象完成 数据库操作,执行 业务处理,登录业务方法处理完毕,返回结果 给controller的service方法,然后 业务方法出栈.
  9. servlet的service方法获取到业务层的返回值后,继续处理,然后响应处理结果给浏 览器,service方法结束,service方法出栈,请求处理完毕,线程销毁.

通过以上流程发现,目前的代码的结构,一个线程中会存在除Servlet对象以外至少还有10个对象,一旦高并发访问,会造成内存资源的极大的浪费,造成项目崩溃.

解决:

优化代码中的资源占比.

实现:

  1. 使用依赖注入的方式在Service对象中获取Mapper对象
  2. 在Servlet中使用init方法完成Spring容器对象的创建及其资源加载

5. 完整代码示例

5.1.创建Maven的war类型的项目并完成基础配置

5.2.在pom.xml文件中配置相关依赖

  • web的依赖
  • mybatis的依赖
  • jdbc的依赖
  • SpringIOC的依赖
  • Spring整合Mybatis的依赖
  • spring-jdbc的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.bjsxt</groupId>
  <artifactId>03_SpringIOC_Mybatis_Login</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>
  <!--配置版本号-->
  <properties>
    <servlet-version>3.1.0</servlet-version>
    <jsp-version>2.2</jsp-version>
    <jstl-version>1.2</jstl-version>
    <mybatis.version>3.5.2</mybatis.version>
  </properties>
  <!--配置依赖-->
  <dependencies>
    <!--配置Mybatis的依赖-->
    <dependency>
      <!--资源坐标-->
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>${mybatis.version}</version>
    </dependency>
    <!--配置JDBC的依赖-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.23</version>
    </dependency>
    <!--Spring整合Mybatis的依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.2</version>
    </dependency>
    <!--配置SpringIOC的依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.1.11.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.11.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.1.3</version>
    </dependency>
    <!--配置web开发相关的依赖-->
    <!--servlet的资源坐标-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet-version}</version>
      <scope>provided</scope>
    </dependency>
    <!--jsp的资源坐标-->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>${jsp-version}</version>
      <scope>provided</scope>
    </dependency>
    <!--jstl的资源坐标-->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>${jstl-version}</version>
    </dependency>
  </dependencies>
  <!--配置tomcat插件-->
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8080</port><!--配置tomcat启动的端口号-->
          <path>/SMlogin</path><!--配置项目的访问名称-->
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

5.3.在resources下创建并配置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">
    <!--创建数据源bean对象-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/s105"></property>
        <property name="username" value="root"></property>
        <property name="password" value="1234"></property>
    </bean>
    <!--配置SqlSession的工厂bean-->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--依赖注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
        <!--设置实体类的路径,相当于mybatisx.xml文件中的typeAliases配置-->
        <property name="typeAliasesPackage" value="com.bjsxt.pojo"></property>
     </bean>
    <!--
        配置Mapper扫描
            Spring容器对象在被创建的时候会加载applicationcontext.xml文件
            并根据文件中的bean标签将所有的bean对象初始化创建:
            Spring容器中的对象:
                DataSource对象
                SqlSessionFactoryBean对象(DataSource对象)
                MapperScannerConfigurer对象(SqlSessionFactoryBean对象)
            Spring容器对象底层会自动调用MapperScannerConfigurer对象中的资源完成SqlSession对象的
            生产,以及使用生产的SqlSession对象完成mapper层接口的扫描,将动态生成的Mapper层接口的
            实例化对象存储到Spring容器中,默认mapper接口实例化的键名为接口名的首字母小写,例如:userMapper
            我们从Spring容器中直接获取Mapper接口对象并完成响应的数据库操作即可。
    -->
        <bean id="mapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <!--依赖注入factory-->
            <property name="sqlSessionFactory" ref="factory"></property>
            <!--配置mapper扫描路径-->
            <property name="basePackage" value="com.bjsxt.mapper"></property>
        </bean>
    <!--配置业务层的bean对象-->
        <bean id="us" class="com.bjsxt.service.impl.UserServiceImpl">
            <property name="userMapper" ref="userMapper"></property>
        </bean>
</beans>

5.4.声明登录的jsp页面

<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <base href="<%=basePath %>"/>
    <title>Title</title>
</head>
<body>
    <h3>Spring整合Mybatis登录案例</h3>
    <hr>
    <c:if test="${sessionScope.flag=='loginFail'}">
        <font color="red" size="20">用户名或密码错误</font>
    </c:if>
    <c:remove var="flag" scope="session"></c:remove>
    <form action="userServlet" method="post">
        用户名: <input type="text" name="uname" value=""><br>
        密码: <input type="password" name="pwd" value=""><br>
        <input type="submit" value="点击登录">
    </form>
</body>
</html>

5.5.声明controller层代码UserController

package com.bjsxt.controller;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
import com.bjsxt.service.impl.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet(value = "/userServlet",loadOnStartup = 1)
public class UserServlet extends HttpServlet {
    //声明业务层属性
    UserService us;

    /**在init方法中完成资源初始化,init方法在Servlet被初始化创建的时候执行,
     * 而Servlet只会被初始化创建一次,也就是init方法只会执行一次。
     * init的方法执行是先于service方法的执行的。也就说:
     *
     *  Servlet对象被初始化创建的时候,触发init方法的执行,而init方法的在被执行的时候
     *  会创建Spring容器对象,并从中获取到业务层的实例化对象,赋值给Servlet的属性。
     *  init方法执行完毕出栈,然后Spring容器对象及Spring容器对象创建的其他不相关的对象
     *  都会被销毁。用户发起请求,请求Servlet时,Servlet调用service方法,service方法中
     *  调用业务层属性完成业务处理。
     *
     *
    @Override
    public void init() throws ServletException {
        //获取Spring容器对象
        ApplicationContext ac=new ClassPathXmlApplicationContext("applicationcontext.xml");
        //获取业务层对象
        us= (UserService) ac.getBean("us");
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置请求编码格式
            req.setCharacterEncoding("utf-8");
        //设置响应编码格式
            resp.setContentType("text/html;charset=utf-8");
            resp.setCharacterEncoding("utf-8");
        //获取请求信息
            String uname=req.getParameter("uname");
            String pwd=req.getParameter("pwd");
        //处理请求
            //登录处理
            User user = us.userLoginService(uname, pwd);
        //响应结果
            HttpSession session = req.getSession();
            if(user!=null){
                //登录成功 重定向到主页面
                session.setAttribute("user",user);
                resp.sendRedirect(req.getContextPath()+"/main.jsp");
            }else{
                //登录失败
                session.setAttribute("flag","loginFail");
                resp.sendRedirect(req.getContextPath()+"/login.jsp");
            }
    }
}

5.6.声明业务接口及其实现类

  • UserService接口
package com.bjsxt.service;
import com.bjsxt.pojo.User;
import java.io.IOException;
public interface UserService {
    //用户登录
    User userLoginService(String uname, String pwd);
}
  • UserServiceImpl实现类
package com.bjsxt.service.impl;
import com.bjsxt.mapper.UserMapper;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
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.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
import java.io.InputStream;
public class UserServiceImpl implements UserService {
    //声明Mapper层属性
    private UserMapper userMapper;
    public UserMapper getUserMapper() {
        return userMapper;
    }
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    //用户登录
    @Override
    public User userLoginService(String uname, String pwd){
        //调用mapper方法根据用户名和密码获取用户信息
        User user = userMapper.userLoginMapper(uname, pwd);
        return user;
    }
}

5.7.声明pojo层

package com.bjsxt.pojo;
import java.util.Objects;
public class User {
    private Integer uid;
    private String uname;
    private String pwd;
    @Override
    public String toString() {
        return "User{" +
                "uid=" + uid +
                ", uname='" + uname + ''' +
                ", pwd='" + pwd + ''' +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(uid, user.uid) &&
                Objects.equals(uname, user.uname) &&
                Objects.equals(pwd, user.pwd);
    }
    @Override
    public int hashCode() {
        return Objects.hash(uid, uname, pwd);
    }
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    public String getUname() {
        return uname;
    }
    public void setUname(String uname) {
        this.uname = uname;
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
    public User() {
    }
    public User(Integer uid, String uname, String pwd) {
        this.uid = uid;
        this.uname = uname;
        this.pwd = pwd;
    }
}

5.8.声明mapper层代码

package com.bjsxt.mapper;
import com.bjsxt.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface UserMapper {
    //根据用户名和密码获取用户信息
    @Select("select * from t_user where uname=#{uname} and pwd=#{pwd}")
    User userLoginMapper(@Param("uname") String uname, @Param("pwd") String pwd);
}

二、SpringIOC的创建对象的单例多例模式和自动注入

1. SpringIOC创建对象的单例和多例模式

问题:

Spring容器对象根据配置文件创建对象的时机默认发生在Spring容器对象在被创建的时候,也就是说,我们一旦获取到Spring容器对象,意味着可以直接获取Spring容器中的对象使用了.那么,如果我对同一个bean对象,连续获取N次,获取到的是不是同一个对象呢?因为spring容器对象底层使用的是map集合存储的bean对象,对map集合按照同一个键名获取数据,获取的是同一个,也就说按照同一个键名从Spring容器中获取的都是同一个对象,那么如果我们希望相同的键名获取的对象每次都不一样,怎么实现?

解决:

不要在Spring容器对象创建的时候,就完成对象的初始化创建,而是变为,从Spring容器中获取的时候才创建,每次获取都重新创建.

实现:

Spring容器的单例和多例模式创建对象.

单例模式(默认):

设置了单例模式的bean,会在Spring容器对象被创建的时候就完成初始化创建,无论获取多少次都是同一个对象.

多例模式:

设置了多例模式的bean,在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">
    <!--
    SpringIOC设置对象的单例模式和多例模式创建对象
    单例模式:默认模式,在bean标签上使用scope属性,默认值为singleton
    多例模式:在bean标签上使用scope属性设置,值为prototype
    -->
    <bean id="stu" class="com.bjsxt.pojo.Student" scope="singleton"></bean>
    <bean id="tea" class="com.bjsxt.pojo.Teacher" scope="prototype"></bean>
</beans>
  • TestIocModel
package com.bjsxt.controller;
import com.bjsxt.pojo.User;
import com.bjsxt.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.rmi.CORBA.StubDelegate;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * @author wuyw2020
 * @date 2020/1/8 20:55
 */
@WebServlet(value = "/user", loadOnStartup = 1)
public class UserServlet extends HttpServlet {
    public static void main(String[] args) {
        //获取Spring容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationcontext.xml");
        Student stu =(Student) ac.getBean("stu");
        Student stu2 =(Student) ac.getBean("stu");
        Teacher teacher = (Teacher) ac.getBean("stu");
        Teacher teacher2 = (Teacher) ac.getBean("stu");
        System.out.println("学生:"+(stu==stu2));
        System.out.println("教室:"+(teacher==teacher2));
    }
}

2. SpringIOC的自动注入

问题:

在学习了SpringIOC的DI依赖注入后,我们可以根据对象之间的依赖关系的链,让Spring容器对象帮我们创建有一个组装好的对象,比如A中有B,B中有C,C中有D.将A,B ,C,D都创建为Bean对象,然后使用ref属性告诉Spring对象之间的依赖关系的组装规则,假如依赖责任链特别长,使用ref注入就会很麻烦很麻烦.怎么办?

解决:

不要声明ref属性来指明依赖关系的注入,只需要告诉Spring容器对象依赖关 系的注入规则,Spring容器对象自动根据规则完成依赖关系的注入.

实现:

  1. 根据bean的ID和属性名一致的规则
  2. 根据bean的类型和属性的类型一致的规则
  3. 根据构造器形参的类型和bean的类型一致的规则
  4. 不能使用自动注入,只能手动声明
  5. 使用默认规则
<?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"
       default-autowire="byName"><!--设置自动注入-->
    <!--
       自动注入:
       在标签上使用autowire属性设置自动注入规则.该属性声明在要注入资源的bean标签上,比如:学生的bean
         autowire属性的值:
           byName:按照属性名和某个bean的id相同的规则自动注入
           byType:按照属性的类型和某个bean的类型相同的规则自动注入.
               注意:同类型的bean只能有一个,否则报错.
           constructor:
               必须声明对应的构造器,根据构造器形参的类型和某个bean的类型相同的规则自动注入.
           no:不使用自动注入,必须使用ref手动注入.
           default:使用默认注入规则.默认值.
           注意:
                可以在beans顶层元标记中使用属性default-autowire声明全局自动注入规则.
   -->
    <bean id="stu" class="com.bjsxt.pojo.Student" autowire="byName">
        <!--用构造器的方法,此处要创建一个只有Teacher一个参数的构造器-->
        <!--<constructor-arg index="0" type="com.Teacher"  ref="tea"></constructor-arg>-->
        <property name="sid" value="1"></property>
        <property name="sname" value="张三"></property>
    </bean>
    <bean id="teacher" class="com.bjsxt.pojo.Teacher">
        <property name="tname" value="liu"></property>
        <property name="tid" value="1"></property>
    </bean>
</beans>

有什么疑问或者不懂的地方进群讨论哦,欢迎大家一起学习~

>>>学习交流群< < <课件,视频,项目,源码,你想要的这里都有

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值