💕喜欢的朋友可以关注一下,下次更新不迷路!💕(●'◡'●)
一、分模块开发
1、为什么分模块开发
先由一个银行的例子引入
- 网络没有那么发达的时候,我们需要到银行柜台或者取款机进行业务操作
- 随着互联网的发展,我们有了电脑以后,就可以在网页上登录银行网站使用U盾进行业务操作
- 再来就是随着智能手机的普及,我们只需要用手机登录APP就可以进行业务操作
这样会出现三个用户端:1、银行柜台+取款机。2、网页银行网站。3、手机APP。
上面三个场景出现的时间是不相同的,如果非要把三个场景的模块代码放入到一个项目,那么当其中某一个模块代码出现问题,就会导致整个项目无法正常启动😢,从而导致银行的多个业务都无法正常班理。所以我们需要按照功能将项目拆分进行分模块开发。
2、开发实现
前面我们已经完成了SSM整合,接下来,咱们就基于SSM整合的项目来实现对项目的拆分。
OK!让我们启动!😎
环境配置
先来一个最裹脚布的操作-----环境配置🤷♂️
JdbcConfig:
定义了与数据库连接相关的Bean。具体来说,它定义了一个DataSource
Bean和一个PlatformTransactionManager
Bean。
java
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
MyBatisConfig:
SqlSessionFactoryBean它的主要作用是创建和配置
SqlSessionFactory
SqlSessionFactory
用于创建SqlSession
实例,
SqlSession
是执行 SQL 操作的接口。
MapperScannerConfigurer的主要作用是扫描指定包中的接口,并将这些接口注册为 Spring 容器中的 Mapper 接口的代理实现类。这样,你就可以直接在 Spring 应用程序中使用这些 Mapper 接口,而无需手动创建它们的实现类。
java
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.itheima.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
ServletConfig:
作用是使用 Java 类而不是 web.xml 来配置 Spring MVC 应用程序。它定义了应用程序的配置类和 DispatcherServlet
的映射,这样当应用程序启动时,Spring 会自动创建和配置所需的上下文和 Servlet。
java
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
SpringConfig:
**Spring,我的超人!**😍
@Configuration
:表示这个类是一个配置类
@ComponentScan({"com.itheima.service"})
:告诉 Spring 扫描指定的包(及其子包)以查找和注册 Spring 组件(如@Component
、@Service
、@Repository
、@Controller
等)。在这个例子中,它会扫描com.itheima.service
包下的所有组件。
@PropertySource("classpath:jdbc.properties")
:指定一个属性资源文件的位置,Spring 会从这个文件中加载属性,并可以在 Spring 应用程序中使用这些属性。在这个例子中,它指定了类路径下的jdbc.properties
文件。
@Import({JdbcConfig.class, MyBatisConfig.class})
:这个注解用于导入其他配置类。
@EnableTransactionManagement
:这个注解启用了 Spring 的事务管理功能,允许在 Spring 应用程序中使用声明式事务。这意味着你可以使用@Transactional
注解来标记需要事务管理的方法。
java
@Configuration
@ComponentScan({"com.itheima.service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
SpringMvcConfig:
@EnableWebMvc
:启用了 Spring MVC 的多项关键功能,包括配置 DispatcherServlet、视图解析、静态资源处理等。这个注解相当于在 XML 配置中声明 <mvc:annotation-driven>
,它使得 Spring MVC 的注解编程模型生效。
java
@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig {
}
SpringMvcSupport:
扩展了 WebMvcConfigurationSupport
的配置类,用于自定义 Spring MVC 的行为。这个类主要用于配置静态资源的处理,例如 CSS、JavaScript、图片等。
通过重写 addResourceHandlers
方法,可以添加资源处理器,以便将特定的 URL 映射到相应的资源位置。
java
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
表现层
负责处理用户的请求并将结果返回给用户。
BookController
java
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);
}
@PutMapping
public Result update(@RequestBody Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);
}
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);
}
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Book book = bookService.getById(id);
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
String msg = book != null ? "" : "数据查询失败,请重试!";
return new Result(code,book,msg);
}
@GetMapping
public Result getAll() {
List<Book> bookList = bookService.getAll();
Integer code = bookList != null ? Code.GET_OK : Code.GET_ERR;
String msg = bookList != null ? "" : "数据查询失败,请重试!";
return new Result(code,bookList,msg);
}
}
Code
返回数据状态码常量
异常处理标号常量
地球毁灭,异常抛给太阳,听懂掌声! 🤣
java
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
public static final Integer SYSTEM_ERR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
public static final Integer SYSTEM_UNKNOW_ERR = 59999;
public static final Integer BUSINESS_ERR = 60002;
}
Result
返回数据的封装
java
public class Result {
private Object data;
private Integer code;
private String msg;
public Result() {
}
public Result(Integer code,Object data) {
this.data = data;
this.code = code;
}
public Result(Integer code, Object data, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
异常处理
业务异常
java
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
系统异常
java
public class SystemException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
服务层(业务逻辑层)
BookServiceImpl
java
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
public boolean save(Book book) {
return bookDao.save(book) > 0;
}
public boolean update(Book book) {
return bookDao.update(book) > 0;
}
public boolean delete(Integer id) {
return bookDao.delete(id) > 0;
}
public Book getById(Integer id) {
if(id == 1){
throw new BusinessException(Code.BUSINESS_ERR,"请不要使用你的技术挑战我的耐性!");
}
//
return bookDao.getById(id);
}
public List<Book> getAll() {
return bookDao.getAll();
}
}
BookService
java
@Transactional
public interface BookService {
/**
* 保存
* @param book
* @return
*/
public boolean save(Book book);
/**
* 修改
* @param book
* @return
*/
public boolean update(Book book);
/**
* 按id删除
* @param id
* @return
*/
public boolean delete(Integer id);
/**
* 按id查询
* @param id
* @return
*/
public Book getById(Integer id);
/**
* 查询全部
* @return
*/
public List<Book> getAll();
}
创建新的模块
实体类Book
在maven_03_pojo
项目中创建com.itheima.domain
包,并将maven_02_ssm
中Book类拷贝到该包中,并将maven_02_ssm
中Book类删除。
因为删除掉实体类后所以要在maven_02_ssm
中的pop.xml添加maven_03_pojo
的依赖。
XML
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_03_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
Dao层(BookDao)
在
maven_04_dao
项目的pom.xml中添加maven_03_pojo
项目,这使得
maven_04_dao
的BookDao接口中能找到Book类
XML
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_03_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
在
maven_04_dao
项目的pom.xml中添加mybatis
的相关依赖,这使得
maven_04_dao
的BookDao接口中,Mybatis的增删改查注解不会报错
XML
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
删除原项目中的Dao包(爽!😘)
删除Dao包以后,因为
maven_02_ssm
中的BookServiceImpl类中有使用到Dao的内容,所以需要在maven_02_ssm
的pom.xml添加maven_04_dao
的依赖
XML
<dependency>
<groupId>com.itheima</groupId>
<artifactId>maven_04_dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
此时在
maven_02_ssm
项目中就已经添加了maven_03_pojo
和maven_04_dao
包
运行测试
将需要被依赖的项目
maven_04_dao
,使用maven的install命令,把其安装到Maven的本地仓库中。
再次对项目进行编译
二、继承与聚合
聚合
定义:将多个模块组织成一个整体,同时进行项目构建的过程称为聚合
作用:使用聚合工程可以将多个工程编组,实现对所包含的模块进行同步构建
- 当工程中某个模块发生更新(变更)时,必须保障工程中与已更新模块关联的模块同步更新,此时可以使用聚合工程来解决批量模块同步构建的问题。
步骤一:创建一个空的maven项目
步骤2:将项目的打包方式改为pom
XML
<?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.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-RELEASE</version>
<packaging>pom</packaging>
</project>
👌三种打包方式:
- jar:默认情况,说明该项目为java项目
- war:说明该项目为web项目
- pom:说明该项目为聚合或继承(后面会讲)项目
步骤3:pom.xml添加所要管理的项目
XML
<?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.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-RELEASE</version>
<packaging>pom</packaging>
<!--设置管理的模块名称-->
<modules>
<module>../maven_02_ssm</module>
<module>../maven_03_pojo</module>
<module>../maven_04_dao</module>
</modules>
</project>
步骤4:使用聚合统一管理项目
当
maven_01_parent
的compile
被点击后,所有被其管理的项目都会被执行编译操作。这就是聚合工程的作用。最后总结一句话就是,聚合工程主要是用来管理项目。
继承
多模块开发存在的另外一个问题,重复配置
的问题。😢
- 所谓继承:描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
- 作用:
- 简化配置
- 减少版本冲突
步骤1:创建一个空的Maven项目并将其打包方式设置为pom
步骤2:在子项目中设置其父工程
分别在
maven_02_ssm
,maven_03_pojo
,maven_04_dao
的pom.xml中添加其父项目为maven_01_parent
XML
<!--配置当前工程继承自parent工程-->
<parent>
<groupId>com.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-RELEASE</version>
<!--设置父项目pom.xml位置路径-->
<relativePath>../maven_01_parent/pom.xml</relativePath>
</parent>
步骤3:优化子项目共有依赖导入问题
1、将子项目共同使用的jar包都抽取出来,维护在父项目的pom.xml中
XML
<?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.itheima</groupId>
<artifactId>maven_01_parent</artifactId>
<version>1.0-RELEASE</version>
<packaging>pom</packaging>
<!--设置管理的模块名称-->
<modules>
<module>../maven_02_ssm</module>
<module>../maven_03_pojo</module>
<module>../maven_04_dao</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.16</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
</project>
2、删除子项目中已经被抽取到父项目的pom.xml中的jar包,如在
maven_02_ssm
的pom.xml中将已经出现在父项目的jar包删除掉
三、属性
作用:可以同时修改依赖的版本👍
版本管理
Maven中的依赖有很多版本
四、私服
私服简介
(1)张三负责ssm_crm的开发,自己写了一个ssm_pojo模块,要想使用直接将ssm_pojo安装到本地仓库即可
(2)李四负责ssm_order的开发,需要用到张三所写的ssm_pojo模块,这个时候如何将张三写的ssm_pojo模块交给李四呢?
(3)如果直接拷贝,那么团队之间的jar包管理会非常混乱而且容器出错,这个时候我们就想能不能将写好的项目上传到中央仓库,谁想用就直接联网下载即可
(4)Maven的中央仓库不允许私人上传自己的jar包,那么我们就得换种思路,自己搭建一个类似于中央仓库的东西,把自己的内容上传上去,其他人就可以从上面下载jar包使用
(5)这个类似于中央仓库的东西就是我们接下来要学习的私服
所以到这就有两个概念,一个是私服,一个是中央仓库私服:公司内部搭建的用于存储Maven资源的服务器
中央仓库:Maven开发团队维护的用于存储Maven资源的服务器
私服仓库分类
(1)在没有私服的情况下,我们自己创建的服务都是安装在Maven的本地仓库中
(2)私服中也有仓库,我们要把自己的资源上传到私服,最终也是放在私服的仓库中
(3)其他人要想使用你所上传的资源,就需要从私服的仓库中获取
(4)当我们要使用的资源不是自己写的,是远程中央仓库有的第三方jar包,这个时候就需要从远程中央仓库下载,每个开发者都去远程中央仓库下速度比较慢(中央仓库服务器在国外)
(5)私服就再准备一个仓库,用来专门存储从远程中央仓库下载的第三方jar包,第一次访问没有就会去远程中央仓库下载,下次再访问就直接走私服下载
(6)前面在介绍版本管理的时候提到过有SNAPSHOT和RELEASE,如果把这两类的都放到同一个仓库,比较混乱,所以私服就把这两个种jar包放入不同的仓库
(7)上面我们已经介绍了有三种仓库,一种是存放SNAPSHOT的,一种是存放RELEASE还有一种是存放从远程仓库下载的第三方jar包,那么我们在获取资源的时候要从哪个仓库种获取呢?
(8)为了方便获取,我们将所有的仓库编成一个组,我们只需要访问仓库组去获取资源。
所有私服仓库总共分为三大类:宿主仓库hosted
保存无法从中央仓库获取的资源
自主研发
第三方非开源项目,比如Oracle,因为是付费产品,所以中央仓库没有代理仓库proxy
代理远程仓库,通过nexus访问其他公共仓库,例如中央仓库
仓库组group
将若干个仓库组成一个群组,简化配置
仓库组不能保存资源,属于设计型仓库
本地仓库访问私服配置
- 我们通过IDEA将开发的模块上传到私服,中间是要经过本地Maven的
- 本地Maven需要知道私服的访问地址以及私服访问的用户名和密码
- 私服中的仓库很多,Maven最终要把资源上传到哪个仓库?
- Maven下载的时候,又需要携带用户名和密码到私服上找对应的仓库组进行下载,然后再给IDEA
上面所说的这些内容,我们需要在本地Maven的配置文件settings.xml
中进行配置。 😎
步骤一:下载Nexus
步骤二:配置私服的名称
在maven的settings.xml文件中
XML
<server>
<id>deploymentRepo</id>//私服中服务器id名称
<username>repouser</username>
<password>repopwd</password>
</server>
改为
XML
<server>
<id>text-release</id>//私服中服务器id名称
<username>repouser</username>
<password>repopwd</password>
</server>
步骤三:配置私服的url访问路径
在settings.xml中
XML
<mirrors>
<!-- mirror
| Specifies a repository mirror site to use instead of a given repository. The repository that
| this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
| for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
|
<mirror>
<id>mirrorId</id>
<mirrorOf>repositoryId</mirrorOf>//仓库组id
<name>Human Readable Name for this Mirror.</name>
<url>http://my.repository.com/repo/path</url>
</mirror>
-->
<mirror>
<id>maven-default-http-blocker</id>
<mirrorOf>external:http:*</mirrorOf>
<name>Pseudo repository to mirror external repositories initially using HTTP.</name>
<url>http://0.0.0.0/</url>
<blocked>true</blocked>
</mirror>
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
步骤四:创建仓库
资源上传和下载
步骤1:配置工程上传私服的具体位置
在idea的pop.xml中配置
XML
<!--配置当前工程保存在私服中的具体位置-->
<distributionManagement>
<repository>
<!--和maven/settings.xml中server中的id一致,表示使用该id对应的用户名和密码-->
<id>itheima-release</id>
<!--release版本上传仓库的具体地址-->
<url>http://localhost:8081/repository/itheima-release/</url>
</repository>
<snapshotRepository>
<!--和maven/settings.xml中server中的id一致,表示使用该id对应的用户名和密码-->
<id>itheima-snapshot</id>
<!--snapshot版本上传仓库的具体地址-->
<url>http://localhost:8081/repository/itheima-snapshot/</url>
</snapshotRepository>
</distributionManagement>
步骤2:发布资源到私服
😘注意:
要发布的项目都需要配置distributionManagement
标签,要么在自己的pom.xml中配置,要么在其父项目中配置,然后子项目中继承父项目即可。
如果私服中没有对应的jar,会去中央仓库下载,速度很慢。可以配置让私服去阿里云中下载依赖。