springboot项目练习15 抽离公共模块整理代码

本文详细介绍了如何在微服务架构下,将新闻系统的实体类和公共类抽离为独立模块,通过创建公共依赖项目并调整服务提供者与消费者的代码,实现代码复用和维护性提升。同时,介绍了前端页面的优化过程,包括使用Bootstrap美化界面和实现前后端交互。
  • 将entity实体类和公共类抽离出原项目
  • 修改服务提供者和服务消费者项目

1 新建项目news-common,添加公共依赖lang jsoup依赖

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.gc.spingboot</groupId>
  <artifactId>news-common</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <properties>
  <springboot-version>2.0.5.RELEASE</springboot-version>
  </properties>
  <dependencies>
  <dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
 <dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.8.3</version>
		</dependency>
  </dependencies>
</project>

2 使用maven clean install 将jar包安装到本地maven仓库

3删除服务提供者和服务消费者项目中的重复代码,引入生成的公共模块依赖

<dependency>
		<groupId>com.gc.spingboot</groupId>
  	<artifactId>news-common</artifactId>
 	 <version>0.0.1-SNAPSHOT</version>

由于注册中心暂时没用到其他模块的代码,故不进行引用

4 调整查看新闻接口,将此方法移动到新闻消费者news-search项目的NewsSearchController中,调整页面,增加点击事件,页面使用bootstarp进行编辑。(在这之前删除掉redis缓存中存储的html页面) flushall      清空所有的key值

5 列表使用list-group row 这个样式(导入bootstarp的js和css样式文件)完成后的列表界面如下图所示:

6 页面列表页和js交互代码如下所示:使用了模态框(前台代码是真的难写)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap 101 Template</title>

<link href="./../bootstrap/dist/css/bootstrap.css" rel="stylesheet">
<style type="text/css">
a:hover, a:focus {
	color: #2a6496;
	text-decoration: underline;
}

a:focus {
	outline: thin dotted #333;
	outline: 5px auto -webkit-focus-ring-color;
	outline-offset: -2px;
}
</style>
</head>
<h4 align="center">
	新闻系统 <small>前台首页</small>
</h4>
<body style="width: auto; height: auto">
	<div class="container">
		<div class="row" align="center">
			<form class="form-inline" role="form" action="javascript:void(0)">
				<div class="form-group ">
					<label for="searchKey" class="sr-only">关键字</label>
					<div class="col-sm-10">
						<input type="text" class="form-control col-lg-2" id="searchKey"
							placeholder="请输入关键字">
					</div>
				</div>
				<div class="form-group ">
					<div class="col-sm-offset-2 col-sm-10 col-lg-3">
						<button type="button" id ="search" class="btn btn-default">搜索</button>
					</div>
				</div>
			</form>
		</div>
		<div class="list-group row" style="margin-top: 10px">
			<ul id="newsList">
<!-- 				<li class="list-group-item">免费域名注册<span class="badge">新</span></li> -->
<!-- 				<li class="list-group-item">免费 Window 空间托管<span class="badge">新</span></li> -->
<!-- 				<li class="list-group-item">图像的数量<span class="badge">新</span></li> -->
<!-- 				<li class="list-group-item">24*7 支持<span class="badge">新</span></li> -->
<!-- 				<li class="list-group-item">每年更新成本<span class="badge">新</span></li> -->
			</ul>
			<div class="row" align="center">


				<ul class="pagination" id="pagination">
					<li id="goPre"><a href="javascript:void(0)">&laquo;</a></li>
					<li class="active" key='1' onclick='pageClick(this)'><a href="javascript:void(0)">1</a></li>
					<li key='2' onclick='pageClick(this)'><a href="javascript:void(0)">2</a></li>
					<li key='3' onclick='pageClick(this)'><a href="javascript:void(0)">3</a></li>
					<li key='4' onclick='pageClick(this)'><a href="javascript:void(0)">4</a></li>
					<li key='5' onclick='pageClick(this)'><a href="javascript:void(0)">5</a></li>
					<li key='6' onclick='pageClick(this)'><a href="javascript:void(0)">6</a></li>
					<li key='7' onclick='pageClick(this)'><a href="javascript:void(0)">7</a></li>
					<li key='8' onclick='pageClick(this)'><a href="javascript:void(0)">8</a></li>
					<li key='9' onclick='pageClick(this)'><a href="javascript:void(0)">9</a></li>
					<li key='10' onclick='pageClick(this)'><a href="javascript:void(0)">10</a></li>
					<li id="geNext"><a href="javascript:void(0)">&raquo;</a></li>
					<li class="disabled"><span id="totalPage">共40页</span></li>
					<li class="disabled"><span id="totalSum">共862条记录</span></li>
				</ul>
			</div>
		</div>
	</div>
	<div class="modal fade bs-example-modal-lg" id="newsModel" >
    <div class="modal-dialog modal-lg" >
        <div class="modal-content modal-lg"  >
            <div class="modal-header">
                <h4 class="modal-title" id="newsModelLabel">news-search新闻查看</h4>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
            </div>
            <div class="modal-body">
                <iframe id="newsModelIframe" width="100%"  frameborder="0" style="height:350px;overflow:auto; "></iframe>
            </div>
            <div class="modal-footer">
                <button class="btn btn-default"  type="button" onclick="closeIfram()" > 关闭  </button>
            </div>
        </div>
    </div>
</div>

	<script src="./../js/jquery.js"></script>
	<script src="/../bootstrap/dist/js/bootstrap.js"></script>
</body>

<script type="text/javascript">
var param = {};
param.pagesize="10";
var totaPage=1;
var curentIndex=1;
param.key="";
$(function(){
	var key = $("#searchKey").val();
	if(key){
		param.key=key;
	}
	param.currentPage="1";
	loadData(param);
});

function  closeIfram(){
	$("#newsModel").modal('hide')
}

/**
 * 分页按钮点击事件
 */
function pageClick(obj){
	console.log(obj);
	$(obj).addClass("active");
	var currentPage = $(obj).attr("key");
	$("#pagination").find("li").each(function(){
		$li = $(this);
		var liKey=$li.attr("key");
		if(liKey && liKey!= currentPage){
			$li.removeClass("active");
		}
	});
	param.currentPage=currentPage;
	var key = $("#searchKey").val();
	if(key){
		param.key=key;
	}
	loadData(param);
}
/**
 * 搜索按钮
	得到当前页
 */
$("#search").bind("click",function(){
	
	var key = $("#searchKey").val();
	if(key){
		param.key=key;
	}

	var currentPage =$("#pagination").find("li[class='active']").attr("key");// 当前活动的页码
	param.currentPage=currentPage;
	
	loadData(param);
});

/**
 * 查看新闻信息
 */
function  shownews(id,url){
	var newsurl="http://localhost:8092/newsSearch/getNews/"+id;
	$.post(newsurl,{url,url},function(res){
		if(res.code==200){
	         var frameSrc = "http://localhost:8092/page/"+res.data;
	         $("#newsModelIframe").attr("src", frameSrc);
	         $('#newsModel').modal({ show: true, backdrop: 'static' });
		}
		
	},"json");
	
	
}

/**
 *数据加载
 */
function  loadData(param){
	var url ="http://localhost:8092/newsSearch/search";
	var html ="";
	$.post(url,param,function(res){
		$("#newsList").empty();
		if(res.code==200){
			for(var i=0;i<res.data.rows.length;i++){
				var item = res.data.rows[i];
				html += "<li class='list-group-item'><a href='javascript:void(0)'"+
				"onclick=shownews('"+item.id+"','"+item.url_3w+"')>"+
				item.title_news+"</a><span class='pull-right'>"+item.source_news+"&nbsp;"+item.ptime+"</span></li>";
			}
			totaPage=res.data.totalPage==0?1:res.data.totalPage;// 默认显示 0 到 10
			$("#newsList").append(html);
			$("#totalPage").empty();
			$("#totalPage").html("共"+res.data.totalPage+"页");
			$("#totalSum").empty();
			$("#totalSum").html("共"+res.data.total+"条记录");
		}
	});
}
/**
 * gpPre 相当于前端分页
 */
$("#goPre").click(function(){
	if(curentIndex==1){
		return;
	}
	//修改li的key的值 和分页下标    的值
	$("#pagination").find("li").each(function(){
		$li = $(this);
		var liKey=$li.attr("key");
		if(liKey && liKey >10 ){
// 			$li.removeClass("active");
			$li.attr("key",liKey-10);
			$li.find("a").html(liKey-10);
			
			
		}
	});
	curentIndex=curentIndex-1;
});
$("#geNext").click(function(){
	// 计算可以轮几轮
	var sum  =  totaPage%10==0?totaPage/10:totaPage/10+1;//总的次数
	if(curentIndex==parseInt(sum)){//相当于最后一页 直接返回
		return;
	}
	//修改li的key的值 和分页下标    的值
	$("#pagination").find("li").each(function(){
		$li = $(this);
		var liKey=$li.attr("key");
		if(liKey){
			$li.attr("key", parseInt(liKey)+10);
			$li.find("a").html(parseInt(liKey)+10);
			if(parseInt(liKey)+10>=totaPage){
				
			}
		}
	});
	curentIndex=curentIndex+1;
});
</script>
</html>

 7 弹出层查看页面效果图如下所示:简单的做了个页面(这个前端还得好好琢磨一下,样式表)

结束:

现在抽离了公共代码块整合和注册中心、redis、solr、mysql使用feign完成了列表的调用,通过redis存储键的方法,完成了请求页面的静态化。

下面将继续其他模块和功能的开发,还有学习前端框架,竟可能让页面看起来好一点。 

<think>我们正在设计一个SpringBoot项目,其中需要为老师端和学生端创建独立的子模块。这种设计有助于代码的模块化、可维护性和团队协作。以下是最佳实践方案:###1.项目结构设计建议采用多模块Maven/Gradle项目结构。父模块管理公共依赖和配置,老师端和学生端作为子模块存在。```parent-project(pom)├──teacher-module(jar)├──student-module(jar)└──common-module(jar)#可选,用于存放公共代码```###2.创建步骤####(1)创建父项目项目的`pom.xml`需设置`<packaging>pom</packaging>`并声明子模块:```xml<modules><module>teacher-module</module><module>student-module</module><!--如果需要公共模块--><module>common-module</module></modules>```####(2)创建子模块每个子模块都是独立的SpringBoot应用:-**teacher-module**:老师端功能-**student-module**:学生端功能####(3)公共模块(可选)如果两个模块有共享代码(如通用工具、DTO、配置类等),可创建`common-module`。子模块通过Maven依赖引入:```xml<dependency><groupId>com.example</groupId><artifactId>common-module</artifactId><version>${project.version}</version></dependency>```###3.配置隔离每个子模块应有自己的配置文件:-`teacher-module/src/main/resources/application.yml`-`student-module/src/main/resources/application.yml`可通过`spring.profiles.active`指定环境配置。###4.启动类隔离每个子模块应有独立的启动类:```java//teacher-module@SpringBootApplicationpublicclassTeacherApplication{publicstaticvoidmain(String[]args){SpringApplication.run(TeacherApplication.class,args);}}//student-module@SpringBootApplicationpublicclassStudentApplication{publicstaticvoidmain(String[]args){SpringApplication.run(StudentApplication.class,args);}}```###5.包结构隔离确保包路径分离,避免代码混淆:-`com.example.teacher`-`com.example.student`###6.依赖管理父`pom.xml`中定义依赖版本(使用`<dependencyManagement>`),子模块按需引入具体依赖。###7.运行与部署-**开发时**:可分别启动`TeacherApplication`和`StudentApplication`-**部署时**:可打包成两个独立的JAR文件部署到不同服务器或同一服务器的不同端口###8.微服务考量(进阶)如果未来需要扩展为微服务架构:-可将子模块改造为SpringCloud微服务-通过注册中心(如Eureka)实现服务发现-通过网关统一路由(如SpringCloudGateway)>**注意**:多模块项目需避免循环依赖,公共代码及时抽离到`common-module`[^1]。§§相关问题§§1.如何在多模块项目中管理SpringBoot的版本和依赖?2.子模块之间如何安全地共享DTO和工具类?3.如何为不同子模块配置不同的数据库?4.多模块项目如何与SpringCloud集成?5.如何避免子模块间的代码耦合?以上方案充分利用了SpringBoot的模块化能力,符合"约定优于配置"原则[^1],能有效提升开发效率。</think>### Spring Boot项目中为老师端和学生端创建独立子模块的设计方案 在Spring Boot项目中为不同用户角色(如老师端和学生端)创建独立子模块是典型的**模块化设计**实践,能有效实现代码隔离、职责分离和独立部署。以下是完整设计方案: --- #### 一、项目结构设计(Maven多模块) ```lua parent-project (pom) # 父模块 ├── teacher-module (jar) # 老师端子模块 ├── student-module (jar) # 学生端子模块 └── common-module (jar) # 公共模块(核心) ``` 1. **父模块 (`parent-project`)** 职责:统一管理依赖版本和构建配置 `pom.xml` 关键配置: ```xml <packaging>pom</packaging> <modules> <module>teacher-module</module> <module>student-module</module> <module>common-module</module> </modules> <!-- 统一Spring Boot版本 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.0</version> </parent> ``` 2. **公共模块 (`common-module`)** 包含共享组件: - 实体类(如 `User.java`, `Course.java`) - 通用工具类(`DateUtils`, `StringUtils`) - 安全配置基类(`BaseSecurityConfig`) - 数据访问层(`CommonRepository`) - 异常处理器(`GlobalExceptionHandler`) 3. **角色专属模块** | 模块 | 职责 | 特有组件示例 | |---|---|----| | `teacher-module` | 老师端业务逻辑 | `TeacherController.java`<br>`GradeService.java`<br>`ExamManagementConfig.java` | | `student-module` | 学生端业务逻辑 | `StudentController.java`<br>`CourseSelectionService.java`<br>`HomeworkSubmitConfig.java` | --- #### 二、关键实现步骤 1. **模块间依赖配置** 在子模块中引入公共模块: ```xml <!-- teacher-module/pom.xml --> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>common-module</artifactId> <version>${project.version}</version> </dependency> </dependencies> ``` 2. **包结构隔离** 使用不同根包名避免冲突: ```java // 老师端 com.example.teacher ├── controller ├── service └── config // 学生端 com.example.student ├── controller ├── service └── config ``` 3. **独立配置文件** 每个子模块拥有专属配置: ```yaml # teacher-module/src/main/resources/application.yml server: port: 8081 # 老师端独立端口 spring: datasource: url: jdbc:mysql://localhost:3306/teacher_db # student-module/src/main/resources/application.yml server: port: 8082 # 学生端独立端口 spring: datasource: url: jdbc:mysql://localhost:3306/student_db ``` 4. **启动类隔离** 每个子模块独立启动: ```java // TeacherApplication.java @SpringBootApplication(scanBasePackages = { "com.example.common", "com.example.teacher" // 仅扫描老师端包 }) public class TeacherApplication { ... } // StudentApplication.java @SpringBootApplication(scanBasePackages = { "com.example.common", "com.example.student" // 仅扫描学生端包 }) public class StudentApplication { ... } ``` --- #### 三、优势与最佳实践 1. **核心优势** - ✅ **独立部署**:可单独升级老师端而不影响学生端 - ✅ **安全隔离**:通过`@PreAuthorize("hasRole('TEACHER')")`实现角色级权限控制[^5] - ✅ **资源分离**:数据库、线程池等资源按角色隔离 - ✅ **团队协作**:不同团队可并行开发不同模块 2. **数据库设计策略** | 方案 | 适用场景 | 示例 | |---|---|---| | **共享库+租户ID** | 数据关联性强 | 所有表包含`tenant_id`字段[^5] | | **独立数据库** | 高隔离需求 | 老师端用`teacher_db`,学生端用`student_db` | | **混合模式** | 平衡型方案 | 基础数据共享库 + 业务独立库 | 3. **热部署优化** 添加`spring-boot-devtools`实现模块级热更新[^3]: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> ``` --- #### 四、部署架构示例 ```mermaid graph TD A[API Gateway] --> B(Teacher Module :8081) A --> C(Student Module :8082) B --> D[Teacher DB] C --> E[Student DB] B & C --> F[Common Service] ``` > **注意**:若需更高并发,可将子模块部署为独立微服务,通过Spring Cloud集成[^1]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_slaves

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值