三分钟搞定maven插件

什么是Maven插件?
Maven 实际上只是Maven插件集合的核心框架。换句话说,插件是执行大部分实际操作的地方。
插件用于:
创建jar文件,
创建war文件,
编译代码,
单元测试代码,
创建项目文档等。
插件是Maven的核心功能,它允许在多个项目中重用通用的构建逻辑。他们通过在项目描述(项目对象模型(POM))的上下文中执行“操作”(即创建WAR文件或编译单元测试)来实现此目的。可以通过一组唯一的参数来自定义插件的行为,这些参数通过每个插件目标(或Mojo)的描述公开。
一个插件通常提供了一组目标,可使用以下语法来执行:
mvn [plugin-name]:[goal-name]
例如:一个 Java 项目可以使用 Maven 编译器插件来编译目标,通过运行以下命令编译
mvn compiler:compile
插件有哪些类型
Maven 提供以下两种类型插

例如
我们使用 maven-antrun-plugin 插件在例子中来在控制台打印数据。现在在 C:\MVN\project 文件夹 创建一个 pom.xml 文件,内容如下:
4.0.0com.companyname.projectgroupproject1.0

org.apache.maven.plugins
maven-antrun-plugin
1.1


id.clean
clean

run



clean phase





接下来,打开命令终端跳转到 pom.xml 所在的目录,并执行下面的 mvn 命令。
mvn clean
Maven 将开始处理并显示 clean 生命周期的 clean 阶段。
[INFO] Scanning for projects…
[INFO] ------------------------------------------------------------------
[INFO] Building Unnamed - com.companyname.projectgroup:project:jar:1.0
[INFO] task-segment: [post-clean]
[INFO] ------------------------------------------------------------------
[INFO] [clean:clean {execution: default-clean}]
[INFO] [antrun:run {execution: id.clean}]
[INFO] Executing tasks [echo] clean phase
[INFO] Executed tasks
[INFO] ------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------
[INFO] Total time: < 1 second
[INFO] Finished at: Sat Jul 07 13:38:59 IST 2020
[INFO] Final Memory: 4M/44M
[INFO] --------
上面的例子展示了以下关键概念:
插件是在 pom.xml 中使用 plugins 元素定义的。
每个插件可以有多个目标。
你可以定义阶段,插件会使用它的 phase 元素开始处理。我们已经使用了 clean 阶段。
你可以通过绑定到插件的目标的方式来配置要执行的任务。我们已经绑定了 echo 任务到 maven-antrun-plugin 的 run 目标。
就是这样,Maven 将处理剩下的事情。它将下载本地仓库中获取不到的插件,并开始处理。

《犬夜叉2021》我想通过Binder找到你
第一章:我还能找到你吗,阿篱
犬夜叉和奈落大决战之后,四魂之玉、食骨之井消失,谁也不知道去了哪,而犬夜叉和阿篱再次被分割到两个世界。
于是犬夜叉拜托一位研究世界宇宙的法师——积木,来帮助他找到阿篱。
时间转眼来到了2021年,积木终于发现了这个世界的秘密。。
其实我们所在的整个宇宙叫做Android宇宙,犬夜叉和阿篱所处的两个世界其实是两个进程,两个世界可以通过食骨之井相连接。
所以想让犬夜叉重新联系到阿篱,必须再找到当年的食骨之井。
第二章:食骨之井改名Binder井?
“犬夜叉,我终于找到了”
“找到什么了?是阿篱吗?阿篱找到了????”
“没有,不过我找到了关键的东西——食骨之井”
“在哪,快带我去”
于是,积木法师带着犬夜叉来到一间屋子里:
这间屋子门面上写着《内核屋》三个大字,犬夜叉一个箭步飞了进去,在里面果然找到了当年那个食骨之井,但是又有点不一样,因为它被改了名,旁边一个破碎的板子上写着——Binder井。板子上还同时刻有使用说明:
Binder井
这口井联系这两个世界,你看到的也许不是真实的,请慎用!
如需使用,请找到当年遗落的四魂之玉,现在它叫SM之玉(ServiceManager),
找到SM之玉,心里默念你想联系的那个世界那个人,如果她在那个世界的SM之玉碎片中留下了地址,那么你就能找到她。
“积木法师,你知道SM之玉吗,哪里可以找到它”,犬夜叉问到。
第三章:四魂之玉——ServiceManager
“说到SM之玉,还要从宇宙的起源说起,Android宇宙创建初期,诞生了第一个有人的世界(用户进程),叫做Init世界,而SM之玉就是由这个世界创建的。
SM之玉创建后,启动了Binder井,成为了他的守护神。
但是它的真身存在于单独的世界中,无法获得。为了让人们能够使用到它,它特意在每个世界都留下了自己的碎片(代理)。”
“在哪在哪,快告诉我”。
“第0街道(句柄值固定为0)”,积木法师指了一个方向说到。
第四章:阿篱,我想你了
犬夜叉急忙去第0街道找到了SM之玉的碎片,然后回到Binder井旁边,心里默念道:
“ SM之玉, 求求你帮我找到阿篱吧。”
忽然,Binder井刮出一阵狂风,一个虚影出现在了犬夜叉的面前。
是阿篱~
“阿篱,你能听到我说话吗?”
“犬夜叉,我能听到,没想到还能看到你”,阿篱的虚影说到。
“我想你了,阿篱…”
故事End
故事结束了。
再帮大家理一下故事梗概,其实也就是Binder的工作流程:
阿篱(服务端)为了让犬夜叉(客户端)找到她,在四魂之玉(ServiceManager)上留下了他们世界(进程)的地址。
犬夜叉在第0街道(句柄为0)找到了四魂之玉碎片(ServiceManager代理)。
通过四魂之玉碎片,犬夜叉看到了阿篱的虚影(服务端代理),并通过虚影告诉了阿篱,想她了(通信)。
当然,故事毕竟是故事,并不能完全说清楚。
所以下面就完整看看Binder的工作流程和原理~
代码实现犬夜叉的需求
首先,我们使用AIDL来实现刚才故事中的场景——让犬夜叉和阿篱两个不同进程的人说上话:
//IMsgManager.aidl

interface IMsgManager {
String getMsg();
void tell(in String msg);
}

//阿篱
public class AliWorldService extends Service {

private static final String TAG = "lz1";

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return mBinder;
}

private Binder mBinder = new IMsgManager.Stub() {
    @Override
    public String getMsg() throws RemoteException {
        String tellMsg="犬夜叉...是我";
        Log.e(TAG, "阿篱:" + tellMsg);
        return tellMsg;
    }

    @Override
    public void tell(String msg) throws RemoteException {
        Log.e(TAG, "我是阿篱,我收到了你说的:" + msg);
    }
};

}


//犬夜叉
public class QycWorldActivity extends Activity {
private static final String TAG = “lz1”;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_qyc);

    Intent i = new Intent(this, AliWorldService.class);
    bindService(i, mConnection, Context.BIND_AUTO_CREATE);
}

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        IMsgManager msgManager = IMsgManager.Stub.asInterface(service);
        try {
            String tellMsg="阿篱,是你吗";
            Log.e(TAG, "犬夜叉:" + tellMsg);
            msgManager.tell(tellMsg);


            String msg = msgManager.getMsg();
            Log.e(TAG, "我是犬夜叉,我收到了你说的:" + msg);

        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(mConnection);
}

}
运行,打印结果:
E/lz1: 犬夜叉:阿篱,是你吗
E/lz1: 我是阿篱,我收到了你说的:阿篱,是你吗
E/lz1: 阿篱:犬夜叉…是我
E/lz1: 我是犬夜叉,我收到了你说的:犬夜叉…是我
AIDL原理
代码比较简单,服务器端新建一个Binder对象并传到onBind方法中,客户端bindservice之后,拿到到服务端的IBinder对象,通过asInterface方法获取到服务端的代理接口,就可以进行方法的调用了。
AIDL其实只是一个帮助我们实现进程间通信的工具,它会根据我们写的AIDL文件代码,生成相应的java接口代码,其内部也是通过Binder实现的。
我们可以通过build——generated——aidl_source_output_dir——debug——out文件路径找到AIDL为我们生成的接口类。这里是主要代码,比较长,可以边看解析边读代码:
public interface IMsgManager extends android.os.IInterface {

public static abstract class Stub extends android.os.Binder implements com.example.studynote.binder.IMsgManager {

    //1
    private static final java.lang.String DESCRIPTOR = "com.example.studynote.binder.IMsgManager";

    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    //2
    public static com.example.studynote.binder.IMsgManager asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.example.studynote.binder.IMsgManager))) {
            return ((com.example.studynote.binder.IMsgManager) iin);
        }
        return new com.example.studynote.binder.IMsgManager.Stub.Proxy(obj);
    }

    @Override
    public android.os.IBinder asBinder() {
        return this;
    }


    //4
    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
        java.lang.String descriptor = DESCRIPTOR;
        switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(descriptor);
                return true;
            }
            case TRANSACTION_getMsg: {
                data.enforceInterface(descriptor);
                java.lang.String _result = this.getMsg();
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            case TRANSACTION_tell: {
                data.enforceInterface(descriptor);
                java.lang.String _arg0;
                _arg0 = data.readString();
                this.tell(_arg0);
                reply.writeNoException();
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }

    private static class Proxy implements com.example.studynote.binder.IMsgManager {
        private android.os.IBinder mRemote;

        Proxy(android.os.IBinder remote) {
            mRemote = remote;
        }

        @Override
        public android.os.IBinder asBinder() {
            return mRemote;
        }

        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }


        //3
        @Override
        public java.lang.String getMsg() throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.lang.String _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

        @Override
        public void tell(java.lang.String msg) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeString(msg);
                mRemote.transact(Stub.TRANSACTION_tell, _data, _reply, 0);
                _reply.readException();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }
    }

    static final int TRANSACTION_getMsg = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_tell = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}

public java.lang.String getMsg() throws android.os.RemoteException;

public void tell(java.lang.String msg) throws android.os.RemoteException;

}
代码比较长,我们依次来分析下:
DESCRIPTOR。Binder的唯一标示。
在Stub类的构造方法中,就是通过attachInterface方法将当前的Binder和这个唯一标示进行了绑定。
asInterface()。将服务端的Binder对象转换成客户端所需的接口类型对象。
这个方法是客户端调用的,在这个方法中,会通过queryLocalInterface(DESCRIPTOR)方法,传入唯一标示,来获取对应的Binder。如果是服务端和客户端在同一个进程,那么就会返回服务端的Binder对象,也就是Stub对象本身,然后站长博客就直接调用对象的方法了。如果在不同进程,也就是我们一般的跨进程情况,就会返回封装后的Stub.Proxy这个代理对象。
Proxy.getMsg/tell
接着就看看代理类里面的方法,也就是我们在客户端(Activity)中实际调用的方法。
这其中有两个比较重要的对象 :_data对象和 _reply对象,都是Parcel类型的,这里会对数据进行一个序列化操作,这样才能进行跨进程传输。
如果方法传有参数,就会把参数写到_data对象,然后调用transact方法发起远程调用请求(RPC),同时当前线程挂起,等待服务端执行完请求。
mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
可以看到,传入了一个int类型的code——TRANSACTION_getMsg,这个code其实就是为了要确定调用的是哪个方法。
等请求结束后,当前线程继续,会从_reply对象中取出返回结果。
整个IPC流程就结束了。那服务器到底是在哪里进行任务执行的呢?继续看onTransact方法。
onTransact
onTransact方法就是服务端要做的事了,运行在服务端的Binder线程池中。
当客户端发起远程调用请求后,会通过系统底层封装,其实也就是内核层的Binder驱动,然后交给服务端的onTransact方法。
在该方法中,首先通过code知晓是哪个方法,然后就执行目标方法,并将序列化结果写到reply中,RPC过程结束,交给客户端处理。

上一篇:安卓php开发!Android开发应该了解的Binder原理,挥泪整理面经


下一篇:IPC机制 Binder