"4S"框架快速構建優雅的Java服務

wolearn發表於2017-06-21

前言

"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"

"4S"框架快速構建優雅的Java服務

  • SSH:Structs(控制層),hibernate(ORM),Spring
  • SSM:SpringMVC(控制層),MyBatis(ORM),Spring

以上就是Java服務中最常見的框架思路。這3個最常見的框架之間涉及到框架的整合,而且框架本身的使用也涉及到大量的配置,如MyBatis的Mapping檔案。如果能去掉這些框架整合的部分,當然不光是這三個框架的整合,實際業務還包括快取,訊息中介軟體等大量框架的整合,會十分的美好。如果再優化下ORM對映的過程,會更加的美好。於是有了"4S"。

"4S"框架快速構建優雅的Java服務

  • "4S":SpringMVC(控制層),Spring Data JPA(ORM),Spring Boot(自動化配置),Spring

2.0 "4S"工程建立

推薦IntelliJ IDEA構建Spring Boot專案。動手點一點,三步搞定一個Spring Boot的Maven工程,就問你簡單不簡單?

"4S"框架快速構建優雅的Java服務
第一步

"4S"框架快速構建優雅的Java服務
第二步

"4S"框架快速構建優雅的Java服務
第三步

搞定後,找到包的根目錄下的Application檔案,執行main函式,一個Java服務就啟動了,連Tomcat都不用配置。

3.0 工程包結構設計

"4S"框架快速構建優雅的Java服務

提供一種基於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 開發環境debug
spring.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.properties

user.name=wolearn
user.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建立完畢後,可以直接通過註解注入,直接獲取常量的值。

@Resource
private 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.ControllerAdvice
public class ControllerAdvice {

    /**
     * 統一處理異常
     * @param exception
     * @param webRequest
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView exception(Exception exception, WebRequest webRequest) {
        return new ModelAndView("error");
    }

}複製程式碼

5.2 簡單檢視對映

有些簡單的請求,直接返回檢視的,不用直接新建一個完整的controller,可以通過配置直接路由。

@Configuration
public 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
 */
@Configuration
public 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=MYSQL
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_spring_boot?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root複製程式碼

Maven中配置JDBC依賴和Spring Data JPA的依賴。JPA是Spring Data的子專案,可以有效減少資料訪問層的程式碼。

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>複製程式碼

6.2 正向工程建表

我們可以通過定義Bean來定義表結構,並通過正向工程直接在資料庫中生成相應的表。舉個User的例子。

@Entity
public 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<User, Long> {
    // 按照地址查詢地址
    List<User> 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即可。更多操作可以參考官方文件。

docs.spring.io/spring-data…

後話

還是很多東西可以聊,如構建許可權控制,快取的使用,事務的使用,後面慢慢聊。喜歡歡迎點贊,打賞。

"4S"框架快速構建優雅的Java服務

相關文章