Spring MVC、Spring、MyBatis框架学习过程中(不具体到某个框架),所遇到的问题(把遇到的问题描述清楚)、该问题是如何解决的(写清楚解决的步骤)、以及解决了问题后对你有什么启发。

发布于:2023-01-10 ⋅ 阅读:(563) ⋅ 点赞:(0)

Spring中常遇到的问题

什么是spring

Spring是分层的Java SE/EE的full-stack轻量级开源框架,以IOC(Inverse of Controll,反转控制)和AOP(Aspect Oriented Programming,面向切面编程)为内核,提供了表示层和持久层以及业务层事务管理等众多企业级开发技术,还能整合开源界诸多著名的第三方框架和类库,逐渐成为使用最多的企业级Java EE开发的首选开源框架。
 1. Spring是如何对XXXService进行依赖注入的
 概念:程序在运行时,希望给某个对象所依赖的对象赋值,就称作依赖注入(需要什么,传递什么,传递的任务由Spring完成

依赖注入的方式  依赖注入的类型 
setter方法注入 基本数据类型和spring类型 
构造方法注入 普通POJO类型 
注解注入    普通POJO类型 

创建对应的Service接口和实现类
 

public interface AccountService 
{
    public Account findAccountById(Integer id) throws Exception;
    public void transfer(Integer sourceId,Integer targetId,float money) throws Exception;
}



 2. 注解的AOP配置遇到的异常
 异常后需要执行的方法
 

package edu.xync.spring.util;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/*
 * 一个用于记录日志的类
 */
@Component("logger")
@Aspect//表示它是一个切面 
public class Logger {
    
    //切入点表达式
    @Pointcut("execution(* edu.xync.spring.service.*.*(..))")
    public void pt1()
    {   }
    //前置通知
    public void beforePrintLog()
    {
        System.out.println("前置:Logger中的beforePrintLog()方法开始记录日志了------------");
    }
    //后置通知
    public void afterReturningPrintLog()
    {
        System.out.println("后置:Logger中的afterReturningPrintLog()方法开始记录日志了------------");
    }
    //异常通知:异常发生后需要执行的方法
    public void afterThrowingPrintLog()
     {
            System.out.println("异常:Logger中的afterThrowingPrintLog()方法开始记录日志了------------");
     }
    //最终通知:无论异常是否发生都会执行的方法
    public void afterPrintLog()
     {
        System.out.println("最终:Logger中的afterPrintLog()方法开始记录日志了------------");
     }
    @Around("pt1()")
    public Object aroundPrintLog(ProceedingJoinPoint pjp)
    {
        Object rtValue=null;
        try 
        {
            this.beforePrintLog();
            rtValue=pjp.proceed();
            this.afterReturningPrintLog();
        } 
        catch (Throwable e) {
            
            e.printStackTrace();
            this.afterThrowingPrintLog();
        }
        finally
        {
            this.afterPrintLog();
        }
        return rtValue;
    }
}


3. Spring的声明式事务管理
事务的四大特性分别是:原子性、一致性、隔离性、持久性。

事务的隔离级别反应事务提交并发时的处理态度:

 (1) READ UNCOMMITTED(读未提交数据):  允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。
(2)READ COMMITTED(读已提交数据):只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。 
(3)REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。
(4)SERIALIZABLE(序列化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。

 int ISOLATION_DEFAULT = -1;   默认采用数据库的隔离级
int ISOLATION_READ_UNCOMMITTED 
= Connection.TRANSACTION_READ_UNCOMMITTED;       
int ISOLATION_READ_COMMITTED  (Oracle的默认隔离级别)
= Connection.TRANSACTION_READ_COMMITTED;       
int ISOLATION_REPEATABLE_READ  (MySQL的默认隔离级别)
= Connection.TRANSACTION_REPEATABLE_READ;     
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;


 
4. Spring 事务中哪几种事务传播行为?


事务传播行为是为了解决业务层方法之间互相调用的事务问题。
TransactionDefinition.PROPAGATION_REQUIRED:
       如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_REQUIRES_NEW:
       创建一个新的事务,如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NESTED:
       如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价
TransactionDefinition.PROPAGATION_MANDATORY :
       如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

 PROPAGATION_REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 (增、删、改操作使用这种方式)
PROPAGATION_SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。(查询操作使用这种方式)
 PROPAGATION_MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
 PROPAGATION_REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
 PROPAGATION_NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。

事务配置环境准备

 

 4. static方法和单例实例方法在内存中有什么区别,分别在什么情况下使用?
 
  (1)静态方法在类加载的时候就创建了,实例方法当对象实例化的时候才去占用内存   
  (2)static方法区占用内存区域有限(连续占用内存区域,执行性能稍高),太多会影响性能

  


什末是Spring MVC


Spring MVC是Spring框架的一个模块,与Spring无需通过中间层整合。Spring MVC主要用来进行Web开发

Spring MVC框架执行流程与核心组件

 1.Spring MVC的异常处理:将异常抛给Spring框架,由Spring框架来处理;我们只需要配置简单的异常处理器,在异常处理器中添加视图页面即可。


调试程序如果发现异常: NoSuchMethodError:javax.servlet.http.HttpServletResponse.getStatus()I
  原因是:spring版本过高,当前的Tomcat不支持。
  解决方案:更换较高版本的JDK和Tomcat。


2.  Spring MVC的全局异常处理机制
异常处理思路
系统中异常包括两类:非运行异常和运行时异(RuntimeException)。前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的方法。
系统的DAO、Service、Controller出现都通过throws Exception向上抛出,最后由spring mvc前段控制器交由异常处理器进行异常处理。如下图。


3. 创建全局异常处理器
 Spring MVC提供了全局异常处理器,进行统一异常处理。

 

具体实现:
(1) 创建自定义异常,继承Exception。

public class UserInfoException extends Exception
{
     private String message;

    public UserInfoException(String message) 
    {
        super(message);
        this.message = message;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }    
}  


(2)  定义全局异常处理器。
   定义全局异常处理器。
   处理的思路:系统遇到异常,在程序中手动抛出,DAO抛给Service、Service抛给Controller、controller抛给前段控制器,前段控制器调用全局异常处理器。
   全局异常处理器解析出异常类型,如果该类型异常是系统自定义的异常,直接取出异常信息,在错误页面显示。如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)。
定义全局异常处理器实现HandlerExceptionResover接口,实现上述思路。

public class GlobalExceptionHandler implements HandlerExceptionResolver
{
    @Override
    public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
            Exception arg3) 
    {
         UserInfoException userInfoException=null;
         if(arg3 instanceof UserInfoException)
             userInfoException=(UserInfoException)arg3;
         else
             userInfoException=new UserInfoException("未知的错误,请联系系统管理员");
         
         String message=userInfoException.getMessage();
         
         ModelAndView mv=new ModelAndView();
         mv.addObject("errorMessage",message);
         mv.setViewName("error");
        return mv;
    }
}


(3) 创建错误显示页面(error.jsp),显示异常具体信息。
    
 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>出错了</title>
</head>
<body>
    错误信息:<p>${errorMessage}</p>
</body>
</html>

什么是MyBatis

MyBatis是一个持久层框架,是apache基金会下的一个顶级项目。利用原生态JDBC,对数据进性基本的增、删、改、查

1.MyBatis的动态sqlMyBatis动态sql可以让我们在xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。

2.原生态JDBC进行数据库操作存在的问题

(1)数据库连接对象(Connection)使用时就创建,不使用时立即释放,对数据库进行频繁的连接对象创建和关闭,造成数据库资源浪费,影响数据库性能。

   解决方案:采用数据库连接池管理数据库连接对象

(2)将sql语句硬编码到java代码中,如果sql语句修改,需要重新编译java代码,不利于系统维护。

   解决方案:将sql语句编写到xml配置文件中,即使sql语句发生变化,不需要编译java代码。

(3)向preparedStatement对象中设置参数,对占位符的参数设置需要硬编码到java代码中,不利于系统维护。

   解决方案:将sql、占位符以及参数都配置到xml文件中。

(4)从ResultSet中遍历结果集数据时,存在硬编码,对行记录进行硬编码(字段与对象属性进行映射),不利于系统维护。

   解决方案:将查询结果集自动映射成java对象。

3.MyBatis中的接口绑定方式
通过注解绑定:在接口的方法上加上 @Select、@Update等注解里面包含Sql语句来绑定
通过xml文件绑定:指定xml映射文件中的namespace为接口的全路径名

4.mybatis的核心配置文件–mybatis-config.xml(数据库连接)

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

5.Mybatis是如何进行分页的?分页插件的原理是什么?


-  Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页。可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。
- 分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。


6.Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?


1. 第一种是使用<resultMap>标签,逐一定义数据库列名和对象属性名之间的映射关系。
2. 第二种是使用sql列的别名功能,将列的别名书写为对象属性名。
- 有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。


7.如何执行批量插入?
MyBatis的foreach标签

<insert id="insert" parameterType="list" >
    insert into user_word_importance_practice (user_id,word,create_time) 
    VALUES
    <foreach collection="list" item="wordImportance" separator=",">
        (#{wordImportance.userId},#{wordImportance.word},#{wordImportance.createTime})
    </foreach>
</insert> 

8. 什么是MyBatis的接口绑定?有哪些实现方式?


(1)接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。

(2)接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。

9.使用MyBatis的mapper接口调用时有哪些要求?


(1)Mapper接口方法名和mapper.xml中定义的每个sql的id相同;
(2)Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同;
(3)Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;
(4)Mapper.xml文件中的namespace即是mapper接口的类路径。
 

10.应用案例问题介绍:

(1)创建User实体类对应的映射文件,在config目录下新建一个mapper目录,在其中创建UserMapper.xml文件,修改namespace的名称。

<mapper namespace="edu.xync.pro.dao.mapper.UserMapper">

  

(2)创建User实体类对应的mapper接口,如下:

 package edu.xync.pro.dao.mapper;

import edu.xync.pro.entity.UserInfo;

public interface UserMapper

{

      //根据id查找对应的用户信息

       public UserInfo findUserById(Integer id) throws Exception;

}

(3)修改UserMapper.xml文件,添加如下内容,

 <select id="findUserById" parameterType="java.lang.Integer" resultType="edu.xync.pro.entity.UserInfo">

     select * from t_user where id=#{id}

</select>

需要注意parameterType的值与mapper方法的输入参数类型一致;resultType的值与方法返回值的类型一致。

(4)在SqlMapConfig.xml中引入对应的映射文件,

<mappers>

         <mapper resource="mapper/UserMapper.xml"/>

  </mappers>

 

(5)定义对应的测试类,代码如下:

 

 public class UserMapperTest

{

private SqlSessionFactory factory=null;



@Before

public void setUp() throws Exception

{

        //MyBatis主配置文件

          String resource="SqlMapConfig.xml";

          //得到配置文件流

          InputStream input=Resources.getResourceAsStream(resource);

              //创建会话工厂对象,传入MyBatis配置文件信息

         factory=new SqlSessionFactoryBuilder().build(input);

}

@Test

 public void findUserById() throws Exception

 {

         //创建SqlSession

         SqlSession session=factory.openSession();

         //通过SqlSession得到对应Mapper接口的代理对象

         UserMapper userMapper=session.getMapper(UserMapper.class);

         UserInfo user=userMapper.findUserById(3);

         System.out.println(user);

   }

}

需要注意的问题:

由于映射文件中statementparameterType配置只能有一个,所以Mapper接口中方法的参数只能有一个。由于DAO层的代码是被业务层调用的,业务层的方法参数不可能只有一个(考虑到业务层逻辑的扩展),所以当业务层调用DAO层的时候,需要将参数适当包装成POJO对象,去调用DAO层的代码。因此上,系统中需要定义适当的POJO类型对象,作为持久层方法的输入参数。

启示

1,sql写在XML文件中,便于统一管理和优化,解除sql和程序代码的耦合。提供映射标签,支持对象和和数据库ORM字段关系的映射,支持对象关系映射标签,支持对象关系的组建提供xml标签,支持编写动态sql

2.通过Spring的IOC特性,将对象之间的依赖关系交给了Spring控制,方便解耦,简化了开发。通过Spring的AOP特性,很容易实现事务,日志,权限的控制。提供了对其他优秀开源框架的集成支持。低侵入式。

3,SpringMVC是使用了MVC设计思想的轻量级web框架,对web层进行解耦,是的我们开发更简洁。与Spring无缝衔接。灵活的数据验证,格式化,数据绑定机制。

本文含有隐藏内容,请 开通VIP 后查看