前言
"4S"框架是我为了区别传统的SSH和SSM说法,提出的一个叫法。自从有了Spring MVC,特别是Spring Boot后,Java服务已经从臃肿的配置中解放出来。Java 服务也可以满足快速构建的需要,迭代速度上并不比PHP,NodeJS差什么。但是很多公司的Java技术栈过于陈旧,无法发挥Java的现有优势。本文主要阐述如何快速优雅的构建一个完整的Java工程。本文提供4S框架的工程化方案。
内容包括:
- 工程构建
- 包结构设计
- 工程配置
- 业务配置
- ORM层实现
本文意图:
- 提高企业开发的效率
- 优化项目结构
- "4S"是个单体服务,但是可以为"5S"(+Spring Cloud)构建微服务提供基础
1.0 从SSH和SSM到"4S"
- SSH:Structs(控制层),hibernate(ORM),Spring
- SSM:SpringMVC(控制层),MyBatis(ORM),Spring
以上就是Java服务中最常见的框架思路。这3个最常见的框架之间涉及到框架的整合,而且框架本身的使用也涉及到大量的配置,如MyBatis的Mapping文件。如果能去掉这些框架整合的部分,当然不光是这三个框架的整合,实际业务还包括缓存,消息中间件等大量框架的整合,会十分的美好。如果再优化下ORM映射的过程,会更加的美好。于是有了"4S"。
- "4S":SpringMVC(控制层),Spring Data JPA(ORM),Spring Boot(自动化配置),Spring
2.0 "4S"工程创建
推荐IntelliJ IDEA构建Spring Boot项目。动手点一点,三步搞定一个Spring Boot的Maven工程,就问你简单不简单?
搞定后,找到包的根目录下的Application文件,运行main函数,一个Java服务就启动了,连Tomcat都不用配置。
3.0 工程包结构设计
提供一种基于4S框架的分包思路,供参考。
- 1是工程相关的配置信息。
- 2是数据库相关的业务信息。bean中维护了与数据库表结构对应的信息。repository代替传统的dao层,维护数据库的操作。
- 3是全局的业务相关的配置信息。包括全局的异常处理,拦截器,工具类,全局缓存。
- 4是业务的主体。这里只分为2层,controller中是控制层,service中是业务处理主体。
- 5和/resource/value目录对应。用配置的方式来维护一些常量,类似于之前的constance的作用。
- 6是程序的入口,因为Spring Boot的配置信息会自动扫描和该文件同级的目录及其子目录的信息,故放在根目录下。
- 7是前端框架
- 8是前端页面
- 9是工程的配置文件
4.0 工程配置
4.1 不同环境配置
开发环境和测试环境连接的数据库和一些配置信息不同,可以通过在application.properties指定不同的配置文件
# 配置环境 正式环境release 开发环境debugspring.profiles.active=debug复制代码
然后在application-debug.properties中配置测试环境信息,在application-release.properties中配置正式环境信息。
4.2 日志配置
日志配置很简单,在application.properties做点配置即可,列举几条常用的
# 日志地址logging.file=D:/springBoot/log.log# 日志打印级别logging.level.org.springframework.web=DEBUG复制代码
4.3 常量
/value/JavaBean 和 /resource/value/xxx.properties 建立一一对应的关系。可以通过Bean对象获取xxx.properties中的常量信息。举个例子。
/resource/value/user.propertiesuser.name=wolearnuser.age=12复制代码
/java/包名/value/UserProperty.java@Component@ConfigurationProperties(prefix = "user", ignoreUnknownFields = false)@PropertySource("classpath:/value/user.properties")public class UserProperty { private String name; private Long age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getAge() { return age; } public void setAge(Long age) { this.age = age; }}复制代码
对应的配置文件和JavaBean建立完毕后,可以直接通过注解注入,直接获取常量的值。
@Resourceprivate UserProperty userProperty;public static void main(String[] args) { System.out.print("name: " + userProperty.getName() + " age: " + userProperty.getAge();")}复制代码
5.0 业务配置
5.1 全局异常处理
可以使用AOP或者@ControllerAdvice注解来做全局控制。这里使用注解的形式做全局的异常处理。发生异常时,跳转到error.html页面。
/java/包名/global/advice@org.springframework.web.bind.annotation.ControllerAdvicepublic class ControllerAdvice { /** * 统一处理异常 * @param exception * @param webRequest * @return */ @ExceptionHandler(Exception.class) public ModelAndView exception(Exception exception, WebRequest webRequest) { return new ModelAndView("error"); }}复制代码
5.2 简单视图映射
有些简单的请求,直接返回视图的,不用直接新建一个完整的controller,可以通过配置直接路由。
@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter{ @Override public void addViewControllers(ViewControllerRegistry registry) { // 路由和视图映射 registry.addViewController("/user").setViewName("/user"); }}复制代码
当访问/user路由时,直接返回user.html页面。
5.3 拦截器
在请求的前后,对全局的请求做拦截。
/** * Created by wulei on 2017/6/8. * * 全局的时间拦截器 */public class TimeInterceptor extends HandlerInterceptorAdapter{ private static Logger logger = Logger.getLogger(TimeInterceptor.class); /** * 请求执行前 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.debug(request.getServletPath() + " StartTime:" + System.currentTimeMillis()); return super.preHandle(request, response, handler); } /** * 请求执行后 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.debug(request.getServletPath() + " EndTime:" + System.currentTimeMillis()); super.postHandle(request, response, handler, modelAndView); }}复制代码
拦截器定义完成后,要在配置类中实例化。
/** * Created by wulei on 2017/6/8. * * 重新配置MVC */@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter{ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(timeInterceptor()); } @Bean public TimeInterceptor timeInterceptor(){ return new TimeInterceptor(); }}复制代码
6.0 ORM层设计
6.1 连接MySQL
在application.properties配置数据库连接信息
# ----------- DB --------------------spring.jpa.database=MYSQLspring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_spring_boot?serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=root复制代码
Maven中配置JDBC依赖和Spring Data JPA的依赖。JPA是Spring Data的子项目,可以有效减少数据访问层的代码。
mysql mysql-connector-java runtime 复制代码 org.springframework.boot spring-boot-starter-data-jpa
6.2 正向工程建表
我们可以通过定义Bean来定义表结构,并通过正向工程直接在数据库中生成相应的表。举个User的例子。
@Entitypublic class User { @Id @GeneratedValue private Long id; private Integer age; private String name; private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; }}复制代码
- @Entity 声明这是一个跟数据库有映射关系的实体类
- @Id 声明主键ID
- @GeneratedValue 声明自增长
当工程启动的时候,自动生成数据库表。
6.3 数据库操作
通过继承JpaRepository接口,实现数据库操作。JpaRepository已经实现了一些基本的数据库操作
public interface UserRepository extends JpaRepository{ // 按照地址查询地址 List findByAddress(String name);}复制代码
简单数据库操作可以直接调用JpaRepository接口中定好的方法。如保存一个User对象。
@Autowired public UserRepository userRepository; public static void main(String[] args) { User user = new User(); user.setName(name); user.setAddress(address); user.setAge(age); userRepository.save(user); }复制代码
直接注入一个Repository。如果要自定义一个查询地址的方法如上findByAddress即可。更多操作可以参考官方文档。
后话
还是很多东西可以聊,如构建权限控制,缓存的使用,事务的使用,后面慢慢聊。喜欢欢迎点赞,打赏。