2017年6月15日 星期四

Call/Pass by Reference

※Call/Pass by Reference 

Call/Pass by Value:將變數傳進另一隻方法當參數,方法對這個變數所做的事,和原本的沒有關係

Call/Pass by Reference:將變傳進另一隻方法當參數,方法對這個變數所做的事,會影響到原來的

這篇講的是 Call/Pass by Reference

※
<script>
'use strict'
    window.onload = () => {
        var s = new Set();
        zzz(s);
        alert(s.size); // 1
    }
    
    var zzz = (y) => {
        y.add('a');
    }
</script>

※


※new

<script>
'use strict'
    window.onload = () => {
        var s = new Set();
        zzz(s);
        alert(s.size); // 1
    }
    
    var zzz = (y) => {
        y.add('a');
    
        // y = null;
        // y = 'undefined';
        // y = 'xxx';
        y = new Set();
        y.add('b');
        y.add('c');
    }
</script>

※受影響的只在 new 之前,之後自己是獨立的

※如果將 y = null; y = 'undefined';y = 'sss'; 都不會對原來的產生影響

※甚至宣告一個新的 Set,然後賦值給 y 也沒有用

Call/Pass by Reference

※Call/Pass by Reference 

Call/Pass by Value:將變數傳進另一隻方法當參數,方法對這個變數所做的事,和原本的沒有關係。 基本型態

Call/Pass by Reference:將變傳進另一隻方法當參數,方法對這個變數所做的事,會影響到原來的。不是基本型態的屬於這個


※
public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    System.out.println(list);
    xxx(list);
    System.out.println(list);
}
    
public static void xxx(List<String> a){
    a.add("a1");
}

※結果:
[]
[a1]



※new

public static void xxx(List<String> a){
    a.add("a1");
    a.add("a2");
    
    // a = null;
    a = new ArrayList<String>();
    a.add("a3");
    a.add("a4");
    System.out.println("in" + a);
}

※結果:
[]
in[a3, a4]
[a1, a2]

※受影響的只在 new 之前,之後自己是獨立的

※a = null、a = 另外一個 List也都和 new 一樣,對原來的 List 不會產生作用,因為指向了別的記憶體空間

※在 C++ 語言裡,java 只有 call by value,只不過傳的是地址而已,所以不能將整個物件改掉,如這個例子,new 或者將別的記憶體空間指定過來就和原來的沒關係了,但如果物件裡的方法可以改變內容是可以的,如上個例子的 List 的 add 方法就是這樣

※這篇文章有說明,C++ 用 &變數名稱就表示 call by reference,從右邊賦值到左邊是可以的



※另一個範例
public class Test {
    private String xxx = "x";
    // setter/getter...
    
    private List<String> list = new ArrayList<>();
    // setter/getter...

    
    Test() {
        list.add("a");
        list.add("b");
        list.add("c");
    }
        
    public static void main(String[] args) {
        Test t1 = new Test();
        Test2 t2 = new Test2();
    
        t2.setXxx2(t1.xxx);//*
        t2.ooo(t1.getXxx());
        System.out.println(t1.getXxx());
        System.out.println(t2.getXxx2());
    
        System.out.println("\r\n");
    
        t2.setList2(t1.getList());//*
        t2.xxx(t1.getList());
        System.out.println(t1.getList());
        System.out.println(t2.getList2());
    }
}
    
class Test2 {
    private String xxx2;
    // setter/getter...

    private List<String> list2;
    // setter/getter...
    
    public void xxx(List<String> l) {
        l.add("d");
        l.add("e");
        l.add("f");
    }
    
    public void ooo(String s) {
        s = "o";
    }
}

※

2017年5月28日 星期日

安裝 (React 16 一)

寫這篇時,官網的版本是15.5.4
本來是0.14,後面直接升級到15,現在最新是16,原文件
官網文件,中文文件


※安裝

可以下載 js 使用,也可以使用 Node.js

React Developer Tools:chrome 到這裡下載,如果執行的是 react,按 F12 會有 React的活頁標籤,但我試的時候,使用 Node.js 執行沒有問題;但使用下載 js 的方式,按下活頁標籤會出現「Uncaught TypeError: Cannot read property 'nativeObjectCreate' of undefined」的警告

.原本執行 react 時,沒出現警告,但一按 React 活頁標籤出現警告時,連原本的也會出現警告,並不是 react 寫錯了,重新整理沒有用,必需將此 js 關掉,重開即可
反正使用下載 js 的方式就不要用 React Developer Tools

※下載 js

使用 BootCDN,下載四個東西
.react:react 的核心庫
https://cdn.bootcss.com/react/16.3.2/umd/react.development.js

.react-dom:操作 dom
https://cdn.bootcss.com/react-dom/16.3.2/umd/react-dom.development.js

.babel-standalone:使用 JSX
https://cdn.bootcss.com/babel-standalone/7.0.0-beta.3/babel.js

.prop-types:可以為欄位定義型態、是否必輸,不是強制的,但 console 會有警告
https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js

複製網址到新開的頁面,然後整個複製成一份新文件
也可用官網安裝教學的 CDN Links,但必需上網才能使用,或者學 BootCDN 的做法,複製下來也是可以,但只有看到 react 和 react dom


※
<script src="../js/react.development_16.3.2.js"></script>
<script src="../js/react-dom.development_16.3.2.js"></script>
<script src="../js/babel-standalone_7.0.0-beta.3.js"></script>
<script src="../js/prop-types._15.6.1.js"></script>
    
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
    
<script type="text/babel">
    const dom1 = React.createElement('h1', '', 'aaa')
    ReactDOM.render(
        dom1, document.querySelector("#div1")
    );
    
    ReactDOM.render(
        <h1>bbb</h1>, document.querySelector("#div2")
    );
    
    const dom3 = <h1>ccc</h1>;
    ReactDOM.render(
        dom3, document.querySelector("#div3")
    );
</script>

※要注意下語法的 script 是 babel,不是 javascript

※以上三種方法都可以,但要小心第二和第三種方法,不能用「'」或「"」包起來

※createElement 參數依序為標籤名、標籤的屬性、內容

※jsx 的根標籤必需只有一個,如上只有一個 h1,如果有很多個 h1,那就不行,解法可在 h1上面加個根標籤,如 div 之類的

※使用自訂標籤

class Dom1 extends React.Component {
    render() {
        return <h1>aaa</h1>
    }
}
    
const Dom2 = _ => {
    return <h1>bbb</h1>
}
    
ReactDOM.render(
    <Dom2 />, document.querySelector("#div1")
);

※有兩種方法,注意一定要大寫開頭

※render 的第一個參數也是用標籤



※使用 Node.js

安裝好 Node.js 後,如下操作
1.npm install -g create-react-app:安裝 create-react-app 指令
2.create-react-app 專案名稱:新增一個專案
3.進入新專案,內容如下(src backup 是我加的):

4.下 npm start:下完會自動跳出瀏覽器 localhost:3000

改port:package.json,scripts有個start
原本是 react-scripts start,改成set PORT=9000 && react-scripts start,如下:


使用 Node.js 的方式,必需將 react 和 html分開

public 的 index.html 寫 html
src 的 index.js 寫react 語法
package.json 設定可參考這裡或這裡

※src\index.js

import React from 'react';
import ReactDOM from 'react-dom';
    
ReactDOM.render(<h1>Yeah</h1>, document.querySelector("#d1"))

※雖然已經 import,但 ReactDOM 還是要寫

※public\index.html 內容就不貼了,反正一定要有個 id 是 d1 即可

※如果要使用其他組件,如下面 App.js (放在和 index.js 同層) 的語法
import React, { Component } from 'react';
    
class Xxx extends React.Component {
    render() {
        return <h1}>xxx</h1>
    }
}
    
export default Xxx;

※React.Component 可以只寫 Component

※匯出名稱是 Btn

※
import React from 'react';
import ReactDOM from 'react-dom';
import Xxx from './App';
    
ReactDOM.render(<Xxx />, document.querySelector("#d1"))

※引用時也是用 Btn,但 from 是寫 App 的路徑

2017年5月24日 星期三

Ext.define (ExtJS 4 二)

以下為了方便,就不貼Ext.onReady了
文件要在 Ext (最上面) 點一下即可
Ext.define 是定義類別的(最後沒有d),可以使用 Ext.create 和 new 的方式使用這個類


※一、使用Ext.create

Ext.define("xxx.ooo", { 
    aaa : "a1",
    bbb : "b1",
    ccc : function(){
        return this.aaa + " " + this.bbb;
    }
});
    
var a = Ext.create("xxx.ooo");
Ext.Msg.alert("標題", "我是" + a.aaa, function(){
    // var a = Ext.create("xxx.ooo"); 不能寫在裡面
    Ext.Msg.alert("call back 標題", a.ccc());
});

※


※覆蓋

Ext.define("xxx.ooo", { 
    aaa : "a1",
    bbb : "b1"
});
    
Ext.define("xxx.ooo", { 
    aaa : "a2"
});
    
var obj = new xxx.ooo();
alert(obj.aaa); // a2
alert(obj.bbb); // undefined

※因為後者蓋前者,所以 obj.bbb 就沒有了


※setter/getter (config)

Ext.define("xxx.ooo", {
    config : {
        ddd : 'abc',
        eee : 'def'
    }
});
    
var a = Ext.create("xxx.ooo");
    
alert(a.getDdd()); // abc
a.setDdd('aaa');
alert(a.getDdd()); // aaa

※使用內鍵的 config 就可以做到 setter/getter



※二、使用new

Ext.define("xxx.ooo", { 
    aaa : "a1",
    bbb : "b1"
});
    
var obj = new xxx.ooo();
alert(obj.message); // undefined
obj.message = 'msg';
alert(obj.message); // msg
alert(obj.aaa); // a1

※

※繼承 (extend)

Ext.define("xxx.ooo", { 
    aaa : "a1",
    bbb : "b1"
});
    
Ext.define("xxx.ooo.qqq", { 
    extend: 'xxx.ooo' // 最後沒有「s」,最後也不可加「;」
});
    
var obj = new xxx.ooo.qqq();
alert(obj.aaa); // a1
alert(new xxx.ooo.qqq().bbb); // b1

※使用內鍵的 extend 可以做到繼承

※因為繼承的關係,所以 aaa 和 bbb 還是有值


※靜態屬性、靜態方法 (statics、inheritableStatics)

Ext.define("xxx.ooo", { 
    aaa : "a1",
    statics : { // statics,後面有「s」
        bbb : "b1",
        ccc : function(){
            return 'ccc';
        }
    }, 
    inheritableStatics : {
        ddd : "d1",
        eee : function(){
            return 'eee';
        }
    }
});
    
var obj = new xxx.ooo();
alert(obj.aaa); // a1
alert(xxx.ooo.bbb); // b1
alert(xxx.ooo.ccc()); // ccc
// alert(obj.ccc()); // Uncaught TypeError: obj.ccc is not a function
alert(obj.bbb); // undefined
    
alert(xxx.ooo.ddd); // d1
alert(xxx.ooo.eee()); // eee
// alert(obj.ddd()); // Uncaught TypeError: obj.ddd is not a function
alert(obj.eee); // undefined

※使用內鍵的 statics 和 inheritableStatics 可以做到靜態

※靜態屬性沒有時是 undefined,但靜態方法沒有時會報錯,以下無法執行


※statics 和 inheritableStatics 差異

Ext.define("xxx.ooo", { 
    aaa : "a1",
    statics : { // statics,後面有「s」
        bbb : "b1",
        ccc : function(){
            return 'ccc';
        }
    }, 
    inheritableStatics : {
        ddd : "d1",
        eee : function(){
            return 'eee';
        }
    }
});
    
Ext.define("xxx.ooo.qqq", {
    extend: 'xxx.ooo' //最後不可加「;」
});
    
alert(xxx.ooo.qqq.bbb); // undefined
// alert(xxx.ooo.qqq.ccc()); // Uncaught TypeError: xxx.ooo.qqq.ccc is not a function
    
alert(xxx.ooo.qqq.ddd); // d1
alert(xxx.ooo.qqq.eee()); // eee

※statics 無法被繼承;而inheritableStatics可以


※別名 (alias)

Ext.define("xxx.ooo", {
    aaa : "a1",
    alias : 'xo'
});
    
var a = Ext.create('xo');
alert(a.aaa);
// alert(new xo()); // Uncaught ReferenceError: xo is not defined

※通過關鍵字 alias 即可達成,但只適用在Ext.create


※備用類別名稱 (alternateClassName)

Ext.define("xxx.ooo", {
    aaa : 'a1',
    alternateClassName: ['zzz', 'yyy.zzz']
});
    
alert(new xxx.ooo().aaa); // a1
alert(new zzz().aaa); // a1
alert(new yyy.zzz().aaa); // a1
alert(Ext.create('yyy.zzz').aaa); // a1

※比 alias 好,使用 new 和Ext.create 都可以


※建構子 (constructor)

Ext.define("xxx.ooo", {
    aaa : 'default aaa',
    bbb : 'default bbb',
    constructor : function(aaa) {
        this.aaa = aaa;
    },
    constructor : function(aaa, bbb) {
        this.aaa = aaa;
        this.bbb = bbb;
    }
});
    
var obj1 = new xxx.ooo();
alert(obj1.aaa); // undefined
alert(obj1.bbb); // undefined
    
var obj2 = new xxx.ooo('a');
alert(obj2.aaa); // a
alert(obj2.bbb); // undefined
    
var obj3 = new xxx.ooo('a', 'b');
alert(obj3.aaa); // a
alert(obj3.bbb); // b

※注意沒有參數的建構子,屬性都是 undefined,如果沒有加 constructor,可以得到 default aaa和 default bbb




※還有其他內鍵的,在左邊點 Class 即可看見,有 10 個

HelloWorld、事件 (ExtJS 4 一)

※HelloWorld

下載完後的圖
下載完有200多MB,但其實只要 locale 和 resources 資料夾和所有檔案,只要 27MB 左右

文檔連結
上面圖的上一層是一個叫 extjs-4.1.1a 的資料夾,以下的測試檔就是放在這一層

<html>
    <head>
        <title>ExtJS4 Test</title>
        <script src="extjs-4.1.1a/ext-all.js"></script>
        <script src="extjs-4.1.1a/locale/ext-lang-zh_TW.js"></script>
        <script src="extjs-4.1.1a/locale/ext-lang-zh_CN.js"></script>
        <link href="extjs-4.1.1a/resources/css/ext-all.css" rel="stylesheet" />
    </head>
    
    <script>
        Ext.onReady(function(){
            Ext.Msg.alert('標題', '內容');
        });
    </script>
</html>

※可以發現文檔裡還有很多,如prompt、confirm…等,都在Methods裡,當然還有Properties、Events、Config options

※方法和 Ext.onReady 都會後者蓋前者,不是附加哦!

※Ext.onReady 裡面還可以再放 Ext.onReady,但是內層會覆蓋外層,如下:
Ext.onReady(function(){
    Ext.Msg.alert('標題1', '內容1');
    
    Ext.onReady(function(){
        Ext.Msg.alert('標題2', '內容2');
        
        Ext.onReady(function(){
            Ext.Msg.alert('標題3', '內容3');
        });
    
    });
});

※所以最後都被「標題3,內容3」覆蓋


※文檔有寫「Ext.MessageBox alias Ext.Msg」,所以都是一樣的
或者用 var xxx = new Ext.window.MessageBox; 然後再用「xxx.方法」也是 ok 的

※alert還有第三、四個參數,不是必要的,看文檔



※事件

※單一事件

<html>
    <head>
        <title>ExtJS4 Test</title>
        <script src="extjs-4.1.1a/ext-all.js"></script>
        <script src="extjs-4.1.1a/locale/ext-lang-zh_TW.js"></script>
        <script src="extjs-4.1.1a/locale/ext-lang-zh_CN.js"></script>
        <link href="extjs-4.1.1a/resources/css/ext-all.css" rel="stylesheet" />
    </head>
    
    <script>
        Ext.onReady(function(){
            var btn = Ext.get("b");
            btn.on('click', function(a,b,c){
                alert('好痛');
            });
        });
    </script>
    
    <body>
        <button id="b">按我</button>
    </body>
</html>

※Ext.get 回傳 Ext.dom.Element ,裡面有個 on 方法



※多個事件

<html>
    <head>
        <title>ExtJS4 Test</title>
        <script src="extjs-4.1.1a/ext-all.js"></script>
        <script src="extjs-4.1.1a/locale/ext-lang-zh_TW.js"></script>
        <script src="extjs-4.1.1a/locale/ext-lang-zh_CN.js"></script>
        <link href="extjs-4.1.1a/resources/css/ext-all.css" rel="stylesheet" />
    </head>
    
    <script>
        Ext.onReady(function(){
            var btn = Ext.get("b");
            btn.on({
                'click': {
                    fn : function(evt, el, o){
                        alert('好痛');
                    },
                    scope:this,
                    delay:1000
                },
    
                'mouseout': {
                    fn : function() {
                        alert('好爽');
                    }
                }
            });
        });
    </script>
    
    <body>
        <button id="b">按我</button>
    </body>
</html>

※

2017年4月12日 星期三

Spring Web (Spring3.x 二十五)

※
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
    
@WebListener
public class SpringListener implements ServletContextListener {
    private ApplicationContext context;
    
    @Override
    public void contextInitialized(ServletContextEvent e) {
        String ooo = e.getServletContext().getInitParameter("ooo");
        context = new ClassPathXmlApplicationContext(ooo);
        e.getServletContext().setAttribute("xxx", context);
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        ((ClassPathXmlApplicationContext) context).close();
    }
}

※自己模擬一支啟動時的 Spring


※web.xml

<context-param>
    <param-name>ooo</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
    
<!-- <listener> -->
<!-- <listener-class>package.className</listener-class> -->
<!-- </listener> -->

※尤於上面已經有Annotation,所以此時的 listener 可以不要

※Spring API裡的 org.springframework.web.context.ContextLoaderListener
建構子有說明使用 contextClass 或 contextConfigLocation
意思就是我上面的ooo,Spring 取名為 contextConfigLocation,對應的是xml設定檔
但如果用的不是設定檔,是Class,那就寫成 contextClass
寫 ooo 是無效的,不要被誤導了

而ApplicationContext,也就是如上的xxx,被存成一個常數,叫
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

ContextLoaderListener 的父類別 ContextLoader 還有說
預設會去 /WEB-INF/applicationContext.xml 抓設定檔
所以如果你的設定檔放在這個路徑,而且也叫 applicationContext.xml
那 context-param 就可以不用寫



※使用Spring 的Web

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

※ContextLoaderListener 是 ServletContextListener 的子類



※測試

※pom.xml

<properties>
    <jdk.version>1.8</jdk.version>
    <spring.version>3.2.15.RELEASE</spring.version>
</properties>
    
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>
    
<build>
    <finalName>SpringWeb</finalName>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.3</version>
            <configuration>
                <source>${jdk.version}</source>
                <target>${jdk.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

※

※applicationContext.xml

<bean id="b" class="Book">
    <property name="bookName" value="DragonBall" />
    <property name="bookMoney" value="70" />
</bean>

※

※bean

public class Book {
    private String bookName;
    private int bookMoney;
    // setter/getter...
}

※

※jsp頁面

<form action="xxx" method="post">
    <input type="submit" value="送出" />
</form>

※

※Servlet

@WebServlet(urlPatterns = { "/xxx" })
public class ServletTest extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String app = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
        ApplicationContext ctx = (ApplicationContext) getServletContext().getAttribute(app);
    
        System.out.println(ctx.getBeanDefinitionCount());
        for (String s : ctx.getBeanDefinitionNames()) {
            System.out.println(s);
        }
    
        Book b = ctx.getBean("b", Book.class);
        System.out.println(b);
    }
}

※使用 Annotation 時,在啟動時出現 @EnableAsync annotation metadata was not injected 的錯誤,我將 Book.java 增加 package 就解決了,也有可能是 component-Scan 寫錯



※使用 annotation 的設定

※
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>bean.Animal</param-value>
</context-param>
    
<context-param>
    <param-name>contextClass</param-name>
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
    
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

※並不一定要使用常數的方式取得 applicationContxt,還可以用 ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

※多個 class 可用「,」隔開,或者用 @ComponentScan



※取得 @Autowired

super.init();
// super.init(config);
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
    .getAutowireCapableBeanFactory()
    .autowireBean(this);
    
// WebApplicationContextUtils.getWebApplicationContext(getServletContext())
//     .getAutowireCapableBeanFactory()
//     .configureBean(this, "animal");

※覆寫 init 方法,有兩個,隨便一個都可以,有參數的可用 config.getServletContext() 取得 ServletContext

※兩者選其一即可

※animal 可以寫 @ComponentScan,這樣就可以抓到很多了


2017年3月22日 星期三

聚合、依賴、繼承、插件 (Maven 4)

※聚合


上面的 A、B 專案,都要先 install 才能讓 C 抓到,這如果不只這兩個專案,那每一個都要先 install,太沒效率了,所以可用繼承的方式,統一用一個 pom.xml 就好了,操作如下:

1.新增 maven 專案後,勾下面的選項



 2.package 要選 pom



※D的pom


<project ...>
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>maven_test</groupId>
    <artifactId>D</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    
    <modules>
        <module>../A</module>
        <module>../B</module>
        <module>../C</module>
    </modules>
</project>

※因為 4 個專案同一層,所以要到上一層才可以抓到其他專案

※packaging 裡的 pom 一定要小寫

※此時只要操作 D 專案即可一次統一操作 3 個專案

※但有些人不想要新增一個專案,也可以將 D 的 pom.xml 複製到 ABC 三個專案的同層,而 D 專案也刪了,最後用命令提示字元下 mvn clean install,當然 Eclipse 還是能 import



※依賴


※左圖

C 專案依賴 A 和 B 專案,只要 scope 是 compile(預設) ,就會將 A、B 兩個專案的 jar 檔,複製到 C 專案
如果有同樣的 jar 檔,會以依賴最短的優先,但路徑都是 1,所以就會以 pom 檔的順序為主,誰先寫誰就贏了

※右圖

C 專案依賴 B 和 A1 專案,只要 scope 是 compile(預設) ,就會將 B、A1 兩個專案的 jar 檔,複製到 C 專案,但 A1 又依賴 A 專案,所以 A 也會複製給 C 專案
如果有同樣的 jar 檔,會以依賴最短的優先,所以會以 B 為主

如果不想依賴 maven 規定好的 jar,那就可以在不想依賴的 <dependency>增加<exclusions>來排除
<dependency>
    <groupId>maven_test</groupId>
    <artifactId>C</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>


※注意 C 專案要抓到 A、B 專案,必需先 mvn install ,這樣 .m2 才會有 jar 檔供 C 下載



※繼承

※父類

<groupId>bruce.chen</groupId>
<artifactId>D</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
    
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.2.2.RELEASE</version>
        </dependency>
    </dependencies>
</dependencyManagement>

※packaging 一定是 pom,否則子類會報錯

※使用 dependencyManagement 標籤將 dependencies 裝起來

※子類

<artifactId>B</artifactId>
<packaging>jar</packaging>
    
<parent>
    <groupId>bruce.chen</groupId>
    <artifactId>D</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>
    
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>


※寫個 parent 標籤,表示它還有父 pom

※自己坐標的 groupId 和 version 如果和父類一樣,也可以刪除

※dependency 標籤的 version 也可以刪除

※parent 有個子標籤 relativePath,預設是 ../pom.xml,如果不是就要打出來了


※插件

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<phase>generate-test-resources</phase>
<goals>
<goal>jar</goal>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

phase 要打上生命週期的名字,也就是哪時候執行這個插件
goal 可以多個,此例是打原碼包和打測試包,至於裡面要打什麼,可參考下圖


左邊選擇 Maven Plugins 會自動跳動上圖的 Plugins,然後選擇 source,表示要打源碼包,點進去就會看到可以用的 goals

此例在 compile phase 並不會打上面設定的 jar 包,可參考下圖



可以看出 compile 還沒到我們指定的 phase,但 package 有包括,加上原本的 jar,總共有三個
demo-0.0.1-SNAPSHOT.jar:原本的 jar 包
demo-0.0.1-SNAPSHOT-sources.jar:goal 為 jar 生出來的,內容和原本的一模一樣,不會有測試的類別
demo-0.0.1-SNAPSHOT-test-sources.jar:goal 為 test-jar 生出來的, 內容只有測試類別
.可以用解壓工具看裡面的內容
.如果專案是 springboot,還會有第四包,springboot 是可以執行的 jar 包,但要打上如下的標籤
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
通常 IDE 內鍵都會幫忙生成