尚硅谷Maven2022----②单一架构案例
文章目录
- 一、创建工程,引入依赖
- 1、架构
- 2、创建工程
- 3、引入依赖
- 4、建包
- 二、搭建环境:持久化层
- 1、数据建模
- 2、 数据库连接信息
- 3、获取数据库连接
- 4、BaseDao
- 5、子类 Dao
- 三、搭建环境:事务控制
- 1、总体思路
- 2、TransactionFilter
- 四、搭建环境:表述层
- 1、视图模板技术 Thymeleaf
- 2、ModelBaseServlet
一、创建工程,引入依赖
1、架构
①架构的概念
『架构』其实就是『项目的结构
』,只是因为架构是一个更大的词,通常用来形容比较大规模事物的结构。
②单一架构
单一架构也叫『all-in-one』结构,就是所有代码、配置文件、各种资源都在同一个工程。
- 一个项目包含一个工程
- 导出一个 war 包
- 放在一个 Tomcat 上运行
2、创建工程
3、引入依赖
①搜索依赖信息的网站
[1]到哪儿找?
https://mvnrepository.com/
[2]怎么选择?
- 确定技术选型:确定我们项目中要使用哪些技术
- 到 mvnrepository 网站搜索具体技术对应的具体依赖信息
- 确定这个技术使用哪个版本的依赖
- 考虑因素1:看是否有别的技术要求这里必须用某一个版本
- 考虑因素2:如果没有硬性要求,那么选择较高版本或下载量大的版本
- 在实际使用中检验所有依赖信息是否都正常可用
确定技术选型、组建依赖列表、项目划分模块……等等这些操作其实都属于架构设计的范畴。
- 项目本身所属行业的基本特点
- 项目具体的功能需求
- 项目预计访问压力程度
- 项目预计将来需要扩展的功能
- 设计项目总体的体系结构
②持久化层所需依赖
mysql:mysql-connector-java:5.1.37
com.alibaba:druid:1.2.8
commons-dbutils:commons-dbutils:1.6
③表述层所需依赖
javax.servlet:javax.servlet-api:3.1.0
org.thymeleaf:thymeleaf:3.0.11.RELEASE
④辅助功能所需依赖
junit:junit:4.12
ch.qos.logback:logback-classic:1.2.3
⑤最终完整依赖信息
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.37</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf</artifactId><version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version><scope>test</scope>
</dependency>
4、建包
package 功能 | package 名称 |
---|---|
主包 | com.atguigu.imperial.court |
子包[实体类] | com.atguigu.imperial.court.entity |
子包[Servlet基类包] | com.atguigu.imperial.court.servlet.base |
子包[Servlet模块包] | com.atguigu.imperial.court.servlet.module |
子包[Service接口包] | com.atguigu.imperial.court.service.api |
子包[Service实现类包] | com.atguigu.imperial.court.service.impl |
子包[Dao接口包] | com.atguigu.imperial.court.dao.api |
子包[Dao实现类包] | com.atguigu.imperial.court.dao.impl |
子包[Filter] | com.atguigu.imperial.court.filter |
子包[异常类包] | com.atguigu.imperial.court.exception |
子包[工具类] | com.atguigu.imperial.court.util |
子包[测试类] | com.atguigu.imperial.court.test |
二、搭建环境:持久化层
1、数据建模
①物理建模
create database db_imperial_court;use db_imperial_court;create table t_emp
(emp_id int primary key auto_increment,emp_name char(100) not null,emp_position char(100) not null,login_account char(100) not null unique,login_password char(100) not null
);insert into t_emp(emp_name, emp_position, login_account, login_password)
values ('爱新觉罗·玄烨', 'emperor', 'xiaoxuanzi1654', '25325C896624D444B2E241807DCAC98B'), # 16540504('纳兰明珠', 'minister', 'brightball1635', 'A580D0EF93C22036C859E194C14CB777'), # 16351119('赫舍里·索额图', 'minister', 'tutu1636', 'E40FD7D49B8B7EF46F47407D583C3538'); # 17030921create table t_memorials
(memorials_id int primary key auto_increment,memorials_title char(100) not null,memorials_content varchar(5000) not null,memorials_emp int not null,memorials_create_time char(100),feedback_time char(100),feedback_content varchar(1000),memorials_status int not null
);insert into t_memorials(memorials_title,memorials_content,memorials_emp,memorials_create_time,feedback_time,feedback_content,memorials_status)
values ('浙江巡抚奏钱塘堤决口疏', '皇上啊,不好啦!钱塘江发大水啦!堤坝冲毁啦!您看这咋弄啊!', 2, '1690-05-07', null, null, 0),('左都御史参鳌拜圈地疏', '皇上啊,鳌拜这厮不是东西呀!占老百姓的地哇!还打人呀!您看咋弄啊!', 3, '1690-04-14', null, null, 0),('都察院劾吴三桂不臣疏', '皇上啊,不得了啦!吴三桂那孙子想造反呀!', 2, '1693-11-18', null, null, 0),('兵部奏准噶尔犯境疏', '皇上啊,不得了啦!葛尔丹要打过来了呀!', 3, '1693-11-18', null, null, 0),('朝鲜使臣朝拜事宜呈皇上御览', '皇上啊!朝鲜国的人要来啦!咱们请他们吃猪肉炖粉条子吧!', 2, '1680-06-11', null, null, 0),('英吉利炮舰购买事宜疏', '皇上啊!英国的小船船咱们买多少啊?', 3, '1680-06-12', null, null, 0),('劾杭州织造贪墨疏', '皇上啊!杭州织造有问题啊!', 2, '1680-06-13', null, null, 0),('禀畅春园落成疏', '皇上啊!畅春园修好了哇!您啥时候过来看看呀!', 3, '1680-06-14', null, null, 0),('请旨木兰秋狝疏', '皇上啊!秋天到啦,又该打猎啦!', 2, '1680-06-15', null, null, 0),('核准西北军饷银两疏', '皇上啊!您看看这钱数算的对不对呀!', 3, '1680-06-16', null, null, 0),('请旨裁撤三藩疏', '皇上啊!咱们不裁撤三藩就芭比Q了哇!', 2, '1680-06-17', null, null, 0),('蒙古王公进京朝拜疏', '皇上啊!蒙古王公要来啦!咱们请他们吃猪肉炖粉条子吧!', 3, '1680-06-18', null, null, 0),('礼部请旨九阿哥赐名疏', '皇上啊!您看九阿哥该叫什么名字呀?', 2, '1680-06-19', null, null, 0),('户部尚书请旨告老还乡疏', '皇上啊!臣想回家养老啦!您看看啥时候给臣把俸禄结一下啊!', 3, '1680-06-20', null, null, 0),('查江宁织造贪墨疏', '皇上啊!江宁织造有问题啊!', 2, '1680-06-21', null, null, 0);
②逻辑建模
[1] Emp 实体类
public class Emp {private Integer empId;private String empName;private String empPosition;private String loginAccount;private String loginPassword;
[2] Memorials 实体类
public class Memorials {private Integer memorialsId;private String memorialsTitle;private String memorialsContent;// 奏折摘要数据库没有,这里是为了配和页面显示private String memorialsContentDigest;private Integer memorialsEmp;// 员工姓名数据库没有,这里是为了配合页面显示private String memorialsEmpName;private String memorialsCreateTime;private String feedbackTime;private String feedbackContent;private Integer memorialsStatus;
2、 数据库连接信息
说明:这是我们第一次用到 Maven 约定目录结构中的 resources 目录,这个目录存放各种配置文件。
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.198.100:3306/db_imperial_court
username=root
password=atguigu
initialSize=10
maxActive=20
maxWait=10000
3、获取数据库连接
①创建 JDBCUtils 工具类
功能1:从数据源获取数据库连接
功能2:将数据源绑定到本地线程(借助ThreadLocal)
功能3:释放线程时和本地线程解除绑定
②创建 javax.sql.DataSource 对象
// 将数据源对象设置为静态属性,保证大对象的单一实例
private static DataSource dataSource;static {// 1.创建一个用于存储外部属性文件信息的Properties对象Properties properties = new Properties();// 2.使用当前类的类加载器加载外部属性文件:jdbc.propertiesInputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");try {// 3.将外部属性文件jdbc.properties中的数据加载到properties对象中properties.load(inputStream);// 4.创建数据源对象dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();//这里捕获到的异常封装为运行时异常继续抛出,为了避免在真正抛出异常后,catch 块捕获从而掩盖问题throw new RuntimeException(e);}}
public static Connection getConnection() {return null;
}
③创建 ThreadLocal 对象
[1]提出需求
(1)在一个方法内控制事务
如果在每一个 Service 方法中都写下面代码,那么代码重复性就太高了:
try{// 1、获取数据库连接// 重要:要保证参与事务的多个数据库操作(SQL 语句)使用的是同一个数据库连接Connection conn = JDBCUtils.getConnection();// 2、核心操作// ...// 3、核心操作成功结束,可以提交事务conn.commit();}catch(Exception e){// 4、核心操作抛出异常,必须回滚事务conn.rollBack();}finally{// 5、释放数据库连接JDBCUtils.releaseConnection(conn);}
(2)将重复代码抽取到 Filter
所谓『当前请求覆盖的 Servlet 方法、Service 方法、Dao 方法』其实就是 chain.doFilter(request, response) 间接调用的方法。
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){try{// 1、获取数据库连接// 重要:要保证参与事务的多个数据库操作(SQL 语句)使用的是同一个数据库连接Connection conn = JDBCUtils.getConnection();// 重要操作:关闭自动提交功能connection.setAutoCommit(false);// 2、核心操作:通过 chain 对象放行当前请求// 这样就可以保证当前请求覆盖的 Servlet 方法、Service 方法、Dao 方法都在同一个事务中。// 同时各个请求都经过这个 Filter,所以当前事务控制的代码在这里只写一遍就行了,// 避免了代码的冗余。chain.doFilter(request, response);// 3、核心操作成功结束,可以提交事务conn.commit();}catch(Exception e){// 4、核心操作抛出异常,必须回滚事务conn.rollBack();}finally{// 5、释放数据库连接JDBCUtils.releaseConnection(conn);}}
(3)数据的跨方法传递
通过 JDBCUtils 工具类获取到的 Connection 对象需要传递给 Dao 方法,让事务涉及到的所有 Dao 方法用的都是同一个 Connection 对象。
但是 Connection 对象无法通过 chain.doFilter() 方法以参数的形式传递过去。
所以从获取到 Connection 对象到使用 Connection 对象中间隔着很多不是我们自己声明的方法——我们无法决定它们的参数。
[2] ThreadLocal 对象的功能
- 全类名:java.lang.ThreadLocal
- 泛型 T:要绑定到当前线程的数据的类型
- 具体三个主要的方法:
方法名 | 功能 |
---|---|
set(T value) | 将数据绑定到当前线程 |
get() | 从当前线程获取已绑定的数据 |
remove() | 将数据从当前线程移除 |
[3] Java 代码
// 由于 ThreadLocal 对象需要作为绑定数据时 k-v 对中的 key,所以要保证唯一性
// 加 static 声明为静态资源即可保证唯一性
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
④声明方法:获取数据库连接
/*** 工具方法:获取数据库连接并返回* @return*/
public static Connection getConnection() {Connection connection = null;try {// 1、尝试从当前线程检查是否存在已经绑定的 Connection 对象connection = threadLocal.get();// 2、检查 Connection 对象是否为 nullif (connection == null) {// 3、如果为 null,则从数据源获取数据库连接connection = dataSource.getConnection();// 4、获取到数据库连接后绑定到当前线程threadLocal.set(connection);}} catch (SQLException e) {e.printStackTrace();// 为了调用工具方法方便,编译时异常不往外抛// 为了不掩盖问题,捕获到的编译时异常封装为运行时异常抛出throw new RuntimeException(e);}return connection;
}
⑤声明方法:释放数据库连接
/*** 释放数据库连接*/
public static void releaseConnection(Connection connection) {if (connection != null) {try {// 在数据库连接池中将当前连接对象标记为空闲connection.close();// 将当前数据库连接从当前线程上移除threadLocal.remove();} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);}}}
⑥初步测试
public class ImperialCourtTest {@Testpublic void testGetConnection() {Connection connection = JDBCUtils.getConnection();System.out.println("connection = " + connection);JDBCUtils.releaseConnection(connection);}}
⑦完整代码
/*** 功能1:从数据源获取数据库连接* 功能2:从数据库获取到数据库连接后,绑定到本地线程(借助 ThreadLocal)* 功能3:释放线程时和本地线程解除绑定*/
public class JDBCUtils {// 数据源成员变量设置为静态资源,保证大对象的单例性;同时保证静态方法中可以访问private static DataSource dataSource;// 由于 ThreadLocal 对象需要作为绑定数据时 k-v 对中的 key,所以要保证唯一性// 加 static 声明为静态资源即可保证唯一性private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();// 在静态代码块中初始化数据源static {try {// 操作思路分析:// 从 jdbc.properties 文件中读取连接数据库的信息// 为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件// 确定的基准:类路径的根目录。resources 目录下的内容经过构建操作中的打包操作后会确定放在 WEB-INF/classes 目录下。// WEB-INF/classes 目录存放编译好的 *.class 字节码文件,所以这个目录我们就称之为类路径。// 类路径无论在本地运行还是在服务器端运行都是一个确定的基准。// 操作具体代码:// 1、获取当前类的类加载器ClassLoader classLoader = JDBCUtils.class.getClassLoader();// 2、通过类加载器对象从类路径根目录下读取文件InputStream stream = classLoader.getResourceAsStream("jdbc.properties");// 3、使用 Properties 类封装属性文件中的数据Properties properties = new Properties();properties.load(stream);// 4、根据 Properties 对象(已封装了数据库连接信息)来创建数据源对象dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {e.printStackTrace();// 为了避免在真正抛出异常后,catch 块捕获到异常从而掩盖问题,// 这里将所捕获到的异常封装为运行时异常继续抛出throw new RuntimeException(e);}}/*** 工具方法:获取数据库连接并返回* @return*/public static Connection getConnection() {Connection connection = null;try {// 1、尝试从当前线程检查是否存在已经绑定的 Connection 对象connection = threadLocal.get();// 2、检查 Connection 对象是否为 nullif (connection == null) {// 3、如果为 null,则从数据源获取数据库连接connection = dataSource.getConnection();// 4、获取到数据库连接后绑定到当前线程threadLocal.set(connection);}} catch (SQLException e) {e.printStackTrace();// 为了调用工具方法方便,编译时异常不往外抛// 为了不掩盖问题,捕获到的编译时异常封装为运行时异常抛出throw new RuntimeException(e);}return connection;}/*** 释放数据库连接*/public static void releaseConnection(Connection connection) {if (connection != null) {try {// 在数据库连接池中将当前连接对象标记为空闲connection.close();// 将当前数据库连接从当前线程上移除threadLocal.remove();} catch (SQLException e) {e.printStackTrace();throw new RuntimeException(e);}}}}
4、BaseDao
①泛型的说明
②创建 QueryRunner 对象
// DBUtils 工具包提供的数据库操作对象
private QueryRunner runner = new QueryRunner();
③通用增删改方法
特别说明:在 BaseDao 方法中获取数据库连接但是不做释放,因为我们要在控制事务的 Filter 中统一释放。
/*** 通用的增删改方法,insert、delete、update 操作都可以用这个方法* @param sql 执行操作的 SQL 语句* @param parameters SQL 语句的参数* @return 受影响的行数*/
public int update(String sql, Object ... parameters) {try {Connection connection = JDBCUtils.getConnection();int affectedRowNumbers = runner.update(connection, sql, parameters);return affectedRowNumbers;} catch (SQLException e) {e.printStackTrace();// 如果真的抛出异常,则将编译时异常封装为运行时异常抛出new RuntimeException(e);return 0;}
}
④查询单个对象
/*** 查询单个对象* @param sql 执行查询的 SQL 语句* @param entityClass 实体类对应的 Class 对象* @param parameters 传给 SQL 语句的参数* @return 查询到的实体类对象*/
public T getSingleBean(String sql, Class<T> entityClass, Object ... parameters) {try {// 获取数据库连接Connection connection = JDBCUtils.getConnection();return runner.query(connection, sql, new BeanHandler<>(entityClass), parameters);} catch (SQLException e) {e.printStackTrace();// 如果真的抛出异常,则将编译时异常封装为运行时异常抛出new RuntimeException(e);}return null;
}
⑤查询多个对象
/*** 查询返回多个对象的方法* @param sql 执行查询操作的 SQL 语句* @param entityClass 实体类的 Class 对象* @param parameters SQL 语句的参数* @return 查询结果*/
public List<T> getBeanList(String sql, Class<T> entityClass, Object ... parameters) {try {// 获取数据库连接Connection connection = JDBCUtils.getConnection();return runner.query(connection, sql, new BeanListHandler<>(entityClass), parameters);} catch (SQLException e) {e.printStackTrace();// 如果真的抛出异常,则将编译时异常封装为运行时异常抛出new RuntimeException(e);}return null;
}
⑥测试
private BaseDao<Emp> baseDao = new BaseDao<>();@Test
public void testGetSingleBean() {String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp where emp_id=?";Emp emp = baseDao.getSingleBean(sql, Emp.class, 1);System.out.println("emp = " + emp);}@Test
public void testGetBeanList() {String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp";List<Emp> empList = baseDao.getBeanList(sql, Emp.class);for (Emp emp : empList) {System.out.println("emp = " + emp);}}@Test
public void testUpdate() {String sql = "update t_emp set emp_position=? where emp_id=?";String empPosition = "minister";String empId = "3";int affectedRowNumber = baseDao.update(sql, empPosition, empId);System.out.println("affectedRowNumber = " + affectedRowNumber);}
5、子类 Dao
三、搭建环境:事务控制
1、总体思路
2、TransactionFilter
①创建 Filter 类
②TransactionFilter 完整代码
public class TransactionFilter implements Filter {// 声明集合保存静态资源扩展名private static Set<String> staticResourceExtNameSet;static {staticResourceExtNameSet = new HashSet<>();staticResourceExtNameSet.add(".png");staticResourceExtNameSet.add(".jpg");staticResourceExtNameSet.add(".css");staticResourceExtNameSet.add(".js");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// 前置操作:排除静态资源HttpServletRequest request = (HttpServletRequest) servletRequest;String servletPath = request.getServletPath();if (servletPath.contains(".")) {String extName = servletPath.substring(servletPath.lastIndexOf("."));if (staticResourceExtNameSet.contains(extName)) {// 如果检测到当前请求确实是静态资源,则直接放行,不做事务操作filterChain.doFilter(servletRequest, servletResponse);// 当前方法立即返回return ;}}Connection connection = null;try{// 1、获取数据库连接connection = JDBCUtils.getConnection();// 重要操作:关闭自动提交功能connection.setAutoCommit(false);// 2、核心操作filterChain.doFilter(servletRequest, servletResponse);// 3、提交事务connection.commit();}catch (Exception e) {try {// 4、回滚事务connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}// 页面显示:将这里捕获到的异常发送到指定页面显示// 获取异常信息String message = e.getMessage();// 将异常信息存入请求域request.setAttribute("systemMessage", message);// 将请求转发到指定页面request.getRequestDispatcher("/").forward(request, servletResponse);}finally {// 5、释放数据库连接JDBCUtils.releaseConnection(connection);}}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void destroy() {}
}
③配置 web.xml
注意:需要首先将当前工程改成 Web 工程。
<filter><filter-name>txFilter</filter-name><filter-class>com.atguigu.imperial.court.filter.TransactionFilter</filter-class>
</filter>
<filter-mapping><filter-name>txFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
④注意点
[1]确保异常回滚
在程序执行的过程中,必须让所有 catch 块都把编译时异常转换为运行时异常抛出;如果不这么做,在 TransactionFilter 中 catch 就无法捕获到底层抛出的异常,那么该回滚的时候就无法回滚。
[2]谨防数据库连接提前释放
由于诸多操作都是在使用同一个数据库连接,那么中间任何一个环节释放数据库连接都会导致后续操作无法正常完成。
四、搭建环境:表述层
1、视图模板技术 Thymeleaf
①服务器端渲染
参考资料(opens new window)
②Thymeleaf 简要工作机制
[1]初始化阶段
目标:创建 TemplateEngine 对象
封装:因为对每一个请求来说,TemplateEngine 对象使用的都是同一个,所以在初始化阶段准备好
[2]请求处理阶段
③逻辑视图与物理视图
假设有下列页面地址:
/WEB-INF/pages/apple.html
/WEB-INF/pages/banana.html
/WEB-INF/pages/orange.html
/WEB-INF/pages/grape.html
/WEB-INF/pages/egg.html
这样的地址可以直接访问
到页面本身,我们称之为:物理视图
。而将物理视图中前面、后面的固定内容抽取出来,让每次请求指定中间变化部分即可,那么中间变化
部分就叫:逻辑视图
。
④ViewBaseServlet 完整代码
为了简化视图页面处理过程,我们将 Thymeleaf 模板引擎的初始化和请求处理过程封装到一个 Servlet 基类中:ViewBaseServlet。以后负责具体模块业务功能的 Servlet 继承该基类即可直接使用。
传送门
特别提醒:这个类不需要掌握,因为以后都被框架封装了,我们现在只是暂时用一下。
⑤声明初始化参数
<!-- 配置 Web 应用初始化参数指定视图前缀、后缀 -->
<!-- 物理视图举例:/WEB-INF/pages/index.html对应逻辑视图:index
-->
<context-param><param-name>view-prefix</param-name><param-value>/WEB-INF/pages/</param-value>
</context-param>
<context-param><param-name>view-suffix</param-name><param-value>.html</param-value>
</context-param>
⑥Thymeleaf 的页面语法
传送门
2、ModelBaseServlet
①提出问题
[1]我们的需求
[2]HttpServlet 的局限
- doGet() 方法:处理 GET 请求
- doPost() 方法:处理 POST 请求
②解决方案
每个请求附带一个请求参数,表明自己要调用的目标方法
Servlet 根据目标方法名通过反射调用目标方法
③ModelBaseServlet 完整代码
特别提醒:为了配合 TransactionFilter 实现事务控制,捕获的异常必须抛出。
传送门
④继承关系
尚硅谷Maven2022----②单一架构案例相关推荐
- 2022版Maven教程 - 第六章 单一架构案例
2022版Maven教程 - 第六章 单一架构案例 一.创建工程,引入依赖 1.架构 ①架构的概念 ②单一架构 2.创建工程 3.引入依赖 ①搜索依赖信息的网站 [1]到哪儿找? [2]怎么选择? ② ...
- 【Maven基础】单一架构案例(一)
第一节 创建工程,引入依赖 1.架构 1.1.架构的概念 『架构』其实就是『项目的结构』,只是因为架构是一个更大的词,通常用来形容比较大规模事物的结构. 1.2.单一架构 单一架构也叫『all-in- ...
- 尚硅谷Java、HTML5前端、全栈式开发视频
Java基础阶段: 一.20天横扫Java基础(课堂实录) https://pan.baidu.com/s/1htTzZRQ 二.尚硅谷Java基础实战--Bank项目 http://pan.baid ...
- 【视频分享】尚硅谷Java视频教程_SpringCloud视频教程
尚硅谷SpringCloud第一手资料,分享给大家,祝大家走上技术巅峰后,可以来点赞赏. 本套视频从面试题,到SpringCloud各种核心组件,到最终的微服务架构总结,帮助大家快速入门.上手并精通微 ...
- {转发}尚硅谷资料集锦
2018年05月18日 20:13:25 寒夕若梦 阅读数:8093 Java基础阶段 一. 20天横扫Java基础(课堂实录) https://pan.baidu.com/s/1htTzZRQ 二. ...
- 尚硅谷《全套Java、Android、HTML5前端视频》
尚硅谷<全套Java.Android.HTML5前端视频> (百万谷粉推荐:史上最牛.最适合自学的全套视频.资料及源码) [尚硅谷官网资料导航] 谷粒学院在线学习:http://www.g ...
- 【备忘】2018年最新尚硅谷全套Java、Android、HTML5前端视频教程下载
全套Java教程--打包下载地址] Java基础阶段 一.20天横扫Java基础(课堂实录) 二.尚硅谷Java基础实战--Bank项目 三.尚硅谷_ORACLE.SQ ...
- 史上最牛、最适合自学的尚硅谷《全套Java视频教程》
涵盖:视频教程.在线课堂.最新教程SpringMVC 视频教程 Java基础阶段: 一.20天横扫Java基础(课堂实录) http://pan.baidu.com/s/1kTgIYDT 二.尚硅 ...
- 尚硅谷大数据视频_Shell视频教程
Shell是一个功能相当强大的编程语言,易编写.易调试.灵活性强.Shell可以帮助我们来管理大数据集群,提高开发效率.本课程详细讲解:Shell解析器.变量.运算符.条件判断.流程控制.函数.cut ...
- 尚硅谷Nginx新版升级教程,带你轻松掌握高并发系统架构
摘要:桃李春风一杯酒,江湖夜雨十年灯. "你说有没有一种可能, "你喜欢我,又不好意思说出口, "其实你可以直接说出来的, "我会让你知道什么叫心想事成.&qu ...
最新文章
- python怎么控制while循环_Python流程控制之while循环怎么学呢?老男孩Python
- EI:天大王灿+昆士兰郭建华揭示生物气溶胶是猪场耐药基因的重要传播途径
- 聊聊电商系统中常见的9大坑,库存超卖、重复下单、物流单ABA...
- [leetcode] Bulb Switcher
- 单词evolve 	pro	legacy	 launcher session
- access的ole对象换成mysql_ACCESS的Ole对象读取写入
- 虚拟鼠标代替安卓触屏_美术学院18级虚拟空间设计专业数字图像程序基础课程优秀结课成果展示优秀学生王雨禾作品展示...
- mysql为何不支持开窗函数?
- mysql学习【第2篇】:基本操作和存储引擎
- 好铁不打钉,好男不当网管-----论网管的自我修养 ...
- [转]访问 OData 服务 (WCF Data Services)
- b树删除节点每次只能删一个吗_【面试索引】BTree、B+Tree、红黑树、B*Tree数据结构...
- 最详细Python批量字典暴力破解zip密码
- C++程序设计一、二(二元一次方程与函数、函数指针)
- Linux内核深入理解定时器和时间管理(6):x86_64 相关的时钟源(kvm-clock,tsc,acpi_pm,hpet)
- 医疗软件还能怎么做,才能更进一步。
- unity 内部嵌入的网页插件中播放视频网页的问题
- php 统计磁盘大小,PHP_PHP统计目录大小的自定义函数分享,计算文件、磁盘分区和目录的 - phpStudy...
- 人事不干人事,这算个什么事?
- 元宇宙技术教程|如何一键生成虚拟人视频
热门文章
- 二进制的转换(二进制、八进制、十进制、十六进制)
- 利用多拨技术将100M宽带免费扩展到1000M
- 如何通过计算机侵入学校网络,如何进行局域网入侵
- 俄亥俄州立大学计算机专业排名,俄亥俄州立大学哥伦布分校计算机科学专业排名第30(2020年USNEWS美国排名)...
- 老路用得上的商学课-61-80学习(读书)笔记
- 带超前进位的四位全加器
- 《UnityAPI.Texture纹理》(Yanlz+Unity+SteamVR+云技术+5G+AI+VR云游戏+Texture+mipMapBias+wrapMode+立钻哥哥++OK++)
- CentOS 7安装/卸载Redis,配置service服务管理
- 谈谈计算机软件开发技术
- 使用美图秀秀批量处理照片