2015-03-26 14:39

Eclipse Git 基本操作

Git 只有本地端(local)跟遠端(remote)的分別,並不是 client 跟 server 的關係,只要是非專案本身的都可以稱為遠端,例如 A 與 B 專案都在自己的電腦裡,A 專案是可以將 B 專案設為遠端進行版本傳遞。

幾個基本日常操作的名稱意義:
  • commit: 將完成的程式提交到版本控制中
  • revert: 放棄修改,將程式回復
  • merge: 將兩個不同版本的程式進行合併
  • pull: 從遠端取回有差異的程式版本
  • push: 將本地端的版本傳到遠端
  • clone: 從遠端複製一個專案到本地



在專案建立 Git 版本控制


在一個專案上滑鼠右鍵選擇 [團隊]->[共享專案]



先建立 Git 儲存庫





將專案中的異動 commit 到 Git 版本控制


隨便建立幾個文件


在專案上右鍵選擇 [團隊]->[確定]


填寫 commit 的訊息,以及選擇要 commit 的檔案




程式回復到尚未修改時


在要回復的檔案右鍵選擇 [取代為]->[HEAD Revision]




版本合併


在專案上右鍵選擇 [團隊]->[Pull] 從遠端取回有差異的程式



當檔案出現衝突時會顯示紅色的標記,這時候檔案右鍵選擇 [團隊]->[Merge Tool]



接著就會出現合併的工具,修改完儲存關閉


最後檔案右鍵選擇 [團隊]->[Add to Index] 將檔案加到 commit 清單中




將本地端的版本傳到遠端






從遠端複製一個專案






指定來源的遠端


目的地目錄最好修改到 Workspace 中




2015-03-13 13:12

[Java] Ant zip 解壓縮筆記

  1. <unzip dest="./target_dir"> 
  2.  
  3.    <!-- 來源的壓縮檔 --> 
  4.    <fileset dir="lib"> 
  5.        <include name="tiles-jsp-*.jar"/> 
  6.    </fileset> 
  7.  
  8.    <!-- 要解出的檔案 --> 
  9.    <patternset> 
  10.        <include name="**/*.tld"/> 
  11.    </patternset> 
  12.  
  13.    <!-- 解出路徑的轉換 --> 
  14.    <mapper type="flatten"/> 
  15.  
  16. </unzip> 

參考文件:
Apache Ant™ User Manual : Unzip Task
Apache Ant™ User Manual : Mapper
2015-03-13 11:59

[Java] Reflection 筆記

  1. @SuppressWarnings("unused") 
  2. Object obj = new Object() { 
  3.    String id = "123"; 
  4.    public String name = "Jax"; 
  5. }; 
  6.  
  7. Class<?> cl = obj.getClass(); 
  8.  
  9. for (Field field : cl.getFields()) { 
  10.    System.out.printf("%s = %s {%s}\n", 
  11.        field.getName(), field.get(obj), field.getType()); 
  12. } 
  13.  
  14. System.out.println("======================="); 
  15.  
  16. for (Field field : cl.getDeclaredFields()) { 
  17.    System.out.printf("%s = %s {%s}\n", 
  18.        field.getName(), field.get(obj), field.getType()); 
  19. } 

Output:
  1. name = Jax {class java.lang.String} 
  2. ======================= 
  3. id = 123 {class java.lang.String} 
  4. name = Jax {class java.lang.String} 
2015-03-10 15:18

[Java] 產生驗證碼圖片

  1. import java.awt.Color; 
  2. import java.awt.Font; 
  3. import java.awt.Graphics; 
  4. import java.awt.image.BufferedImage; 
  5. import java.io.File; 
  6. import java.util.Random; 
  7.  
  8. import javax.imageio.ImageIO; 
  9.  
  10. public class TestCaptchaImage { 
  11.  
  12.    public static void main(String[] args) throws Exception { 
  13.        String captcha = "09875"; 
  14.  
  15.        int width = 55; 
  16.        int height = 20; 
  17.        Color fontColor = new Color(36, 85, 92); /*文字顏色*/ 
  18.        Color lineColor = new Color(65, 161, 175); /*線條顏色*/ 
  19.        Color bgColor = new Color(208, 226, 229); /*底色*/ 
  20.  
  21.        Random random = new Random(); 
  22.        random.setSeed(System.currentTimeMillis()); 
  23.  
  24.        BufferedImage image = new BufferedImage( 
  25.            width, height, BufferedImage.TYPE_INT_RGB 
  26.        ); 
  27.        Graphics g = image.getGraphics(); 
  28.  
  29.        /* 底色 */ 
  30.        g.setColor(bgColor); 
  31.        g.fillRect(0, 0, width, height); 
  32.  
  33.        /* 畫線 */ 
  34.        g.setColor(lineColor); 
  35.        for (int i=0; i < 20; i++) { 
  36.            g.drawLine( 
  37.                random.nextInt(width), 0, 
  38.                random.nextInt(width), height 
  39.            ); 
  40.        } 
  41.  
  42.        /* 畫出文字 */ 
  43.        g.setFont(new Font("Atlantic Inline", Font.BOLD, 20)); 
  44.        g.setColor(fontColor); 
  45.        g.drawString(captcha, 0, 18); 
  46.  
  47.        /* 畫線 */ 
  48.        g.setColor(lineColor); 
  49.        for (int i=0; i < 4; i++) { 
  50.            g.drawLine( 
  51.                0, random.nextInt(height), 
  52.                width, random.nextInt(height) 
  53.            ); 
  54.        } 
  55.  
  56.        g.dispose(); 
  57.  
  58.        ImageIO.write(image, "png", new File("captcha_image.png")); 
  59.    } 
  60. } 

參考文件:
Graphics (Java 2 Platform SE 6)
2015-03-06 17:12

[Java] 製作縮圖筆記

  1. package test_image; 
  2.  
  3. import java.awt.Graphics; 
  4. import java.awt.image.BufferedImage; 
  5. import java.io.File; 
  6.  
  7. import javax.imageio.ImageIO; 
  8.  
  9. public class TestImageResize { 
  10.  
  11.    private static int targetWidth = 120; 
  12.    private static int targetHeight = 80; 
  13.    private static double targetRate = (double) targetWidth / targetHeight; 
  14.  
  15.    public static void main(String[] args) throws Exception { 
  16.  
  17.        System.out.printf("Target w:%s, h:%s, r:%s\n",  
  18.            targetWidth, targetHeight, targetRate); 
  19.  
  20.  
  21.        BufferedImage image = ImageIO.read(new File("input.jpg")); 
  22.  
  23.        int type = image.getType(); 
  24.        if(type == 0) { type = BufferedImage.TYPE_INT_ARGB; } 
  25.  
  26.        int width = image.getWidth(); 
  27.        int height = image.getHeight(); 
  28.        double rate = (double) width / height; 
  29.  
  30.        System.out.printf("Source w:%s, h:%s, r:%s\n", width, height, rate); 
  31.  
  32.  
  33.  
  34.        /* 等比例縮小至指定大小內 */ 
  35.        int rWidth = targetWidth; 
  36.        int rHeight = targetHeight; 
  37.  
  38.        if(width < targetWidth && height < targetHeight) { 
  39.            rWidth = width; 
  40.            rHeight = height; 
  41.        } else if(rate > targetRate) { 
  42.            rHeight = (int) (targetWidth / rate); 
  43.        } else { 
  44.            rWidth = (int) (targetHeight * rate); 
  45.        } 
  46.        System.out.printf("Resize w:%s, h:%s\n", rWidth, rHeight); 
  47.  
  48.        BufferedImage resize1 = new BufferedImage(rWidth, rHeight, type); 
  49.        Graphics g1 = resize1.getGraphics(); 
  50.        g1.drawImage(image, 0, 0, rWidth, rHeight, null); 
  51.        g1.dispose(); 
  52.  
  53.        ImageIO.write(resize1, "jpg", new File("output_1.jpg")); 
  54.  
  55.  
  56.  
  57.  
  58.        /* 等比例縮小填滿指定大小 */ 
  59.        BufferedImage resize2 = new BufferedImage(targetWidth,targetHeight,type); 
  60.        Graphics g2 = resize2.getGraphics(); 
  61.  
  62.        int startX = 0; 
  63.        int startY = 0; 
  64.        int size = 0; 
  65.  
  66.        if(rate > targetRate) { 
  67.            startX = (int) (width - height * targetRate) / 2; 
  68.            size = height; 
  69.        } else { 
  70.            startY = (int) (height - width / targetRate) / 2; 
  71.            size = width; 
  72.        } 
  73.        System.out.printf("x:%s, y:%s, size:%s\n", startX, startY, size); 
  74.  
  75.        g2.drawImage( 
  76.            image, 
  77.            0, 0, targetWidth, targetHeight, 
  78.            startX, startY, (size + startX), (size + startY), 
  79.            null 
  80.        ); 
  81.  
  82.        g2.dispose(); 
  83.  
  84.        ImageIO.write(resize2, "jpg", new File("output_2.jpg")); 
  85.    } 
  86. } 

參考文件:
Graphics (Java 2 Platform SE 6)
2015-03-06 15:33

[Java] Jsoup 筆記

  1. import org.jsoup.Jsoup; 
  2. import org.jsoup.nodes.Document; 
  3. import org.jsoup.nodes.Element; 
  4. import org.jsoup.select.Elements; 
  5.  
  6. public class TestJsoup { 
  7.  
  8.    public static void main(String[] args) throws Exception { 
  9.  
  10.        String url = "http://epg.dishstar.net/calendar.php?s=DISC&d=1"; 
  11.  
  12.        // Document doc = Jsoup.parse(new URL(url), 5000); 
  13.        Document doc = Jsoup.connect(url) 
  14.                            .userAgent("Mozilla/5.0") 
  15.                            .timeout(5000).get(); 
  16.  
  17.        Elements trs = doc.select("table tr"); 
  18.        for (Element tr : trs) { 
  19.            String time = tr.select("td:eq(0)").text(); 
  20.            String title = tr.select("td:eq(1)").text(); 
  21.  
  22.            System.out.println(time + " <=> " + title); 
  23.        } 
  24.    } 
  25. } 
2015-03-06 11:57

Spring JavaMail 筆記

Gmail via SSL
  1. <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> 
  2.    <property name="host" value="smtp.gmail.com" /> 
  3.    <property name="port" value="465" /> 
  4.    <property name="username" value="smtp-user" /> 
  5.    <property name="password" value="smtp-passwd" /> 
  6.  
  7.    <property name="javaMailProperties"> 
  8.        <props> 
  9.            <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> 
  10.            <prop key="mail.smtp.auth">true</prop> 
  11.        </props> 
  12.    </property> 
  13. </bean> 


Gmail via TLS
  1. <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> 
  2.    <property name="host" value="smtp.gmail.com" /> 
  3.    <property name="port" value="587" /> 
  4.    <property name="username" value="smtp-user" /> 
  5.    <property name="password" value="smtp-passwd" /> 
  6.    <property name="javaMailProperties"> 
  7.        <props> 
  8.            <prop key="mail.smtp.starttls.enable">true</prop> 
  9.            <prop key="mail.smtp.auth">true</prop> 
  10.        </props> 
  11.    </property> 
  12. </bean> 


Sample Code
  1. package test_mail; 
  2.  
  3. import javax.mail.Message; 
  4. import javax.mail.MessagingException; 
  5. import javax.mail.internet.MimeMessage; 
  6.  
  7. import org.springframework.context.support.AbstractApplicationContext; 
  8. import org.springframework.context.support.ClassPathXmlApplicationContext; 
  9. import org.springframework.mail.SimpleMailMessage; 
  10. import org.springframework.mail.javamail.JavaMailSender; 
  11. import org.springframework.mail.javamail.MimeMessageHelper; 
  12.  
  13. public class TestSpringMail { 
  14.  
  15.    public static void main( String[] args ) throws Exception { 
  16.  
  17.        AbstractApplicationContext context 
  18.            = new ClassPathXmlApplicationContext("test_mail/spring-mail.xml"); 
  19.  
  20.        JavaMailSender mailSender  
  21.            = (JavaMailSender) context.getBean("mailSender"); 
  22.  
  23.        sample1(mailSender); 
  24.        sample2(mailSender); 
  25.        sample3(mailSender); 
  26.  
  27.        context.close(); 
  28.    } 
  29.  
  30.  
  31.    public static void sample1(JavaMailSender mailSender) throws Exception { 
  32.        MimeMessage mimeMessage = mailSender.createMimeMessage(); 
  33.  
  34.        mimeMessage.setFrom("from@no-spam.com"); 
  35.        mimeMessage.setRecipients( 
  36.            Message.RecipientType.TO, "to@no-spam.com" 
  37.        ); 
  38.        mimeMessage.setSubject("Testing Subject"); 
  39.        mimeMessage.setContent( 
  40.            "<b>Testing Content.</b>",  
  41.            "text/html; charset=utf-8" 
  42.        ); 
  43.  
  44.        mailSender.send(mimeMessage); 
  45.    } 
  46.  
  47.  
  48.    public static void sample2(JavaMailSender mailSender) throws Exception { 
  49.        MimeMessage mimeMessage = mailSender.createMimeMessage(); 
  50.        MimeMessageHelper message = new MimeMessageHelper(mimeMessage, "utf-8"); 
  51.  
  52.        message.setFrom("from@no-spam.com"); 
  53.        message.setTo("to@no-spam.com"); 
  54.        message.setSubject("Testing Subject"); 
  55.        message.setText("<b>Testing Content.</b>", true); 
  56.  
  57.        mailSender.send(mimeMessage); 
  58.    } 
  59.  
  60.  
  61.    public static void sample3(JavaMailSender mailSender) { 
  62.        SimpleMailMessage message = new SimpleMailMessage(); 
  63.        message.setFrom("from@no-spam.com"); 
  64.        message.setTo("to@no-spam.com"); 
  65.        message.setSubject("Testing Subject"); 
  66.        message.setText("Testing Content."); 
  67.  
  68.        mailSender.send(message); 
  69.    } 
  70.  
  71. } 


參考自:
JavaMail API – Sending email via Gmail SMTP example : Mkyong
Spring – Sending e-mail with attachment : Mkyong
Spring – Define an E-mail template in bean configuration file : Mkyong
Spring – Sending E-mail via Gmail SMTP server with MailSender : Mkyong
2015-03-05 10:24

用 Spring 取得 JSP 執行後的字串內容

為了取得執行後的字串內容,我們需要建立 Response 並且替換 PrintWriter,這樣才有辦法取得執行後的內容。

接著可以從 Spring 的 ViewResolver 去取得 View,再透過 View 去處理 render 前的包裝,最後才由 dispatcher 真正去處理 render 的動作。

想到要建立 Request 跟 Response 就感覺讓人頭痛,還好 Spring 有提供 Mock 的類別可以簡單地去建立 Request 跟 Response。


JspRenderer

  1. package com.orion.webmvc.util; 
  2.  
  3. import java.io.IOException; 
  4. import java.io.PrintWriter; 
  5. import java.io.StringWriter; 
  6. import java.io.UnsupportedEncodingException; 
  7. import java.util.Locale; 
  8. import java.util.Map; 
  9.  
  10. import javax.servlet.RequestDispatcher; 
  11. import javax.servlet.ServletContext; 
  12.  
  13. import org.springframework.beans.factory.annotation.Autowired; 
  14. import org.springframework.mock.web.MockHttpServletRequest; 
  15. import org.springframework.mock.web.MockHttpServletResponse; 
  16. import org.springframework.ui.Model; 
  17. import org.springframework.web.servlet.View; 
  18. import org.springframework.web.servlet.ViewResolver; 
  19.  
  20.  
  21. public class JspRenderer { 
  22.  
  23.    @Autowired 
  24.    private ServletContext servletContext; 
  25.  
  26.  
  27.    private ViewResolver viewResolver; 
  28.  
  29.    public void setViewResolver(ViewResolver viewResolver) { 
  30.        this.viewResolver = viewResolver; 
  31.    } 
  32.  
  33.  
  34.    public String render(String viewName, Model model)  
  35.        throws IOException  
  36.    { 
  37.        return render(viewName, model.asMap()); 
  38.    } 
  39.  
  40.    public String render(String viewName, Map<String,Object> modelMap)  
  41.        throws IOException  
  42.    { 
  43.  
  44.        RendererRequest request = new RendererRequest(servletContext); 
  45.        RendererResponse response = new RendererResponse(); 
  46.  
  47.        try { 
  48.            /* 透過 ViewResolver 取得 View 進行 render 的動作 */ 
  49.            View view = viewResolver.resolveViewName( 
  50.                viewName, Locale.getDefault() 
  51.            ); 
  52.            view.render(modelMap, request, response); 
  53.  
  54.            return response.getContentAsString(); 
  55.        } 
  56.        catch(Exception e) { 
  57.            throw new IOException(e); 
  58.        } 
  59.    } 
  60. } 
  61.  
  62.  
  63. class RendererRequest extends MockHttpServletRequest { 
  64.  
  65.    private ServletContext servletContext; 
  66.  
  67.    public RendererRequest(ServletContext servletContext) { 
  68.        this.servletContext = servletContext; 
  69.    } 
  70.  
  71.    @Override 
  72.    public RequestDispatcher getRequestDispatcher(String path) { 
  73.        /* 需要透過真實的 RequestDispatcher 進行 Render */ 
  74.        return servletContext.getRequestDispatcher(path); 
  75.    } 
  76. } 
  77.  
  78.  
  79. class RendererResponse extends MockHttpServletResponse { 
  80.  
  81.    private StringWriter writer = new StringWriter(); 
  82.  
  83.    @Override 
  84.    public PrintWriter getWriter() throws UnsupportedEncodingException { 
  85.        /* 用 StringWriter 作為輸出的容器 */ 
  86.        return new PrintWriter(writer); 
  87.    } 
  88.  
  89.    @Override 
  90.    public boolean isCommitted() { 
  91.        /* true 是為了讓 View 可以採用 include 方式 Render 到 Response */ 
  92.        return true; 
  93.    } 
  94.  
  95.    @Override 
  96.    public String getContentAsString() throws UnsupportedEncodingException { 
  97.        /* 取得 Render 後的內容 */ 
  98.        return writer.getBuffer().toString(); 
  99.    } 
  100. } 


配置 spring.xml

  1. <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
  2.    <property name="prefix" value="/WEB-INF/views/" /> 
  3.    <property name="suffix" value=".jsp" /> 
  4. </bean> 
  5.  
  6. <bean id="jspRenderer" class="com.orion.webmvc.util.JspRenderer"> 
  7.    <property name="viewResolver" ref="viewResolver"/> 
  8. </bean> 


使用範例

  1. //@Autowired 
  2. //private JspRenderer jspRenderer; 
  3.  
  4. Map<String,Object> jspMap = new HashMap<String,Object>(); 
  5. jspMap.put("jspMessage", "中文訊息測試"); 
  6. jspMap.put("costMessage", 4567.89); 
  7.  
  8. String jspOutput = jspRenderer.render("/mailer/test", jspMap); 
  9. System.out.println(jspOutput); 


參考自:Render and capture the output of a JSP as a String | Technological Oddity
2015-03-04 11:33

[Java] FileFilter 筆記

  1. File dir = new File("D:/log"); 
  2.  
  3. File[] list = dir.listFiles(new FileFilter(){ 
  4.    public boolean accept(File file) { 
  5.        return file.getName().endsWith(".txt"); 
  6.    } 
  7. }); 
  8.  
  9. for(File file : list){ 
  10.    System.out.println(file.getAbsoluteFile()); 
  11. } 
  12.  
  13. // 004.txt 
2015-03-04 11:27

[Java] 取得本地端 IP 與 MAC

  1. import java.net.InetAddress; 
  2. import java.net.NetworkInterface; 
  3.  
  4. public class TestInetAddress { 
  5.  
  6.    public static void main(String[] args) throws Exception { 
  7.        InetAddress ip = InetAddress.getLocalHost(); 
  8.  
  9.        System.out.println("Current IP address : " + ip.getHostAddress()); 
  10.        // Current IP address : 192.168.0.109 
  11.  
  12.        System.out.println(ip.getCanonicalHostName()); 
  13.        // 192.168.0.109 
  14.  
  15.        System.out.println(ip.getHostName()); 
  16.        // jaxhu-PC 
  17.  
  18.  
  19.  
  20.        NetworkInterface network = NetworkInterface.getByInetAddress(ip); 
  21.  
  22.        byte[] mac = network.getHardwareAddress(); 
  23.  
  24.        StringBuilder sb = new StringBuilder(); 
  25.        for (int i = 0; i < mac.length; i++) { 
  26.            sb.append(String.format("%s%02X", (i > 0 ? "-" : ""), mac[i])); 
  27.        } 
  28.  
  29.        System.out.println("Current MAC address : " + sb.toString()); 
  30.        // Current MAC address : 38-2C-4A-B4-C3-24 
  31.  
  32.        System.out.println(network.getDisplayName()); 
  33.        // Realtek PCIe GBE Family Controller 
  34.  
  35.        System.out.println(network.getName()); 
  36.        // eth3 
  37.    } 
  38. } 

參考自:How to get MAC address in Java : Mkyong
2015-03-04 11:23

[Java] Object Serializable 筆記

  1. import java.io.FileInputStream; 
  2. import java.io.FileOutputStream; 
  3. import java.io.ObjectInputStream; 
  4. import java.io.ObjectOutputStream; 
  5. import java.io.Serializable; 
  6.  
  7. class Address implements Serializable { 
  8.  
  9.    private static final long serialVersionUID = 1L; 
  10.  
  11.    String street; 
  12.    String country; 
  13.  
  14.    public Address() {} 
  15.  
  16.    public Address(String s, String c) { 
  17.        street = s; country = c; 
  18.    } 
  19.  
  20.    public void setStreet(String street){ this.street = street; } 
  21.    public String getStreet(){ return this.street; } 
  22.  
  23.    public void setCountry(String country){ this.country = country; } 
  24.    public String getCountry(){ return this.country; } 
  25.  
  26.    @Override 
  27.    public String toString() { 
  28.        return String.format("Street : %s Country : %s", street, country); 
  29.    } 
  30. } 
  31.  
  32.  
  33. public class TestSerializable { 
  34.  
  35.    public static void main(String[] args) throws Exception { 
  36.        Address addr = new Address("wall street", "united state"); 
  37.  
  38.        FileOutputStream fout = new FileOutputStream("address.ser"); 
  39.        ObjectOutputStream oos = new ObjectOutputStream(fout); 
  40.        oos.writeObject(addr); 
  41.        oos.close(); 
  42.  
  43.        FileInputStream fin = new FileInputStream("address.ser"); 
  44.        ObjectInputStream ois = new ObjectInputStream(fin); 
  45.        Address addr2 = (Address) ois.readObject(); 
  46.        ois.close(); 
  47.  
  48.        System.out.println(addr2); 
  49.        // Street : wall street Country : united state 
  50.    } 
  51. } 

參考自:
How to read an Object from file in Java : Mkyong
How to write an Object to file in Java : Mkyong
Understand the serialVersionUID : Mkyong
2015-03-04 11:20

[Java] 執行 shell command

  1. Process p = Runtime.getRuntime().exec("ping -n 3 google.com"); 
  2. p.waitFor(); 
  3.  
  4. Scanner sc = new Scanner(p.getInputStream(), "MS950"); 
  5. String output = sc.useDelimiter("\\Z").next(); 
  6. sc.close(); 
  7.  
  8. System.out.println("exitValue: " + p.exitValue()); 
  9. System.out.println(output); 

Output:
  1. exitValue: 0 
  2.  
  3. Ping google.com [173.194.72.138] (使用 32 位元組的資料): 
  4. 回覆自 173.194.72.138: 位元組=32 時間=21ms TTL=48 
  5. 回覆自 173.194.72.138: 位元組=32 時間=20ms TTL=48 
  6. 回覆自 173.194.72.138: 位元組=32 時間=18ms TTL=48 
  7.  
  8. 173.194.72.138 的 Ping 統計資料: 
  9.    封包: 已傳送 = 3,已收到 = 3, 已遺失 = 0 (0% 遺失), 
  10. 大約的來回時間 (毫秒): 
  11.    最小值 = 18ms,最大值 = 21ms,平均 = 19ms 
2015-03-03 16:40

[Java] 自定字符集 Charset

網路上找到有關自定 Charset 的資料很少,大部分的人都是不建議自行定義一個 Charset,的確沒有什麼特別的理由,實在不需要去做這樣的事,但實際遇到的是工作上用的 DB 實在太舊,還在用 Big5 編碼,以及一堆的自造字,偏偏大部分都還是人名,讓別人的名子變成奇怪的字,實在很不禮貌,基於這個理由我延伸了 Big5 Charset。

要製作一個 Charset 一定要瞭解 Java 的字串核心,如果之前是寫 C 語言的朋友,首先要認知 char 跟 btye 是不一樣的,在 Java 的 char 是固定 Unicode 編碼,所有輸入都會轉成 Unicode,輸出時在轉成指定編碼,如下圖:



那麼要實做哪些東西呢??
class Charset
定義 Charset 的名稱跟提供 Encoder 跟 Decoder,跟據 IANA 的規範自定的 Charset 名稱必須以 X-x- 開頭。
class CharsetDecoder
字符解碼器負責將 byte[] 轉換成 char[]
class CharsetEncoder
字符編碼器負責將 char[] 轉換成 byte[]
class CharsetProvider
Charset 提供者,以名稱提供 Charset,在 Charset.forName("xxx") 調用時會尋訪所有的 Provider 來取得 Charset。
META-INF/services/java.nio.charset.spi.CharsetProvider
告知 JVM 將 CharsetProvider 註冊到延伸清單中,JVM 在啟動的時候會掃描所有這個路徑的檔案。

最後再將 {Charset}.jar 檔複製到 {jre}/lib/ext 目錄下就部署完畢


Big5_Extend

  1. public class Big5_Extend extends Charset { 
  2.  
  3.    private static final String BASE_CHARSET = "Big5"; 
  4.    private static final String NAME = "X-Big5-Extend"; 
  5.    private static final String[] ALIASES = { "X-Big5_Extend" }; 
  6.    private Charset baseCharset; 
  7.  
  8.    public Big5_Extend() { 
  9.        this(NAME, ALIASES); 
  10.    } 
  11.  
  12.    public Big5_Extend(String canonical, String[] aliases) { 
  13.        super(canonical, aliases); 
  14.        baseCharset = Charset.forName(BASE_CHARSET); 
  15.    } 
  16.  
  17.    public boolean contains(Charset cs) { 
  18.        return this.getClass().isInstance(cs) || 
  19.                baseCharset.getClass().isInstance(cs); 
  20.    } 
  21.  
  22.    public CharsetDecoder newDecoder() { 
  23.        return new Decoder(this, baseCharset.newDecoder()); 
  24.    } 
  25.  
  26.    public CharsetEncoder newEncoder() { 
  27.        return new Encoder(this, baseCharset.newEncoder()); 
  28.    } 
  29.  
  30.    // ... 
  31. } 
繼承自 Charset,我們只要實作名稱定義跟 Encoder / Decoder 兩件事,當然如果有 char mapping array 也是在這裡初始化,讓所有 Encoder 跟 Decoder 可以共用同一個記憶體空間。


解碼處理

  1. protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { 
  2.    base.reset(); /* 重置狀態 */ 
  3.  
  4.    /* 先用原生的 Big5 進行解碼 */ 
  5.    CoderResult result = base.decode(in, out, true); 
  6.    if(!result.isUnmappable() || in.remaining() < 2){ return result; } 
  7.  
  8.  
  9.    /* 無法轉換,進一步使用自訂的解碼 */ 
  10.    int pos = in.position(); 
  11.    char big5Char = (char)(in.get(pos) << 8 | in.get(pos + 1)); 
  12.    char outChar; 
  13.  
  14.    switch (big5Char) { 
  15.        case '\uFA40': outChar = '\u5803'; break; /* 堃 */ 
  16.        case '\uFA41': outChar = '\u83D3'; break; /* 菓 */ 
  17.        case '\uFA42': outChar = '\u854B'; break; /* 蕋 */ 
  18.        case '\uFA43': outChar = '\u4F8A'; break; /* 侊 */ 
  19.        default: return result; /* 不在清單內直接回傳 */ 
  20.    } 
  21.    out.put(outChar); 
  22.  
  23.    in.position(pos + 2); 
  24.    return decodeLoop(in, out); /* 遞迴處理*/ 
  25. } 
解碼的部分就是先呼叫 big5 原本的 decode,當發生無法解碼的四種狀況就會停止解碼,回傳解碼狀態,我們只要針對 isUnmappable 的狀態接著處裡,base.reset() 是為了清除 Decoder 內部的狀態紀錄,不然會被前一次的解碼結果所影響。


編碼處理

  1. protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { 
  2.    base.reset(); /* 重置狀態 */ 
  3.  
  4.    /* 先用原生的 Big5 進行編碼 */ 
  5.    CoderResult result = base.encode(in, out, true); 
  6.    if(!result.isUnmappable() || out.remaining() < 2){ return result; } 
  7.  
  8.  
  9.    /* 無法轉換,進一步使用自訂的編碼 */ 
  10.    int pos = in.position(); 
  11.    char uniChar = in.get(pos); 
  12.    char outChar; 
  13.  
  14.    switch (uniChar) { 
  15.        case '\u5803': outChar = '\uFA40'; break; /* 堃 */ 
  16.        case '\u83D3': outChar = '\uFA41'; break; /* 菓 */ 
  17.        case '\u854B': outChar = '\uFA42'; break; /* 蕋 */ 
  18.        case '\u4F8A': outChar ='\uFA43'; break; /* 侊 */ 
  19.        default: return result; /* 不在清單內直接回傳 */ 
  20.    } 
  21.    out.put((byte)(outChar >> 8)); 
  22.    out.put((byte)outChar); 
  23.  
  24.    in.position(pos + 1); 
  25.    return encodeLoop(in, out); /* 遞迴處理*/ 
  26. } 
編碼的部分跟解碼採用相同的方式,一樣是先呼叫 big5 原本的 encode。


CoderResult 四種狀態

  • UNDERFLOW 欠位
  • OVERFLOW 溢位
  • MALFORMED 有缺陷的輸入
  • UNMAPPABLE 無映射字符


完整的 Big5_Extend

  1. package com.custom.nio.charset; 
  2.  
  3. import java.nio.CharBuffer; 
  4. import java.nio.ByteBuffer; 
  5. import java.nio.charset.Charset; 
  6. import java.nio.charset.CharsetEncoder; 
  7. import java.nio.charset.CharsetDecoder; 
  8. import java.nio.charset.CoderResult; 
  9.  
  10.  
  11. public class Big5_Extend extends Charset { 
  12.  
  13.    private static final String BASE_CHARSET = "Big5"; 
  14.    private static final String NAME = "X-Big5-Extend"; 
  15.    private static final String[] ALIASES = { "X-Big5_Extend" }; 
  16.    private Charset baseCharset; 
  17.  
  18.    public Big5_Extend() { 
  19.        this(NAME, ALIASES); 
  20.    } 
  21.  
  22.    public Big5_Extend(String canonical, String[] aliases) { 
  23.        super(canonical, aliases); 
  24.        baseCharset = Charset.forName(BASE_CHARSET); 
  25.    } 
  26.  
  27.    public boolean contains(Charset cs) { 
  28.        return this.getClass().isInstance(cs) || 
  29.                baseCharset.getClass().isInstance(cs); 
  30.    } 
  31.  
  32.    public CharsetDecoder newDecoder() { 
  33.        return new Decoder(this, baseCharset.newDecoder()); 
  34.    } 
  35.  
  36.    public CharsetEncoder newEncoder() { 
  37.        return new Encoder(this, baseCharset.newEncoder()); 
  38.    } 
  39.  
  40.  
  41.  
  42.    private class Decoder extends CharsetDecoder { 
  43.        /* Java 原生的 Big5 解碼器 */ 
  44.        private final CharsetDecoder base; 
  45.  
  46.        Decoder(Charset cs, CharsetDecoder base) { 
  47.            super(cs, base.averageCharsPerByte(), base.maxCharsPerByte()); 
  48.            this.base = base; 
  49.        } 
  50.  
  51.        @Override 
  52.        protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { 
  53.            base.reset(); /* 重置狀態 */ 
  54.  
  55.            /* 先用原生的 Big5 進行解碼 */ 
  56.            CoderResult result = base.decode(in, out, true); 
  57.            if(!result.isUnmappable() || in.remaining() < 2){ return result; } 
  58.  
  59.  
  60.            /* 無法轉換,進一步使用自訂的解碼 */ 
  61.            int pos = in.position(); 
  62.            char big5Char = (char)(in.get(pos) << 8 | in.get(pos + 1)); 
  63.            char outChar; 
  64.  
  65.            switch (big5Char) { 
  66.                case '\uFA40': outChar = '\u5803'; break; /* 堃 */ 
  67.                case '\uFA41': outChar = '\u83D3'; break; /* 菓 */ 
  68.                case '\uFA42': outChar = '\u854B'; break; /* 蕋 */ 
  69.                case '\uFA43': outChar = '\u4F8A'; break; /* 侊 */ 
  70.                default: return result; /* 不在清單內直接回傳 */ 
  71.            } 
  72.  
  73.            out.put(outChar); 
  74.  
  75.            in.position(pos + 2); 
  76.            return decodeLoop(in, out); 
  77.        } 
  78.    } 
  79.  
  80.  
  81.  
  82.    private class Encoder extends CharsetEncoder { 
  83.        /* Java 原生的 Big5 編碼器 */ 
  84.        private final CharsetEncoder base; 
  85.  
  86.        Encoder(Charset cs, CharsetEncoder base) { 
  87.            super(cs, base.averageBytesPerChar(), base.maxBytesPerChar()); 
  88.            this.base = base; 
  89.        } 
  90.  
  91.        @Override 
  92.        protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { 
  93.            base.reset(); /* 重置狀態 */ 
  94.  
  95.            /* 先用原生的 Big5 進行編碼 */ 
  96.            CoderResult result = base.encode(in, out, true); 
  97.            if(!result.isUnmappable() || out.remaining() < 2){ return result; } 
  98.  
  99.  
  100.            /* 無法轉換,進一步使用自訂的編碼 */ 
  101.            int pos = in.position(); 
  102.            char uniChar = in.get(pos); 
  103.            char outChar; 
  104.  
  105.            switch (uniChar) { 
  106.                case '\u5803': outChar = '\uFA40'; break; /* 堃 */ 
  107.                case '\u83D3': outChar = '\uFA41'; break; /* 菓 */ 
  108.                case '\u854B': outChar = '\uFA42'; break; /* 蕋 */ 
  109.                case '\u4F8A': outChar ='\uFA43'; break; /* 侊 */ 
  110.                default: return result; /* 不在清單內直接回傳 */ 
  111.            } 
  112.  
  113.            out.put((byte)(outChar >> 8)); 
  114.            out.put((byte)outChar); 
  115.  
  116.            in.position(pos + 1); 
  117.            return encodeLoop(in, out); 
  118.        } 
  119.    } 
  120. } 


CharsetProvider

  1. package com.custom.nio.charset; 
  2.  
  3. import java.nio.charset.Charset; 
  4. import java.util.Collection; 
  5. import java.util.HashMap; 
  6. import java.util.HashSet; 
  7. import java.util.Iterator; 
  8. import java.util.Map; 
  9.  
  10. /** 字元編碼器連結器,用來向 JVM 提交自訂的編碼器 
  11. */ 
  12. public class CharsetProvider extends java.nio.charset.spi.CharsetProvider { 
  13.  
  14.    static Map<String, Charset> name2charset; 
  15.    static Collection<Charset> charsets; 
  16.  
  17.    public Charset charsetForName(String charsetName) { 
  18.        if (charsets == null){ init(); } 
  19.        return name2charset.get(charsetName.toLowerCase()); 
  20.    } 
  21.  
  22.    public Iterator<Charset> charsets() { 
  23.        if (charsets == null){ init(); } 
  24.        return charsets.iterator(); 
  25.    } 
  26.  
  27.    void init() { 
  28.        name2charset = new HashMap<String, Charset>(); 
  29.  
  30.        charsets = new HashSet<Charset>(); 
  31.        charsets.add(new Big5_Extend()); 
  32.  
  33.        for (Charset charset : charsets) { 
  34.            name2charset.put(charset.name().toLowerCase(), charset); 
  35.            for (String aliase: charset.aliases()) { 
  36.                name2charset.put(aliase.toLowerCase(), charset); 
  37.            } 
  38.        } 
  39.    } 
  40. } 


java.nio.charset.spi.CharsetProvider

  1. com.custom.nio.charset.CharsetProvider 
內容就一行類別定義


測試

  1. public class Test { 
  2.  
  3.    public static void main(String[] args) throws Throwable { 
  4.        String charset = "X-Big5-Extend"; 
  5.        String source = "堃菓蕋侊"; 
  6.  
  7.        byte[] bytes = source.getBytes(charset); 
  8.        for (byte b : bytes) { 
  9.            System.out.printf("%x ", b); 
  10.        } 
  11.        System.out.println("\n"); 
  12.        // fa 40 fa 41 fa 42 fa 43 
  13.  
  14.        String result = new String(bytes, charset); 
  15.        System.out.println(result); 
  16.        // 堃菓蕋侊 
  17.    } 
  18. } 

參考自:Java字符编码解码的实现详解_java_脚本之家
2015-03-03 13:34

[轉載] Spring Collections (List, Set, Map, and Properties)

轉載自:Spring Collections (List, Set, Map, and Properties) example

Spring examples to show you how to inject values into collections type (List, Set, Map, and Properties). 4 major collection types are supported :
  • List – <list/>
  • Set – <set/>
  • Map – <map/>
  • Properties – <props/>


Spring beans

A Customer object, with four collection properties.
  1. package com.mkyong.common; 
  2.  
  3. import java.util.List; 
  4. import java.util.Map; 
  5. import java.util.Properties; 
  6. import java.util.Set; 
  7.  
  8. public class Customer 
  9. { 
  10.    private List<Object> lists; 
  11.    private Set<Object> sets; 
  12.    private Map<Object, Object> maps; 
  13.    private Properties pros; 
  14.  
  15.    //... 
  16. } 
See different code snippets to declare collection in bean configuration file.


1. List example

  1. <property name="lists"> 
  2.    <list> 
  3.        <value>1</value> 
  4.        <ref bean="PersonBean" /> 
  5.        <bean class="com.mkyong.common.Person"> 
  6.            <property name="name" value="mkyongList" /> 
  7.            <property name="address" value="address" /> 
  8.            <property name="age" value="28" /> 
  9.        </bean> 
  10.    </list> 
  11. </property> 


2. Set example

  1. <property name="sets"> 
  2.    <set> 
  3.        <value>1</value> 
  4.        <ref bean="PersonBean" /> 
  5.        <bean class="com.mkyong.common.Person"> 
  6.            <property name="name" value="mkyongSet" /> 
  7.            <property name="address" value="address" /> 
  8.            <property name="age" value="28" /> 
  9.        </bean> 
  10.    </set> 
  11. </property> 


3. Map example

  1. <property name="maps"> 
  2.    <map> 
  3.        <entry key="Key 1" value="1" /> 
  4.        <entry key="Key 2" value-ref="PersonBean" /> 
  5.        <entry key="Key 3"> 
  6.            <bean class="com.mkyong.common.Person"> 
  7.                <property name="name" value="mkyongMap" /> 
  8.                <property name="address" value="address" /> 
  9.                <property name="age" value="28" /> 
  10.            </bean> 
  11.        </entry> 
  12.    </map> 
  13. </property> 


4. Properties example

  1. <property name="pros"> 
  2.    <props> 
  3.        <prop key="admin">admin@nospam.com</prop> 
  4.        <prop key="support">support@nospam.com</prop> 
  5.    </props> 
  6. </property> 


Full Spring’s bean configuration file.

  1. <beans xmlns="http://www.springframework.org/schema/beans" 
  2.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  3.    xsi:schemaLocation="http://www.springframework.org/schema/beans 
  4.    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
  5.  
  6.    <bean id="CustomerBean" class="com.mkyong.common.Customer"> 
  7.  
  8.        <!-- java.util.List --> 
  9.        <property name="lists"> 
  10.            <list> 
  11.                <value>1</value> 
  12.                <ref bean="PersonBean" /> 
  13.                <bean class="com.mkyong.common.Person"> 
  14.                    <property name="name" value="mkyongList" /> 
  15.                    <property name="address" value="address" /> 
  16.                    <property name="age" value="28" /> 
  17.                </bean> 
  18.            </list> 
  19.        </property> 
  20.  
  21.        <!-- java.util.Set --> 
  22.        <property name="sets"> 
  23.            <set> 
  24.                <value>1</value> 
  25.                <ref bean="PersonBean" /> 
  26.                <bean class="com.mkyong.common.Person"> 
  27.                    <property name="name" value="mkyongSet" /> 
  28.                    <property name="address" value="address" /> 
  29.                    <property name="age" value="28" /> 
  30.                </bean> 
  31.            </set> 
  32.        </property> 
  33.  
  34.        <!-- java.util.Map --> 
  35.        <property name="maps"> 
  36.            <map> 
  37.                <entry key="Key 1" value="1" /> 
  38.                <entry key="Key 2" value-ref="PersonBean" /> 
  39.                <entry key="Key 3"> 
  40.                    <bean class="com.mkyong.common.Person"> 
  41.                        <property name="name" value="mkyongMap" /> 
  42.                        <property name="address" value="address" /> 
  43.                        <property name="age" value="28" /> 
  44.                    </bean> 
  45.                </entry> 
  46.            </map> 
  47.        </property> 
  48.  
  49.        <!-- java.util.Properties --> 
  50.        <property name="pros"> 
  51.            <props> 
  52.                <prop key="admin">admin@nospam.com</prop> 
  53.                <prop key="support">support@nospam.com</prop> 
  54.            </props> 
  55.        </property> 
  56.  
  57.    </bean> 
  58.  
  59.    <bean id="PersonBean" class="com.mkyong.common.Person"> 
  60.        <property name="name" value="mkyong1" /> 
  61.        <property name="address" value="address 1" /> 
  62.        <property name="age" value="28" /> 
  63.    </bean> 
  64. </beans> 

Run it…
  1. package com.mkyong.common; 
  2.  
  3. import org.springframework.context.ApplicationContext; 
  4. import org.springframework.context.support.ClassPathXmlApplicationContext; 
  5.  
  6. public class App { 
  7.  
  8.    public static void main( String[] args ) { 
  9.  
  10.        ApplicationContext context  
  11.            = new ClassPathXmlApplicationContext("SpringBeans.xml"); 
  12.  
  13.        Customer cust = (Customer)context.getBean("CustomerBean"); 
  14.        System.out.println(cust); 
  15.    } 
  16. } 

Output
  1. Customer [ 
  2.    lists=[ 
  3.        1, 
  4.        Person [address=address 1, age=28, name=mkyong1], 
  5.        Person [address=address, age=28, name=mkyongList] 
  6.    ], 
  7.  
  8.    maps={ 
  9.        key 1=1, 
  10.        key 2=Person [address=address 1, age=28, name=mkyong1], 
  11.        key 3=Person [address=address, age=28, name=mkyongMap] 
  12.    }, 
  13.  
  14.    pros={ 
  15.        admin=admin@nospam.com, 
  16.        support=support@nospam.com 
  17.    }, 
  18.  
  19.    sets=[ 
  20.        1, 
  21.        Person [address=address 1, age=28, name=mkyong1], 
  22.        Person [address=address, age=28, name=mkyongSet] 
  23.    ] 
  24. ] 
2015-03-01 20:14

[Java] Jackson Json Parser 筆記

Object Encode / Decode
  1. import java.util.Arrays; 
  2. import java.util.Date; 
  3.  
  4. import com.fasterxml.jackson.databind.ObjectMapper; 
  5.  
  6. class Album { 
  7.    private int id; 
  8.    private String title; 
  9.    private Date date; 
  10.    private String[] list; 
  11.  
  12.    public int getId() { return id; } 
  13.    public void setId(int id) { this.id = id; } 
  14.  
  15.    public String getTitle() { return title; } 
  16.    public void setTitle(String title) { this.title = title; } 
  17.  
  18.    public Date getDate() { return date; } 
  19.    public void setDate(Date date) { this.date = date; } 
  20.  
  21.    public String[] getList() { return list; } 
  22.    public void setList(String[] list) { this.list = list; } 
  23.  
  24.    @Override 
  25.    public String toString() { 
  26.        return String.format("id: %s, title: %s, date: %s, list: %s", 
  27.            id, title, date, Arrays.toString(list) 
  28.        ); 
  29.    } 
  30. } 
  31.  
  32. public class TestJackson { 
  33.  
  34.    public static void main(String[] args) throws Exception { 
  35.        Album album = new Album(); 
  36.        album.setId(1); 
  37.        album.setTitle("Go Go Go!");; 
  38.        album.setDate(new Date()); 
  39.        album.setList(new String[]{"Love", "Despair"}); 
  40.  
  41.        ObjectMapper jsonMapper = new ObjectMapper(); 
  42.  
  43.        String json = jsonMapper.writeValueAsString(album); 
  44.        System.out.println(json); 
  45.        // {"id":1,"title":"Go Go Go!","date":1425211903948,"list":["Love","Despair"]} 
  46.  
  47.        Album album2 = jsonMapper.readValue(json, Album.class); 
  48.        System.out.println(album2); 
  49.        // id: 1, title: Go Go Go!, date: Sun Mar 01 20:11:43 CST 2015, list: [Love, Despair] 
  50.    } 
  51. } 


Parser to Map
  1. ObjectMapper jsonMapper = new ObjectMapper(); 
  2. Map<String,String> map; 
  3.  
  4. map = jsonMapper.readValue( 
  5.    "{\"name\":\"jax\", \"age\":\"31\"}", 
  6.    new TypeReference<HashMap<String,String>>(){} 
  7. ); 
  8. System.out.println(map); 
  9. // {age=31, name=jax} 
  10.  
  11.  
  12. jsonMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); 
  13. jsonMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); 
  14.  
  15. map = jsonMapper.readValue( 
  16.    "{name:'jax', age:'31'}", 
  17.    new TypeReference<HashMap<String,String>>(){} 
  18. ); 
  19. System.out.println(map); 
  20. // {age=31, name=jax} 


Encode Date
  1. Date date = new Date(); 
  2.  
  3. String json; 
  4. ObjectMapper jsonMapper = new ObjectMapper(); 
  5.  
  6. json = jsonMapper.writeValueAsString(date); 
  7. System.out.println(json); 
  8. // 1425211840183 
  9.  
  10.  
  11. jsonMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); 
  12.  
  13. json = jsonMapper.writeValueAsString(date); 
  14. System.out.println(json); 
  15. // "2015-03-01T12:10:40.183+0000" 

參考自:FasterXML/jackson-databind · GitHub
2015-03-01 18:10

[轉載] Make 命令教程

轉載自:Make 命令教程 - 阮一峰的网络日志

代碼變成可執行文件,叫做編譯(compile);先編譯這個,還是先編譯那個(即編譯的安排),叫做構建(build)。

Make 是最常用的構建工具,誕生於 1977 年,主要用於 C 語言的項目。但是實際上 ,任何只要某個文件有變化,就要重新構建的項目,都可以用 Make 構建。

本文介紹 Make 命令的用法,從簡單的講起,不需要任何基礎,只要會使用命令行,就能看懂。我的參考資料主要是Isaac Schlueter的《Makefile文件教程》《GNU Make手冊》


一、Make 的概念


Make 這個詞,英語的意思是"制作"。Make 命令直接用了這個意思,就是要做出某個文件。比如,要做出文件 a.txt,就可以執行下面的命令。

  1. $ make a.txt 

但是,如果你真的輸入這條命令,它並不會起作用。因為 Make 命令本身並不知道,如何做出 a.txt,需要有人告訴它,如何調用其他命令完成這個目標。

比如,假設文件 a.txt 依賴於 b.txt 和 c.txt ,是後面兩個文件連接(cat 命令)的產物。那麼,make 需要知道下面的規則。

  1. a.txt: b.txt c.txt 
  2.    cat b.txt c.txt > a.txt 

也就是說,make a.txt 這條命令的背後,實際上分成兩步:第一步,確認 b.txt 和 c.txt 必須已經存在,第二步使用 cat 命令 將這個兩個文件合並,輸出為新文件。

像這樣的規則,都寫在一個叫做 Makefile 的文件中,Make 命令依賴這個文件進行構建。Makefile 文件也可以寫為 makefile, 或者用命令行參數指定為其他文件名。

  1. $ make -f rules.txt 
  2. # 或者 
  3. $ make --file=rules.txt 

上面代碼指定 make 命令依據 rules.txt 文件中的規則,進行構建。

總之,make 只是一個根據指定的 Shell 命令進行構建的工具。它的規則很簡單,你規定要構建哪個文件、它依賴哪些源文件,當那些文件有變動時,如何重新構建它。



二、Makefile 文件的格式


構建規則都寫在 Makefile 文件裡面,要學會如何 Make 命令,就必須學會如何編寫 Makefile 文件。


2.1 概述


Makefile 文件由一系列規則(rules)構成。每條規則的形式如下。

  1. <target> : <prerequisites> 
  2. [tab]  <commands> 

上面第一行冒號前面的部分,叫做"目標"(target),冒號後面的部分叫做"前置條件"(prerequisites);第二行必須由一個tab鍵起首,後面跟著"命令"(commands)。

"目標"是必需的,不可省略;"前置條件"和"命令"都是可選的,但是兩者之中必須至少存在一個。

每條規則就明確兩件事:構建目標的前置條件是什麼,以及如何構建。下面就詳細講解,每條規則的這三個組成部分。


2.2 目標(target)


一個目標(target)就構成一條規則。目標通常是文件名,指明Make命令所要構建的對像,比如上文的 a.txt 。目標可以是一個文件名,也可以是多個文件名,之間用空格分隔。

除了文件名,目標還可以是某個操作的名字,這稱為"偽目標"(phony target)。

  1. clean: 
  2.    rm *.o 

上面代碼的目標是 clean,它不是文件名,而是一個操作的名字,屬於"偽目標 ",作用是刪除對像文件。

  1. $ make  clean 

但是,如果當前目錄中,正好有一個文件叫做 clean,那麼這個命令不會執行。因為 Make 發現 clean 文件已經存在,就認為沒有必要重新構建了,就不會執行指定的 rm 命令。

為了避免這種情況,可以明確聲明 clean 是"偽目標",寫法如下。

  1. .PHONY: clean 
  2. clean: 
  3.    rm *.o temp 

聲明 clean 是"偽目標"之後,make 就不會去檢查是否存在一個叫做 clean 的文件,而是每次運行都執行對應的命令。像 .PHONY 這樣的內置目標名還有不少,可以查看手冊

如果 Make 命令運行時沒有指定目標,默認會執行 Makefile 文件的第一個目標。

  1. $ make 

上面代碼執行 Makefile 文件的第一個目標。


2.3 前置條件(prerequisites)


前置條件通常是一組文件名,之間用空格分隔。它指定了"目標"是否重新構建的判斷標准:只要有一個前置文件不存在,或者有過更新(前置文件的 last-modification 時間戳比目標的時間戳新),"目標"就需要重新構建。

  1. result.txt: source.txt 
  2.    cp source.txt result.txt 

上面代碼中,構建 result.txt 的前置條件是 source.txt 。如果當前目錄中,source.txt 已經存在,那麼 make result.txt 可以正常運行,否則必須再寫一條規則,來生成 source.txt 。

  1. source.txt: 
  2.    echo "this is the source" > source.txt 

上面代碼中,source.txt 後面沒有前置條件,就意味著它跟其他文件都無關,只要這個文件還不存在,每次調用 make source.txt,它都會生成。

  1. $ make result.txt 
  2. $ make result.txt 

上面命令連續執行兩次 make result.txt。第一次執行會先新建 source.txt,然後再新建 result.txt。第二次執行,Make 發現 source.txt 沒有變動(時間戳晚於 result.txt),就不會執行任何操作,result.txt 也不會重新生成。

如果需要生成多個文件,往往采用下面的寫法。

  1. source: file1 file2 file3 

上面代碼中,source 是一個偽目標,只有三個前置文件,沒有任何對應的命令。

  1. $ make source 

執行 make source 命令後,就會一次性生成 file1,file2,file3 三個文件。這比下面的寫法要方便很多。

  1. $ make file1 
  2. $ make file2 
  3. $ make file3 


2.4 命令(commands)


命令(commands)表示如何更新目標文件,由一行或多行的 Shell 命令組成。它是構建"目標"的具體指令,它的運行結果通常就是生成目標文件。

每行命令之前必須有一個 tab 鍵。如果想用其他鍵,可以用內置變量 .RECIPEPREFIX 聲明。

  1. .RECIPEPREFIX = > 
  2. all: 
  3. > echo Hello, world 

上面代碼用 .RECIPEPREFIX 指定,大於號(>)替代 tab 鍵。所以,每一行命令的起首變成了大於號,而不是 tab 鍵。

需要注意的是,每行命令在一個單獨的 shell 中執行。這些 Shell 之間沒有繼承關系。

  1. var-lost: 
  2.    export foo=bar 
  3.    echo "foo=[$$foo]" 

上面代碼執行後(make var-lost),取不到 foo 的值。因為兩行命令在兩個不同的進程執行。一個解決辦法是將兩行命令寫在一行,中間用分號分隔。

  1. var-kept: 
  2.    export foo=bar; echo "foo=[$$foo]" 

另一個解決辦法是在換行符前加反斜杠轉義。

  1. var-kept: 
  2.    export foo=bar; \ 
  3.    echo "foo=[$$foo]" 

最後一個方法是加上 .ONESHELL: 命令。

  1. .ONESHELL: 
  2. var-kept: 
  3.    export foo=bar; 
  4.    echo "foo=[$$foo]" 



三、Makefile文件的語法


3.1 注釋


井號(#)在 Makefile 中表示注釋。

  1. # 这是注释 
  2. result.txt: source.txt 
  3.    # 这是注释 
  4.    cp source.txt result.txt # 这也是注释 


3.2 回聲(echoing)


正常情況下,make 會打印每條命令,然後再執行,這就叫做回聲(echoing)。

  1. test: 
  2.    # 这是测试 

執行上面的規則,會得到下面的結果。

  1. $ make test 
  2. # 这是测试 

在命令的前面加上 @,就可以關閉回聲。

  1. test: 
  2.    @# 这是测试 

現在再執行 make test,就不會有任何輸出。

由於在構建過程中,需要了解當前在執行哪條命令,所以通常只在注釋和純顯示的 echo 命令前面加上 @

  1. test: 
  2.    @# 这是测试 
  3.    @echo TODO 


3.3 通配符


通配符(wildcard)用來指定一組符合條件的文件名。Makefile 的通配符與 Bash 一致,主要有星號(*)、問號(?)和 [...] 。比如, *.o 表示所有後綴名為 o 的文件。

  1. clean: 
  2.    rm -f *.o 


3.4 模式匹配


Make 命令允許對文件名,進行類似正則運算的匹配,主要用到的匹配符是 %。比如,假定當前目錄下有 f1.c 和 f2.c 兩個源碼文件,需要將它們編譯為對應的對像文件。

  1. %.o: %.c 

等同於下面的寫法。

  1. f1.o: f1.c 
  2. f2.o: f2.c 

使用匹配符 %,可以將大量同類型的文件,只用一條規則就完成構建。


3.5 變量和賦值符


Makefile 允許使用等號自定義變量。

  1. txt = Hello World 
  2. test: 
  3.    @echo $(txt) 

上面代碼中,變量 txt 等於 Hello World。調用時,變量需要放在 $( ) 之中。

調用Shell變量,需要在美元符號前,再加一個美元符號,這是因為Make命令會對美元符號轉義。

  1. test: 
  2.    @echo $$HOME 

有時,變量的值可能指向另一個變量。

  1. v1 = $(v2) 

上面代碼中,變量 v1 的值是另一個變量 v2。這時會產生一個問題,v1 的值到底在定義時擴展(靜態擴展),還是在運行時擴展(動態擴展)?如果 v2 的值是動態的,這兩種擴展方式的結果可能會差異很大。

為了解決類似問題,Makefile 一共提供了四個賦值運算符 (=、:=、?=、+=),它們的區別請看 StackOverflow

  1. VARIABLE = value 
  2. # 在执行时扩展,允许递归扩展。 
  3.  
  4. VARIABLE := value 
  5. # 在定义时扩展。 
  6.  
  7. VARIABLE ?= value 
  8. # 只有在该变量为空时才设置值。 
  9.  
  10. VARIABLE += value 
  11. # 将值追加到变量的尾端。 


3.6 內置變量(Implicit Variables)


Make命令提供一系列內置變量,比如,$(CC) 指向當前使用的編譯器,$(MAKE) 指向當前使用的Make工具。這主要是為了跨平台的兼容性,詳細的內置變量清單見手冊

  1. output: 
  2.    $(CC) -o output input.c 


3.7 自動變量(Automatic Variables)


Make 命令還提供一些自動變量,它們的值與當前規則有關。主要有以下幾個。


(1)$@

$@指代當前目標,就是Make命令當前構建的那個目標。比如,make foo的 $@ 就指代foo。

  1. a.txt b.txt: 
  2.    touch $@ 

等同於下面的寫法。

  1. a.txt: 
  2.    touch a.txt 
  3. b.txt: 
  4.    touch b.txt 


(2)$<

$< 指代第一個前置條件。比如,規則為 t: p1 p2,那麼$< 就指代p1。

  1. a.txt: b.txt c.txt 
  2.    cp $< $@ 

等同於下面的寫法。

  1. a.txt: b.txt c.txt 
  2.    cp b.txt a.txt 


(3)$?

$? 指代比目標更新的所有前置條件,之間以空格分隔。比如,規則為 t: p1 p2,其中 p2 的時間戳比 t 新,$?就指代p2。


(4)$^

$^ 指代所有前置條件,之間以空格分隔。比如,規則為 t: p1 p2,那麼 $^ 就指代 p1 p2 。


(5)$*

$* 指代匹配符 % 匹配的部分, 比如% 匹配 f1.txt 中的f1 ,$* 就表示 f1。


(6)$(@D) 和 $(@F)

$(@D) 和 $(@F) 分別指向 $@ 的目錄名和文件名。比如,$@是 src/input.c,那麼$(@D) 的值為 src ,$(@F) 的值為 input.c。


(7)$(<D) 和 $(<F)

$(<D) 和 $(<F) 分別指向 $< 的目錄名和文件名。

所有的自動變量清單,請看手冊。下面是自動變量的一個例子。

  1. dest/%.txt: src/%.txt 
  2.    @[ -d dest ] || mkdir dest 
  3.    cp $< $@ 

上面代碼將 src 目錄下的 txt 文件,拷貝到 dest 目錄下。首先判斷 dest 目錄是否存在,如果不存在就新建,然後,$< 指代前置文件(src/%.txt), $@ 指代目標文件(dest/%.txt)。


3.8 判斷和循環


Makefile使用 Bash 語法,完成判斷和循環。

  1. ifeq ($(CC),gcc) 
  2.    libs=$(libs_for_gcc) 
  3. else 
  4.    libs=$(normal_libs) 
  5. endif 

上面代碼判斷當前編譯器是否 gcc ,然後指定不同的庫文件。

  1. LIST = one two three 
  2. all: 
  3.    for i in $(LIST); do \ 
  4.        echo $$i; \ 
  5.    done 
  6.  
  7. # 等同于 
  8.  
  9. all: 
  10.    for i in one two three; do \ 
  11.        echo $i; \ 
  12.    done 

上面代碼的運行結果。

  1. one 
  2. two 
  3. three 


3.9 函數


Makefile 還可以使用函數,格式如下。

  1. $(function arguments) 
  2. # 或者 
  3. ${function arguments} 

Makefile提供了許多內置函數,可供調用。下面是幾個常用的內置函數。


(1)shell 函數

shell 函數用來執行 shell 命令

  1. srcfiles := $(shell echo src/{00..99}.txt) 


(2)wildcard 函數

wildcard 函數用來在 Makefile 中,替換 Bash 的通配符。

  1. srcfiles := $(wildcard src/*.txt) 


(3)subst 函數

subst 函數用來文本替換,格式如下。

  1. $(subst from,to,text) 

下面的例子將字符串 "feet on the street" 替換成 "fEEt on the strEEt"。

  1. $(subst ee,EE,feet on the street) 

下面是一個稍微復雜的例子。

  1. comma:= , 
  2. empty:= 
  3. # space变量用两个空变量作为标识符,当中是一个空格 
  4. space:= $(empty) $(empty) 
  5. foo:= a b c 
  6. bar:= $(subst $(space),$(comma),$(foo)) 
  7. # bar is now `a,b,c'. 


(4)patsubst 函數

patsubst 函數用於模式匹配的替換,格式如下。

  1. $(patsubst pattern,replacement,text) 

下面的例子將文件名 "x.c.c bar.c",替換成 "x.c.o bar.o"。

  1. $(patsubst %.c,%.o,x.c.c bar.c) 


(5)替換後綴名

替換後綴名函數的寫法是:變量名 + 冒號 + 後綴名替換規則。它實際上 patsubst 函數的一種簡寫形式。

  1. min: $(OUTPUT:.js=.min.js) 

上面代碼的意思是,將變量 OUTPUT 中的後綴名 .js 全部替換成 .min.js 。


四、Makefile 的實例



(1)執行多個目標

  1. .PHONY: cleanall cleanobj cleandiff 
  2.  
  3. cleanall : cleanobj cleandiff 
  4.    rm program 
  5.  
  6. cleanobj : 
  7.    rm *.o 
  8.  
  9. cleandiff : 
  10.    rm *.diff 

上面代碼可以調用不同目標,刪除不同後綴名的文件,也可以調用一個目標(cleanall),刪除所有指定類型的文件。


(2)編譯C語言項目

  1. edit : main.o kbd.o command.o display.o 
  2.    cc -o edit main.o kbd.o command.o display.o 
  3.  
  4. main.o : main.c defs.h 
  5.    cc -c main.c 
  6. kbd.o : kbd.c defs.h command.h 
  7.    cc -c kbd.c 
  8. command.o : command.c defs.h command.h 
  9.    cc -c command.c 
  10. display.o : display.c defs.h 
  11.    cc -c display.c 
  12.  
  13. clean : 
  14.    rm edit main.o kbd.o command.o display.o 
  15.  
  16. .PHONY: edit clean 
2015-03-01 15:36

Spring Security 存取控制表示式

常用内建表示式

ps: 定義在 SecurityExpressionRoot

表示式說明
hasRole('role')當前的 User 擁有指定的 Role 就回傳 true
hasAnyRole('role1', 'role2')當前的 User 擁有任一個 Role 就回傳 true
principal當前的 User 的 Principal 物件
authentication當前的 User 的 Authentication 物件
permitAll總是為 true
denyAll總是為 false
isAnonymous()當前的 User 是匿名登入就回傳 true
isRememberMe()當前的 User 是透過 remember-me 登入就回傳 true
isAuthenticated()當前的 User 不是匿名登入就回傳 true
isFullyAuthenticated()當前的 User 不是匿名登入或 remember-me 登入就回傳 true



在方法執行前的驗證


驗證 User 角色
  1. @PreAuthorize("hasRole('ROLE_USER')") 
  2. public void create(Contact contact); 

驗證參數值是否等於 User 名稱
  1. @PreAuthorize("#contact.name == authentication.name") 
  2. public void doSomething(Contact contact); 

驗證 User 角色以及來源 IP 區間
  1. @PreAuthorize("hasRole('admin') and hasIpAddress('192.168.1.0/24')") 
  2. public void doSomething(Contact contact); 



在方法內的驗證


取得角色驗證
  1. @RequestMapping("/index") 
  2. public void index(HttpServletRequest request) { 
  3.    System.out.println(request.isUserInRole("ROLE_USER")); 
  4.  
  5.    if (request.isUserInRole("admin")) { 
  6.        System.out.println("is admin"); 
  7.    } 
  8. } 



在 JSP 的驗證


取得 User 名稱
  1. <sec:authentication property="name"/> 
  2. <sec:authentication property="principal.username"/> 

取得 User IP
  1. <sec:authentication property="details.remoteAddress"/> 

取得 User SessionId
  1. <sec:authentication property="details.sessionId"/> 

驗證角色為 admin 才顯示
  1. <sec:authorize access="hasRole('admin')"> 
  2.    <div>is admin</div> 
  3. </sec:authorize> 

驗證角色為 admin 存入變數 isAdmin
  1. <sec:authorize var="isAdmin" access="hasRole('admin')" /> 
  2. <c:if test="isAdmin"> 
  3.    <div>is admin</div> 
  4. </c:if> 


參考自:15. Expression-Based Access Control