1. 在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)

@

目录

在 Web 中应用 MyBatis ,同时使用 MVC 架构模式,以及对应的 ThreadLocal 事务控制。

实现功能,银行账户的转账功能,同时进行事务上的处理。

需求描述:

实际简单的转账操作:

数据库表的设计和准备数据

2. 实现步骤:

这里说明一下,开发可以

  • 从后往前
  • 也可以,从前往后

二者没有太大区别,你认为哪个方向更好编写,便按照哪个方向即可,我个人比较习惯从前往后,所以这里我就从前往后了。

1. 第一步:环境搭建

IDEA中创建Maven WEB应用

注意:这里的 Archetype : 选择org.apache.maven.archetypes:maven-archetype-webapp 不要选错了。

IDEA配置Tomcat,这里Tomcat使用10+版本。并部署应用到tomcat。

默认创建的maven web应用没有 java resources目录。

一般会自动添加上,如果没有的话,有两种手动添加上的方式:

  • 第一种就是:直接在 IDEA 当的 main 目录下,新建

  • 第二种修改:修改maven-archetype-webapp-1.4.jar中的配置文件

这里自动生成的:web.xml 文件的版本较低,内容有点不太合适,我们可以从 tomcat10 的样例文件中复制,然后修改

如下是:tomcat 10 当中的样例:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true"> </web-app>
  • 删除 index.jsp文件,因为我们这个项目不使用JSP。只使用 html。

  • 确定 pom.xml 文件中的打包方式是 war 包。

  • 引入相关依赖

    • 编译器版本修改为 17
    • 引入的依赖包括:mybatis,mysql驱动,junit,logback,servlet。

<?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.rainbowsea</groupId>
<artifactId>mybatis-004-web</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <name>mybatis-004-web Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency> <!-- mybatis 依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency> <!-- mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency> <!-- logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency> <!--servlet依赖-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies> <build>
<finalName>mybatis-004-web</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
  • 引入相关配置文件,放到resources目录下(全部放到类的根路径下)

    • mybatis-config.xml
    • AccountMapper.xml
    • logback.xml
    • jdbc.properties
  • logback.xml logbak 日志框架信息

  • <?xml version="1.0" encoding="UTF-8"?>
    
    <configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
    </encoder>
    </appender> <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/> <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
    </root> </configuration>
  • AccountMapper.xml SQl语句的映射文件

  • <?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先随意写一个-->
    <mapper namespace="account">
    <select id="selectByActno" resultType="com.rianbowsea.bank.pojo.Account">
    select * from t_act where actno = #{actno}
    </select>
    <!--#{pojo的属性名}--> <update id="updateByActno">
    update t_act set balance = #{balance} where actno = #{actno}
    </update> </mapper>
  • jdb.properties 数据库连接信息的配置文件

  • jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis
    jdbc.username=root
    jdbc.password=MySQL123
  • mybatis-config.xml 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>
    <!--resource , 一定是从类路径下开始查找资源-->
    <properties resource="jdbc.properties"></properties>
    <environments default="mybatis">
    <environment id="mybatis">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">
    <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>
    <mappers>
    <mapper resource="AccountMapper.xml"/>
    </mappers>
    </configuration>

2. 第二步:前端页面 index.html

在Tomcat当中 ,index.html 默认就是开始页面,主页的。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>银行账户转账</title>
</head>
<body>
<form action="/bank/transfer" method="post">
转出账户:<input type="text" name="fromActno"> <br>
转入账户:<input type="text" name="toActno"> <br>
转账金额:<input type="text" name="money"> <br>
<input type="submit" value="转账"> </form>
</body>
</html>

3. 第三步:创建pojo包、service包、dao包、web包、utils包,exceptions包

4. 第四步:编写 utils 包下的,获取 MyBatis,SqlSesion 连接的工具类

package com.rianbowsea.bank.utils;

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; public class SqlSessionUtil {
// 工具类的构造方法一般都是私有话化的
// 工具类中所有的方法都是静态的,直接类名即可调用,不需要 new 对象
// 为了防止new对象,构造方法私有化。 private SqlSessionUtil() { } private static SqlSessionFactory sessionFactory = null; // 静态代码块,类加载时执行
// SqlSessionUtil 工具类在进行第一次加载的时候,解析mybatis-config.xml 文件,创建SqlSessionFactory对象。
static {
// 获取到 SqlSessionFactoryBuilder 对象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // 获取到SqlSessionFactory 对象
// SQlsessionFactory对象,一个SqlSessionFactory对应一个 environment, 一个environment通常是一个数据库
try {
sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
} } // 全局的,服务器级别的,一个服务器当中定义一个即可
private static ThreadLocal<SqlSession> local = new ThreadLocal<>(); /**
* 获取会话对象
*
* @return SqlSession
*/
public static SqlSession openSession() {
// 先从 ThreadLocal 当中获取,获取到 SqlSession 对象
SqlSession sqlSession = local.get(); if (null == sqlSession) {
// ThreadLocat 没有就, 创建一个
sqlSession = sessionFactory.openSession();
// 同时将其设置到 ThreadLocal容器当中,将SqlSession对象绑定到当前线程上
local.set(sqlSession);
} return sqlSession;
} /**
* 关闭SqlSession 对象(从当前线程中移除SqlSession 对象)
* @param sqlSession
*/
public static void close(SqlSession sqlSession) {
if(sqlSession != null) {
// 1.先将其关闭
sqlSession.close();
// 2. 再将其当前线程移除ThreadLocal 当前线程外面,防止被其他线程拿到整个没用的线程
local.remove();
/*
注意:移除SqlSession 对象和当前线程的绑定关系
因为Tomcat 服务器是支持线程池的,也就是说,用过的先吃对象t1,可能下一I此还会使用整个t1(已经关闭,没用的)线程。
*/
}
}
}

5. 第五步:定义pojo类:Account

对于 pojo 当中的类,一定要有 set 和 get 方法,以及无参数构造方法,不然,大部分的框架是无法通过反射机制,进行操作的,从而出现错误的。

package com.rianbowsea.bank.pojo;

/**
* 账户类,封装账户数据
*/
public class Account {
private Long id;
private String actno;
private Double balance; public Account() {
} public Account(Long id, String actno, Double balance) {
this.id = id;
this.actno = actno;
this.balance = balance;
} @Override
public String toString() {
return "Account{" +
"id=" + id +
", actno='" + actno + '\'' +
", balance=" + balance +
'}';
} public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getActno() {
return actno;
} public void setActno(String actno) {
this.actno = actno;
} public Double getBalance() {
return balance;
} public void setBalance(Double balance) {
this.balance = balance;
}
}

6. 第六步:编写AccountDao接口,以及AccountDaoImpl实现类

分析dao中至少要提供几个方法,才能完成转账:

  • 转账前需要查询余额是否充足:selectByActno
  • 转账时要更新账户:update

AccountDao

package com.rianbowsea.bank.dao;

import com.rianbowsea.bank.pojo.Account;

/**
* 账户的DAO对象,负责t_act 表中数据的CRUD,一般一个表对应一个 DAO
* 强调以下,DAO对象中的任何一个方法和业务不挂钩,没有任何业务逻辑在里头
* DAo中的方法就是CRUD的,所以方法名大部分是:insertXxx,deletexxx,updatexxx,selectxxx
*/
public interface AccountDao { /**
* 根据账号查询账户信息
* @param actno 账号
* @return 账户信息
*/
Account selectActno(String actno); /**
* 更新账户信息
* @param account 被更新的账户信息
* @return 1表示更新成功,其他表示更新失败
*/
int updateByActno(Account account); }

AccountDaoImpl

package com.rianbowsea.bank.dao.impl;

import com.rianbowsea.bank.dao.AccountDao;
import com.rianbowsea.bank.pojo.Account;
import com.rianbowsea.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession; public class AccountDaoImpl implements AccountDao {
private SqlSession sqlSession = SqlSessionUtil.openSession(); @Override
public Account selectActno(String actno) {
Account account = (Account) sqlSession.selectOne("account.selectByActno", actno);
// 注意:事务的控制,都是放在业务层的,不是放在持久层DAo,更不放在utils工具层
//sqlSession.close();
return account;
} @Override
public int updateByActno(Account account) { int count = sqlSession.update("account.updateByActno", account);
// 注意:事务的控制,都是放在业务层的,不是放在持久层DAo,更不放在utils工具层
//sqlSession.commit(); // 提交数据
//sqlSession.close();
return count;
}
}

7. 第七步:AccountDaoImpl 中编写了mybatis 代码,需要编写SQL映射文件了

AccountMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace先随意写一个-->
<mapper namespace="account">
<select id="selectByActno" resultType="com.rianbowsea.bank.pojo.Account">
select * from t_act where actno = #{actno}
</select>
<!--#{pojo的属性名}--> <update id="updateByActno">
update t_act set balance = #{balance} where actno = #{actno}
</update> </mapper>

8. 第八步:编写AccountService接口以及AccountServiceImpl

AccountService

package com.rianbowsea.bank.service;

import com.rianbowsea.bank.exceptions.MoneyNotEnoughException;
import com.rianbowsea.bank.exceptions.TransferException; /**
* 注意: 业务类当中的业务方法的名字在起名字的时候,最好见名知意,能够体现出具体的业务是做什么的
* 账户业务类
*/
public interface AccountService { /**
* 账户转账业务
*
* @param fromActno 转出账户
* @param toActno 转入账户
* @param money 转账金额
*/
void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException; }

AccountServiceImpl

package com.rianbowsea.bank.service.impl;

import com.rianbowsea.bank.dao.AccountDao;
import com.rianbowsea.bank.dao.impl.AccountDaoImpl;
import com.rianbowsea.bank.exceptions.MoneyNotEnoughException;
import com.rianbowsea.bank.exceptions.TransferException;
import com.rianbowsea.bank.pojo.Account;
import com.rianbowsea.bank.service.AccountService;
import com.rianbowsea.bank.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession; public class AccountServiceImpl implements AccountService { private AccountDao accountDao = new AccountDaoImpl(); @Override
public void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException { // 添加事务控制代码
SqlSession sqlSession = SqlSessionUtil.openSession(); // 1.判断转出账户的金额是否充足(select)
Account fromAct = accountDao.selectActno(fromActno); if (fromAct.getBalance() < money) {
// 2.如果转出账户余额不足,提示用户
throw new MoneyNotEnoughException("对不起,余额不足");
} // 3. 如果转出账户余额充足,更新转出账户的余额(update)
// 先在内存当中修改
Account toACt = accountDao.selectActno(toActno);
toACt.setBalance(toACt.getBalance() + money);
fromAct.setBalance(fromAct.getBalance() - money); // 4. 更新转入账户的余额(update)
int count = accountDao.updateByActno(toACt);
count += accountDao.updateByActno(fromAct); // 模拟异常
//String s = null;
//s.toString(); if(count !=2) {
throw new TransferException("转账异常,未知原因");
} // 注意:事务的控制,都是放在业务层的,不是放在持久层DAo,更不放在utils工具层
sqlSession.commit(); // 提交数据
sqlSession.close();
} }

9. 第九步:编写 自定义 Exception 异常

MoneyNotEnoughException

package com.rianbowsea.bank.exceptions;

/**
* 余额不足异常
*/
public class MoneyNotEnoughException extends Exception{
public MoneyNotEnoughException() {
} public MoneyNotEnoughException(String message) {
super(message);
}
}

TransferException

package com.rianbowsea.bank.exceptions;

/**
* 转账异常
*/
public class TransferException extends Exception{
public TransferException() {
} public TransferException(String message) {
super(message);
}
}

10. 第十步:编写AccountController

AccountController

package com.rianbowsea.bank.web;

import com.rianbowsea.bank.exceptions.MoneyNotEnoughException;
import com.rianbowsea.bank.exceptions.TransferException;
import com.rianbowsea.bank.service.AccountService;
import com.rianbowsea.bank.service.impl.AccountServiceImpl;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/transfer")
public class AccountServlet extends HttpServlet {
// 为了让整个对象在其他方法中可以用,声明为实例变量
private AccountService accountService = new AccountServiceImpl(); @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException { // 获取表单数据
String fromActno = request.getParameter("fromActno");
String toActno = request.getParameter("toActno");
double money = Double.parseDouble(request.getParameter("money")); // 转账业务
try {
// 调用service的转账方法完成转账,(调用业务层)
accountService.transfer(fromActno,toActno,money);
// 程序能够走到这里,表示转账一定成功了
// 调用View完成展示结果
response.sendRedirect(request.getContextPath()+"/success.html");
} catch (MoneyNotEnoughException e) {
response.sendRedirect(request.getContextPath()+"/error.html");
throw new RuntimeException(e);
} catch (TransferException e) {
response.sendRedirect(request.getContextPath()+"/error2.html");
throw new RuntimeException(e);
} catch (NullPointerException e) {
response.sendRedirect(request.getContextPath()+"/error2.html");
throw new RuntimeException(e);
} }
}

11. 第十一步:运行测试:

首先测试,没有模拟异常,看是否可以转账成功。

模拟异常,看事务上的处理,是否成功,是否能成功回滚,是否会丢失数据

2.1 补充说明:事务上的处理

在之前的转账业务中,更新了两个账户,我们需要保证它们的同时成功或同时失败,这个时候就需要使用事务机制,在 transfer 方法开始执行时开启事务,直到两个更新都成功之后 ,为了保证service和dao中使用的SqlSession对象是同一个,可以将SqlSession对象存放到ThreadLocal当中。

注意:移除SqlSession 对象和当前线程的绑定关系

因为Tomcat 服务器是支持线程池的,也就是说,用过的先吃对象t1,可能下一I此还会使用整个t1(已经关闭,没用的)线程。

注意:事务的控制,都是放在业务层的,不是放在持久层DAo,更不放在utils工具层 .

3. MyBatis核心对象的作用域

3.1 SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

3.2 SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

3.3 SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
}

4. 总结:

  1. 为了保证 service 和 dao 中使用的SqlSession对象是同一个,可以将SqlSession对象存放到ThreadLocal当中。

  1. 注意:移除SqlSession 对象和当前线程的绑定关系

    因为Tomcat 服务器是支持线程池的,也就是说,用过的先吃对象t1,可能下一I此还会使用整个t1(已经关闭,没用的)线程。

  1. 注意:事务的控制,都是放在业务层的,不是放在持久层DAo,更不放在utils工具层 .

  2. SqlSessionFactoryBuilder: 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。

  3. SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。

  4. SqlSession 它的最佳的作用域是请求或方法作用域**。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 **也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。

5. 最后:

“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”

在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)的更多相关文章

  1. SpringMVC内容略多 有用 熟悉基于JSP和Servlet的Java Web开发,对Servlet和JSP的工作原理和生命周期有深入了解,熟练的使用JSTL和EL编写无脚本动态页面,有使用监听器、过滤器等Web组件以及MVC架构模式进行Java Web项目开发的经验。

    熟悉基于JSP和Servlet的Java Web开发,对Servlet和JSP的工作原理和生命周期有深入了解,熟练的使用JSTL和EL编写无脚本动态页面,有使用监听器.过滤器等Web组件以及MVC架构 ...

  2. MVC架构模式详细说明

    一.简介: 架构模式是一个通用的.可重用的解决方案,用于在给定上下文中的软件体系结构中经常出现的问题.架构模式与软件设计模式类似,但具有更广泛的范围. 模型-视图-控制器模式,也称为MVC模式(Mod ...

  3. MVC架构模式(转载)

    MVC架构模式 [概念理解] MVC模式和C++的实现 面向对象的设计模式是经验的总结,MVC思想是原先用于构建用户界面的.这篇文章主要论述了如何在新的Web应用领域中使用设计模式和MVC架构.文章首 ...

  4. Spring 框架基础(06):Mvc架构模式简介,执行流程详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.SpringMvc框架简介 1.Mvc设计理念 MVC是一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码,将业务逻辑聚集 ...

  5. [Buffalo]MVC架构模式

    MVC架构模式:即Model(模型)-View(视图)-Controller(控制器). 很多人将MVC和所谓的“三层架构”进行比较,其实两者并没有什么可比性,MVC更不是分别对应着UI.业务逻辑和数 ...

  6. SpringMVC MVC 架构模式

    MVC 架构模式 MVC(Model.View 和 Controller)是一种架构模式,它是按照职责划分的思想将系统操作分为三个模块进行处理,每个模块负责一个职责,而且模块之间可以相互交互通信,共同 ...

  7. iOS开发之--MVC 架构模式

    随着项目开发时间的增加,从刚开始那种很随意的代码风格,逐渐会改变,现在就介绍下MVC的架构模式,MVC的架构模式,从字面意思上讲,即:MVC 即 Modal View Controller(模型 视图 ...

  8. ASP.NET MVC架构模式

    一.是什么? MVC,即(Model-View-Controller,模型—视图—控制器模式),和三层类似,用于表示一种软件架构模式.在这种模式下,将系统的实现分为模型Model,视图View,控制器 ...

  9. Android 四大组件 与 MVC 架构模式

    作为一个刚从JAVA转过来的Android程序员总会思考android MVC是什么样的? 首先,我们必须得说Android追寻着MVC架构,那就得先说一下MVC是个啥东西! 总体而来说MVC不能说是 ...

  10. iOS - MVC 架构模式

    1.MVC 从字面意思来理解,MVC 即 Modal View Controller(模型 视图 控制器),是 Xerox PARC 在 20 世纪 80 年代为编程语言 Smalltalk-80 发 ...

随机推荐

  1. 持续定义Saas模式云数据仓库+实时搜索

    简介: 本文由阿里云计算平台事业部 MaxCompute 产品经理孟硕为大家带来<持续定义Saas模式云数据仓库+实时搜索>的相关分享.以下是视频内容精华整理,主要包括以下三个部分:1.W ...

  2. 慢sql治理经典案例分享

    ​简介:菜鸟供应链金融慢sql治理已经有一段时间,自己负责的应用持续很长时间没有慢sql告警,现阶段在推进组内其他成员治理应用慢sql.这里把治理过程中的一些实践拿出来分享下. ​ 作者 | 如期 来 ...

  3. 项目实战总结以及接入U-APM

    ​简介: 导致 App 性能低下的原因有很多,除去设备硬件和软件的外部因素,其中大部分是开发者错误地使用线.系统函数.编程范式.数据结构等导致的.即便是较有经验的程序员,也很难在开发时就能避免所有导致 ...

  4. [PHP] Laravel 的 503 Service Unavailable 模板提示的来源

    当我们看到 Laravel 的 503 样式的模板时,是启用维护模式的时候的提示(php artisan down). 开启访问运行 php artisan up. Refer:Laravel 503 ...

  5. PostMan接口测试实用小点

    PostMan接口测试实用小点 1. 接口测试变量存取操作 在Postman中有很多地方可以存储一些变量,这里只介绍经常使用的环境变量.变量设置后,在UI界面可以通过{{变量名}}获取到对应值. 在环 ...

  6. 利用PostMan 模拟上传/下载文件

    我们经常用postman模拟各种http请求.但是有时候因为业务需要,我们需要测试上传下载功能.其实postman也是很好支持这两种操作的. 一.上传文件: 1.打开postman 选择对应reque ...

  7. goframe v2.1.0 gf-cli的使用

    目录 1.视频教程 2.官方文档 3.下载 linux系统安装环境 windows系统安装环境 4.创建项目 5.启动项目 6.交叉编译 7.gen命令的使用 8.orm的操作 1.视频教程 http ...

  8. SpringBoot-RSA加密

    前言 最近由于工作业务的需要,需要对指定的字段信息进行非对称加解密:由于需要加密的内容过于庞大:自己执行程序会出现:Data must not be longer than 117 bytes 的异常 ...

  9. fastposter v2.8.4 发布 电商海报生成器

    fastposter v2.8.4 发布 电商海报生成器 fastposter海报生成器,电商海报编辑器,电商海报设计器,fast快速生成海报 海报制作 海报开发.贰维海报,图片海报,分享海报贰维码推 ...

  10. 网络安全—PKI公钥基础设施

    文章目录 前提知识 散列函数 非对称加密 数字签名 PKI 受信任的人 RA注册 CA颁发 IKE数字签名认证(交换证书) 密钥管理 前提知识 散列函数 散列也可以叫哈希函数,MD5.SHA-1.SH ...