SSM-Maven补充、RESTful
Maven补充-依赖冲突
- spring-webmvc、spring-jdbc都依赖spring-beans,如果spring-webmvc的版本是5.2.6,那么它依赖的spring-beans默认版本也是5.2.6,如果spring-jdbc的版本是5.2.8,那么它依赖的spring-beans默认版本也是5.2.8,如果将5.2.6版本的spring-webmvc、5.2.8版本的spring-jdbc放一起
- 会出现2个版本的spring-beans,这就是产生了依赖冲突,需要想办法只保留一个版本的spring-beans
依赖冲突的解决方案
1.方案一:
- Maven默认情况下:优先保留前面先声明的版本
-
根据下面的代码,最终spring-beans将会是5.2.6
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency>
2.方案二:
- 单独为依赖库增加dependency指定版本号
-
根据下面的代码,最终spring-beans将会是5.2.8
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency> <!-- 直接指定使用哪个版本 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.8.RELEASE</version> </dependency>
3.方案三:
- 使用exclusion排除某个依赖
-
根据下面的代码,最终spring-beans将会是5.2.6
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> <!-- 删除当前包依赖的spring-beans --> <exclusions> <exclusion> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency>
4.方案四:
- 最常用的解决方案:使用dependencyManagement锁定依赖库的版本号
- 根据下面的代码,最终spring-beans将会是5.2.8
-
注意: dependencyManagement只是声明版本号,并不会触发下载导入依赖库
<properties> <spring.version>5.2.8.RELEASE</spring.version> </properties> <!-- 仅用于锁定库的版本号,并不会去下载导入库--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- 会去下载导入库 --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.6.RELEASE</version> </dependency> </dependencies>
统一指定版本
-
通过dependencyManagement指定好版本,然后dependency中不需要再写版本
<!--统一设置所有依赖的版本号--> <properties> <spring.version>5.2.8.RELEASE</spring.version> </properties> <!--统一固定所有依赖的版本号码--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </dependencyManagement> <!--不用在设置版本号--> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> </dependencies>
Maven补充-分模块构建项目
- 项目规模比较庞大时,可以考虑对项目进行拆分,分模块进行构建项目
- 有2种常见的拆分思路
- 按业务模块:员工模块、部门模块、工资模块等
- 按层:dao层、service层、web层
初始化
- 新建一个Maven项目20_parent(同2)
- 右击20_parent->new->Module…->Maven->next->项目的默认路径就在20_parent下面,项目命名为dao->finished
-
此时会发现20_parent的pom.xml出现了以下标签
<!-- 只要这个项目想作为别人的父项目,打包方式必须为pom --> <packaging>pom</packaging> <!--聚合,可以通过modules标签聚合多个项目,可以对多个项目进行统一构建管理,通常会在父项目中聚合它的子项目--> <modules> <module>dao</module> </modules>
-
子项目dao的pom.xml如下:
<!-- 指定父项目 --> <parent> <artifactId>20_parent</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <!--当前项目的id、版本号--> <artifactId>dao</artifactId> <version>1.0-SNAPSHOT</version>
继承
-
子项目继承父项目后,可以继承父项目中的pom.xml文件中的一些内容,比如:dependencies、dependencyManagement、properties等内容,在20_parent的pom.xml添加如下
<!-- 这个变量,在dao项目中可以直接引用--> <properties> <spring.version>5.2.8.RELEASE</spring.version> </properties> <!-- 仅用于锁定库的版本号,并不会去下载导入库--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </dependencyManagement> <!-- 会去下载导入库 --> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> </dependencies>
- 父项目的这些东西,可以在子项目中直接使用,或者pom.xml中直接使用
-
同理分别新建service、web层,此时项目一级目录如下
20_parent dao //子项目 service //子项目 web //子项目 src //父项目创建时自带 20_parent.iml //父项目创建时自带 pom.xml //父项目创建时自带
-
注意: 子项目是不可以使用父项目的类的,因此直接把父项目src文件夹删除即可
隐藏项目不使用的文件夹
- 分模块后,项目结构看起来会复杂一点,建议隐藏一些不常用的文件或文件夹
- 比如上面项目,会有4个iml文件(20_parent.iml、dao.iml、service.iml、web.iml),iml文件是用于说明idea的,因此整个项目不会使用
- idea配置:(Preverences)Setting->Editor->File Types ->Ignore Files and Folders,写上
*.iml;
,点击OK即可,此时打开项目发现没有.iml文件了
- idea配置:(Preverences)Setting->Editor->File Types ->Ignore Files and Folders,写上
-
此时项目文件目录如下:
20_parent dao //子项目 src main test pom.xml service //子项目 src main test pom.xml web //子项目 src main test pom.xml pom.xml //父项目创建时自带
依赖
- service、dao、web分别放各自的代码,那么他们之间如何依赖的呢?
- 一个项目A可以使用dependency依赖另一个项目B
- 项目A中可以直接拥有项目B的classpath中的内容(比如类、配置文件、依赖库等)
- 举例
-
在dao项目下新建一个domain、一个dao
//domain下的模型类 //com.zh.domain.Skill //dao下的dao类 //com.zh.dao.SkillDao
-
在service项目下新建一个service
//service //com.zh.service.SkillService; //SkillServiceImpl //com.zh.service.impl.SkillServiceImpl;
-
- service项目依赖于dao项目,要使用到dao项目中的Skill、SkillDao,该怎么办呢?
-
在service项目的pom.xml添加对dao项目的依赖即可,这样就可以在service项目中直接使用dao项目中的所有类了
<dependencies> <dependency> <groupId>org.example</groupId> <artifactId>dao</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
- 同理在web项目中创建Controller
-
创建SkillController类
//controller类 //com.zh.controller.SkillController
-
再在pom.xml中添加对servcie的依赖
<dependencies> <dependency> <groupId>org.example</groupId> <artifactId>service</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
-
注意: Controller既可以使用service中的类,也可以使用dao中的类
-
继承与依赖的区别
- 继承:要使用一个项目的pom.xml文件中的配置
- 依赖:要使用一个项目中的所有类、配置文件(properties)、依赖库(pom.xml中的dependencies)等
各模块的分工
1.父模块20_parent
-
添加pom.xml
<modelVersion>4.0.0</modelVersion> <!-- 项目的坐标--> <groupId>org.example</groupId> <artifactId>20_parent</artifactId> <version>1.0-SNAPSHOT</version> <!-- 只要这个项目想作为别人的父项目,打包方式必须为pom --> <packaging>pom</packaging> <!-- 聚合--> <modules> <module>dao1</module> <module>service1</module> <module>web1</module> </modules> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>
2.dao模块
-
添加pom.xml
<modelVersion>4.0.0</modelVersion> <!-- 指定父项目 --> <parent> <artifactId>20_parent</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>dao1</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.11.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies>
-
resources中添加db.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/test-mybatis jdbc.username=root jdbc.password=root mybatis.typeAliasesPackage=com.zh.domain mybatis.mapperScan=com.zh.dao mybatis.configLocation=mybatis-config.xml
-
resources中添加mybatis-config.xml
<configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>
-
添加DaoConfig
//专门用于dao层的IoC容器bean //com.zh.cfg.DaoConfig @Configuration @PropertySource("classpath:db.properties") @MapperScan("${mybatis.mapperScan}") public class DaoConfig { @Value("${jdbc.driverClassName}") private String driverClassName; @Value("${jdbc.url}") private String url; @Value("${jdbc.username}") private String username; @Value("${jdbc.password}") private String password; @Value("${mybatis.typeAliasesPackage}") private String typeAliasesPackage; @Value("${mybatis.configLocation}") private String configLocation; @Bean public DataSource dataSource() { DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName(driverClassName); ds.setUrl(url); ds.setUsername(username); ds.setPassword(password); return ds; } @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setTypeAliasesPackage(typeAliasesPackage); bean.setConfigLocation(new ClassPathResource(configLocation)); return bean; } }
-
Skill跟SkillDao代码如下
//domain public class Skill { private Integer id; private Date createdTime; private String name; private Integer level; } //dao public interface SkillDao { @Insert("INSERT INTO skill(name, level) VALUES (#{name}, #{level})") boolean save(Skill skill); @Update("UPDATE skill SET name = #{name}, level = #{level} WHERE id = #{id}") boolean update(Skill skill); @Delete("DELETE FROM skill WHERE id = #{id}") boolean remove(Integer id); @Select("SELECT * FROM skill") List<Skill> list(); @Select("SELECT * FROM skill WHERE id = #{id}") Skill get(Integer id); }
3.service层
-
pom.xml
<parent> <artifactId>20_parent</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>service1</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.example</groupId> <artifactId>dao1</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--AOP使用--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.6</version> </dependency> </dependencies>
-
添加ServiceConfig
//com.zh.cfg.ServiceConfig @Configuration @EnableTransactionManagement @ComponentScan("com.zh.service") //引入dao层的配置 @Import(DaoConfig.class) //专门用于service层的IoC容器配置 public class ServiceConfig { //事务管理器 @Bean public DataSourceTransactionManager mgr(DataSource dataSource) { DataSourceTransactionManager mgr = new DataSourceTransactionManager(); mgr.setDataSource(dataSource); return mgr; } }
-
SkillService/SkillServiceImpl代码如下
//com.zh.service.SkillService public interface SkillService { boolean save(Skill skill); List<Skill> list(); Skill get(Integer id); boolean remove(Integer id); } //com.zh.service.impl.SkillServiceImpl @Service @Transactional public class SkillServiceImpl implements SkillService { @Autowired private SkillDao dao; @Override public boolean save(Skill skill) { Integer id = skill.getId(); if (id == null || id < 1) { return dao.save(skill); } return dao.update(skill); } @Override @Transactional(readOnly = true) public List<Skill> list() { return dao.list(); } @Override @Transactional(readOnly = true) public Skill get(Integer id) { return dao.get(id); } @Override public boolean remove(Integer id) { return dao.remove(id); } }
4.web层
-
pom.xml
<parent> <artifactId>20_parent</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>web1</artifactId> <version>1.0-SNAPSHOT</version> <!--web项目必备--> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.example</groupId> <artifactId>service1</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.8.RELEASE</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.3.3</version> </dependency> </dependencies>
- 然后添加web.xml,webapp、WEBINF等,配置成web项目,跟之前配置一样
- 删除掉web.xml,使用纯注解
-
SkillController代码:
@Controller @RequestMapping("/skills") public class SkillController { @Autowired private SkillService service; @GetMapping("/list") @ResponseBody public List<Skill> list() { return service.list(); } @GetMapping("/get") @ResponseBody public Skill list(Integer id) { return service.get(id); } @PostMapping("/save") @ResponseBody public String save(Skill skill) { return service.save(skill) ? "保存成功" : "保存失败"; } @PostMapping("/remove") @ResponseBody public String remove(Integer id) { return service.remove(id) ? "保存成功" : "保存失败"; } }
- 添加包cfg
-
替代web.xml的类WebInitializer
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{WebConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{SpringMVCConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/"}; } @Override public void onStartup(ServletContext servletContext) throws ServletException { // 需要调用super super.onStartup(servletContext); // 添加Filter FilterRegistration.Dynamic encodingFilter = servletContext.addFilter( "CharacterEncodingFilter", CharacterEncodingFilter.class); encodingFilter.setInitParameter("encoding", "UTF-8"); encodingFilter.addMappingForUrlPatterns(null, false, "/*"); } }
-
SpringIoC容器类WebConfig
package com.zh.cfg; import org.springframework.context.annotation.Import; //导入容器配置 @Import(ServiceConfig.class) public class WebConfig {}
-
SpringMVC容器类SpringMVCConfig
@ComponentScan("com.zh.controller") @EnableWebMvc public class SpringMVCConfig implements WebMvcConfigurer { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } /** * 相当于<mvc:message-converters> */ @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); stringConverter.setDefaultCharset(StandardCharsets.UTF_8); converters.add(stringConverter); MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); jsonConverter.setDefaultCharset(StandardCharsets.UTF_8); converters.add(jsonConverter); } }
-
-
webapp下添加index.jsp
<body> <div> <a href="${ctx}/skills/get?id=1" target="_blank">单个</a> </div> <div> <a href="${ctx}/skills/list" target="_blank">列表</a> </div> <div> 保存 <form action="${ctx}/skills/save" method="post"> <input name="id" placeholder="id"> <input name="name" placeholder="name"> <input name="level" placeholder="level"> <button type="submit">保存</button> </form> </div> <div> 删除 <form action="${ctx}/skills/remove" method="post"> <input name="id" placeholder="id"> <button type="submit">删除</button> </form> </div> </body>
启动部署项目
- 点击Tomcat->Edit Configuration->Deployment->点击下面的+,找到Artifact…->web:war expload
- 即只需要部署web层的项目即可
optional
-
3个项目的依赖关系如下
<!-- project_a--> <dependencies> <dependency> <groupId>com.zh</groupId> <artifactId>project_b</artifactId> </dependency> </dependencies> <!-- project_b--> <dependencies> <dependency> <groupId>com.zh</groupId> <artifactId>project_c</artifactId> <!--optional指定是否依赖,true:可选择的--> <optional>true</optional> </dependency> <dependency> <groupId>com.zh</groupId> <artifactId>project_d</artifactId> </dependency> </dependencies>
-
最终的依赖结果是
- project_a依赖project_b、project_d,project_a并不依赖project_c,除非project_a显式声明依赖project_c
RESTful
RESTful简介
- REST的全称是:REpresentational State Transfer,译为“表现层状态转移”
- REST是一种互联网软件架构设计风格:定义了一组用于创建Web服务(给客户端提供的后台服务)的约束、符合REST架构的Web服务,称为RESTful Web服务
- 总结:就是一个规范标准用来规范后台接口的路径、请求方式
RESTful风格的实践建议
- URL中使用名词(建议用复数形式),不使用动词
- 推荐:/users 、/users/6
- 不推荐:/listUsers、/getUser?id=6、/user/list、 /user/get?id=6
- 使用HTTP的请求方法来表达动作
- 一个资源连接到其他资源,使用子资源的形式
- GET /users/6/cars/88:查询id为6的用户的所有车的第88号车
- POST /user/8/cars:给id为8的用户添加一两车
- API版本化
- zh.com/v1/users
- zh.com/v2/user/66
- 返回JSON格式的数据
- 发生错误时,不要返回200状态码
关于put、delete
- 浏览器的form不支持put、delete,可以使用HiddenHttpMethodFilter进行转化
- put请求:
post + _method=put
- delete请求:
post + _method=delete
- put请求:
- 注意: 为了安全意见,很多公司严格禁用PUT、DELETE请求,Tomcat现在默认已经不支持PUT、DELETE请求
- 举例
- 表单
<!--post--> <form action="${ctx}/skills/10" method="post"> <!--delete请求:_method=delete --> <input type="hidden" name="_method" value="delete"> <!--put请求:_method=put --> <!--<input type="hidden" name="_method" value="put">--> <button type="submit">提交</button> </form>
-
web.xml添加filter
<filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
- put会调用Controller中用@PutMapping修饰的方法、delete会调用Controller中用@DeleteMapping修饰的方法
- 表单
@RestController
@RestController=@Controller + @ResponseBody
- 相当于默认给所有方法都增加了
@ResponseBody
,如果还是希望跳转页面就返回ModelAndView对象,则不会自动添加@ResponseBody
// 相当于添加了@Controller,而且给每个方法都添加了@ResponseBody
@RestController
@RequestMapping("/skills")
public class SkillController {
@GetMapping("/{id}")
//@ResponseBody
public Skill get(@PathVariable("id") Integer id) {
// service.get(id);
System.out.println("Get------");
return null;
}
@DeleteMapping("/{id}")
public String remove(@PathVariable("id") Integer id) {
// service.remove(id);
return "删除成功!";
}
@PutMapping("/{id}")
public String update(@PathVariable("id") Integer id, Skill skill) {
// skill.setId(id);
// service.update(skill);
return "更新成功";
}
@PostMapping("/")
public String save(Skill skill) {
// service.save(skill)
return "添加成功";
}
@GetMapping("/")
public List<Skill> list() {
// service.list()
return null;
}
//ModelAndView不会自动添加@ResponseBody
@GetMapping("/test")
public ModelAndView test() {
ModelAndView mv = new ModelAndView();
mv.setViewName("/page/index.jsp");
mv.addObject("name", "mj");
return mv;
}
}