Google Code Prettify

2017年12月31日 星期日

第一個 spring boot web 程式 (使用 embedded tomcat)

經過了一年八個月,這個世界的技術已經又有改變 ... (其實是我當時還沒學到比較新的技術)
現在要用 spring boot 來改寫 spring MVC - getting started 這一篇的程式,同樣的,架構採用 spring MVC,spring framework 最讓人詬病的就是設定非常複雜,spring boot 的出現主要就在解決這個問題。現在開始來看一下程式怎麼寫 …


上圖是程式碼放罝的位置,這是 Gradle 的習慣。
  1. embedded tomcat 不支援 jsp,所以這個程式的顯示都使用 thymeleaf 板型的 html,這是 spring boot 提供的,支援 html 5。
  2. 網頁相關的檔案放在 /src/main/resources 目錄下,html 放在 templates 子目錄,css、javascript 放在 static 子目錄。
  3. spring boot 的設定預設放在 application.properties,這個檔通常放在 classpath 的根目錄 【註】。
buildscript {
    ext {
        springBootVersion = '1.5.9.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

group = 'idv.steven'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-data-jpa') 
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
    compile group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.2.1'
    compile group: 'javax.inject', name: 'javax.inject', version: '1'
    compileOnly('org.projectlombok:lombok')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}
這是 gradle 的設定檔,前一篇採用 maven,這裡使用 gradle,兩個都很流行,看個人習慣,沒什麼特別的好壞之分。紅色那一行,設定了我們要採用的 spring boot 版本,藍色那行指定了要使用 spring boot 插件,上面那行深綠色,指出要依賴 spring boot 插件。因為現在要寫的程式是 web 程式,且會用 JPA 到資料庫中存取使用者資料,並且網頁會套用 thymeleaf 版型,所以寫了橘色那三行設定,gradle 會自行去找出相關依賴的 jar 檔。紫色那行是引入 MariaDB 的 jar 檔,因為這裡的資料庫是使用 MariaDB
package idv.steven.demo.database.entity;

import java.sql.Timestamp;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Table(name="users")
@Data
public class UsersEntity {
    @Id
    private String name;
    private String password;
    private Timestamp createtime;
}
上面是對應到資料庫 table Users 的 entity。
package idv.steven.demo.database.dao;

import org.springframework.data.repository.CrudRepository;
import idv.steven.demo.database.entity.UsersEntity;

public interface UsersDao extends CrudRepository {

}
存取 table Users 就採用 spring data 提供的 CrudRepository,不知道怎麼用可以參考「spring data: CrudRepository 自動生成」。
server.port=8080

spring.datasource.url=jdbc:mariadb://192.168.0.103:3306/demo
spring.datasource.username=root
spring.datasource.password=p@ssw0rd##
  • 資料庫連線相關設定,只要在 application.properties 中如上設定即可,通常只要設定 url、username、password,不需要設定 driver-class-name,spring boot 從 url 就能判斷現在要使用的是那一種資料庫。
  • 第一行 server.port 是設定 tomcat 要啟動後監聽那一個 port,預設就是 8080,如果本來就是要使用 8080,這一行可以刪除。與 tomcat 相關的設定還有:
    • server.session-timeout (以秒為單位)
    • server.context-path (預設為 / )
    • server.tomcat.uri-encoding (預設為 UTF-8)
    • server.tomcat.compression (預設為 off)
package idv.steven.demo.controller;

import javax.inject.Inject;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import idv.steven.demo.database.dao.UsersDao;
import idv.steven.demo.database.entity.UsersEntity;
import idv.steven.demo.model.IndexForm;

@Controller
public class IndexController {
    @Inject
    private UsersDao daoUsers;
 
    @RequestMapping("/index")
    public String login(IndexForm form) {
        if (form.getUserName() != null) {
            UsersEntity user = daoUsers.findOne(form.getUserName());
   
            if (user != null && user.getPassword().equals(form.getPassword())) {
                return "success";
            }
        }
        else {
            return "login";
        }
  
        return "fail";
    } 
}
Controller 如上所示,很簡單的針對傳入的登入帳號、密碼,檢查與資料庫中的是否相同? 相同就導向 success.html 網頁,不相同就導向 fail.html 網頁。
<html xmlns:th="http://www.thymeleaf.org">
<head>
 <link rel="sytlesheet" th:href="@{bootstrap/css/bootstrap.min.css}"></link>
 <link rel="sytlesheet" th:href="@{bootstrap/css/bootstrap-theme.min.css}"></link>
</head>
<body>

 <form action="index.do" method="post">
        帳號: <input name="userName" type="text" />
        密碼: <input name="password" type="password" />
        <input type="submit" value="登入" />
    </form>
</body>
</html>
登入頁,如上所示,簡單的輸入帳號、密碼後按【登入】。
package idv.steven.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApp {
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApp.class, args);
    }
}
最後很重要的,spring boot 要有一個起始的類別,這個類別上面要寫上 @SpringBootApplication 註記,這樣 spring boot 會自動的掃描所有類別與 jar 檔。


如果是在 eclipse 中開發程式,可以在專案名稱上按右鍵,選用「Run As > Spring Boot App」,spring boot 就會啟動 embedded tomcat,然後可以打開瀏覽器測試。
如上,輸入帳號、密碼後按【登入】,即會導向 Controller,再由 Controller 判斷要導向 success.html 或 fail.html。

【註】
application.properties 可以放在以下四個位置:
  1. 外置,在相對於應用程式運行目錄的 /config 子目錄裡。
  2. 外置,在應用程式運行的目錄裡。
  3. 內置,在 config package 內。
  4. 內置,在 classpath 根目錄。
spring boot 會依上列順序搜尋。
〈參考資料〉https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-application-property-files

【補充】
上面的程式是 compile 成 jar 檔,執行在 spring boot 包裝進 jar 檔裡的 embedded tomcat 上,如果想要 compile 成 war 檔並執行在一般的 tomcat,要如下修改:
  • DemoApp.java (啟動類別)
package idv.steven.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;

@SpringBootApplication
public class DemoApp extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(DemoApp.class);
    }
 
    public static void main(String[] args) {
        SpringApplication.run(DemoApp.class, args);
    }
}
如紅色部份所示,繼承 SpringBootServletInitializer,並覆寫 configure method。
  • build.gradle
apply plugin: 'war'
在 build.gradle 中加上如上內容,導入 war 插件,並確定環境變數有設定 JAVA_HOME 及安裝了 gradle,然後在 build.gradle 所在路徑上下如下指令:
gradle war

2017年12月22日 星期五

在 linux 中無法切換身份? (su: Permission denied)

Linux 預設,只有 root 可以切換到別的身份 (帳號),如果要取消這個限制,怎麼做? 如下:

1. 變更授權規則
    編輯 /etc/pam.d/su 檔,找到 auth required 這個項目,原來的值可能如下:
auth            required        pam_wheel.so trust group=wheel
    將它改成如下:
auth            required        pam_wheel.so use_uid
2. 將要能切換到別的身份的帳號加入 wheel 群組
    假設想要 user1 和 user2 可以相互切換,那麼執行如下指令:
usermod -a -G wheel user1
usermod -a -G wheel user2 
再測試看看,應該沒問題了。