CGLIB詳解(最詳細)
轉載地址:https://blog.csdn.net/danchu/article/details/70238002
什麼是CGLIB
CGLIB是一個強大的、高效能的程式碼生成庫。其被廣泛應用於AOP框架(Spring、dynaop)中,用以提供方法攔截操作。Hibernate作為一個比較受歡迎的ORM框架,同樣使用CGLIB來代理單端(多對一和一對一)關聯(延遲提取集合使用的另一種機制)。CGLIB作為一個開源專案,其程式碼託管在github,地址為:https://github.com/cglib/cglib
為什麼使用CGLIB
CGLIB代理主要通過對位元組碼的操作,為物件引入間接級別,以控制物件的訪問。我們知道Java中有一個動態代理也是做這個事情的,那我們為什麼不直接使用Java動態代理,而要使用CGLIB呢?答案是CGLIB相比於JDK動態代理更加強大,JDK動態代理雖然簡單易用,但是其有一個致命缺陷是,只能對介面進行代理。如果要代理的類為一個普通類、沒有介面,那麼Java動態代理就沒法使用了。關於Java動態代理,可以參者這裡
CGLIB組成結構
CGLIB底層使用了ASM(一個短小精悍的位元組碼操作框架)來操作位元組碼生成新的類。除了CGLIB庫外,指令碼語言(如Groovy何BeanShell)也使用ASM生成位元組碼。ASM使用類似SAX的解析器來實現高效能。我們不鼓勵直接使用ASM,因為它需要對Java位元組碼的格式足夠的瞭解
例子
說了這麼多,可能大家還是不知道CGLIB是幹什麼用的。下面我們將使用一個簡單的例子來演示如何使用CGLIB對一個方法進行攔截。
首先,我們需要在工程的POM檔案中引入cglib的dependency,這裡我們使用的是2.2.2版本
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
依賴包下載後,我們就可以幹活了,按照國際慣例,寫個hello world
public class SampleClass {
public void test(){
System.out.println("hello world");
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before method run...");
Object result = proxy.invokeSuper(obj, args);
System.out.println("after method run...");
return result;
}
});
SampleClass sample = (SampleClass) enhancer.create();
sample.test();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
在mian函式中,我們通過一個Enhancer和一個MethodInterceptor來實現對方法的攔截,執行程式後輸出為:
before method run...
hello world
after method run...
- 1
- 2
- 3
在上面的程式中,我們引入了Enhancer和MethodInterceptor,可能有些讀者還不太瞭解。別急,我們後面將會一一進行介紹。就目前而言,一個使用CGLIB的小demo就完成了
常用的API
目前網路上對CGLIB的介紹資料比較少,造成對cglib的學習困難。這裡我將對cglib中的常用類進行一個介紹。為了避免解釋的不清楚,我將為每個類都配有一個demo,用來做進一步的說明。首先就從Enhancer開始吧。
Enhancer
Enhancer可能是CGLIB中最常用的一個類,和Java1.3動態代理中引入的Proxy類差不多(如果對Proxy不懂,可以參考這裡)。和Proxy不同的是,Enhancer既能夠代理普通的class,也能夠代理介面。Enhancer建立一個被代理物件的子類並且攔截所有的方法呼叫(包括從Object中繼承的toString和hashCode方法)。Enhancer不能夠攔截final方法,例如Object.getClass()方法,這是由於Java final方法語義決定的。基於同樣的道理,Enhancer也不能對fianl類進行代理操作。這也是Hibernate為什麼不能持久化final class的原因。
public class SampleClass {
public String test(String input){
return "hello world";
}
}
- 1
- 2
- 3
- 4
- 5
下面我們將以這個類作為主要的測試類,來測試呼叫各種方法
@Test
public void testFixedValue(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello cglib";
}
});
SampleClass proxy = (SampleClass) enhancer.create();
System.out.println(proxy.test(null)); //攔截test,輸出Hello cglib
System.out.println(proxy.toString());
System.out.println(proxy.getClass());
System.out.println(proxy.hashCode());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
程式的輸出為:
Hello cglib
Hello cglib
class com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
at com.zeus.cglib.SampleClass$$EnhancerByCGLIB$$e3ea9b7.hashCode(<generated>)
...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
上述程式碼中,FixedValue用來對所有攔截的方法返回相同的值,從輸出我們可以看出來,Enhancer對非final方法test()、toString()、hashCode()進行了攔截,沒有對getClass進行攔截。由於hashCode()方法需要返回一個Number,但是我們返回的是一個String,這解釋了上面的程式中為什麼會丟擲異常。
Enhancer.setSuperclass用來設定父型別,從toString方法可以看出,使用CGLIB生成的類為被代理類的一個子類,形如:SampleClass EnhancerByCGLIB e3ea9b7
Enhancer.create(Object…)方法是用來建立增強物件的,其提供了很多不同引數的方法用來匹配被增強類的不同構造方法。(雖然類的構造放法只是Java位元組碼層面的函式,但是Enhancer卻不能對其進行操作。Enhancer同樣不能操作static或者final類)。我們也可以先使用Enhancer.createClass()來建立位元組碼(.class),然後用位元組碼動態的生成增強後的物件。
可以使用一個InvocationHandler(如果對InvocationHandler不懂,可以參考這裡)作為回撥,使用invoke方法來替換直接訪問類的方法,但是你必須注意死迴圈。因為invoke中呼叫的任何原代理類方法,均會重新代理到invoke方法中。
public void testInvocationHandler() throws Exception{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){
return "hello cglib";
}else{
throw new RuntimeException("Do not know what to do");
}
}
});
SampleClass proxy = (SampleClass) enhancer.create();
Assert.assertEquals("hello cglib", proxy.test(null));
Assert.assertNotEquals("Hello cglib", proxy.toString());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
為了避免這種死迴圈,我們可以使用MethodInterceptor,MethodInterceptor的例子在前面的hello world中已經介紹過了,這裡就不浪費時間了。
有些時候我們可能只想對特定的方法進行攔截,對其他的方法直接放行,不做任何操作,使用Enhancer處理這種需求同樣很簡單,只需要一個CallbackFilter即可:
@Test
public void testCallbackFilter() throws Exception{
Enhancer enhancer = new Enhancer();
CallbackHelper callbackHelper = new CallbackHelper(SampleClass.class, new Class[0]) {
@Override
protected Object getCallback(Method method) {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "Hello cglib";
}
};
}else{
return NoOp.INSTANCE;
}
}
};
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
SampleClass proxy = (SampleClass) enhancer.create();
Assert.assertEquals("Hello cglib", proxy.test(null));
Assert.assertNotEquals("Hello cglib",proxy.toString());
System.out.println(proxy.hashCode());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
ImmutableBean
通過名字就可以知道,不可變的Bean。ImmutableBean允許建立一個原來物件的包裝類,這個包裝類是不可變的,任何改變底層物件的包裝類操作都會丟擲IllegalStateException。但是我們可以通過直接操作底層物件來改變包裝類物件。這有點類似於Guava中的不可變檢視
為了對ImmutableBean進行測試,這裡需要再引入一個bean
public class SampleBean {
private String value;
public SampleBean() {
}
public SampleBean(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
然後編寫測試類如下:
@Test(expected = IllegalStateException.class)
public void testImmutableBean() throws Exception{
SampleBean bean = new SampleBean();
bean.setValue("Hello world");
SampleBean immutableBean = (SampleBean) ImmutableBean.create(bean); //建立不可變類
Assert.assertEquals("Hello world",immutableBean.getValue());
bean.setValue("Hello world, again"); //可以通過底層物件來進行修改
Assert.assertEquals("Hello world, again", immutableBean.getValue());
immutableBean.setValue("Hello cglib"); //直接修改將throw exception
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Bean generator
cglib提供的一個操作bean的工具,使用它能夠在執行時動態的建立一個bean。
@Test
public void testBeanGenerator() throws Exception{
BeanGenerator beanGenerator = new BeanGenerator();
beanGenerator.addProperty("value",String.class);
Object myBean = beanGenerator.create();
Method setter = myBean.getClass().getMethod("setValue",String.class);
setter.invoke(myBean,"Hello cglib");
Method getter = myBean.getClass().getMethod("getValue");
Assert.assertEquals("Hello cglib",getter.invoke(myBean));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在上面的程式碼中,我們使用cglib動態的建立了一個和SampleBean相同的Bean物件,包含一個屬性value以及getter、setter方法
Bean Copier
cglib提供的能夠從一個bean複製到另一個bean中,而且其還提供了一個轉換器,用來在轉換的時候對bean的屬性進行操作。
public class OtherSampleBean {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@Test
public void testBeanCopier() throws Exception{
BeanCopier copier = BeanCopier.create(SampleBean.class, OtherSampleBean.class, false);//設定為true,則使用converter
SampleBean myBean = new SampleBean();
myBean.setValue("Hello cglib");
OtherSampleBean otherBean = new OtherSampleBean();
copier.copy(myBean, otherBean, null); //設定為true,則傳入converter指明怎麼進行轉換
assertEquals("Hello cglib", otherBean.getValue());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
BulkBean
相比於BeanCopier,BulkBean將copy的動作拆分為getPropertyValues和setPropertyValues兩個方法,允許自定義處理屬性
@Test
public void testBulkBean() throws Exception{
BulkBean bulkBean = BulkBean.create(SampleBean.class,
new String[]{"getValue"},
new String[]{"setValue"},
new Class[]{String.class});
SampleBean bean = new SampleBean();
bean.setValue("Hello world");
Object[] propertyValues = bulkBean.getPropertyValues(bean);
assertEquals(1, bulkBean.getPropertyValues(bean).length);
assertEquals("Hello world", bulkBean.getPropertyValues(bean)[0]);
bulkBean.setPropertyValues(bean,new Object[]{"Hello cglib"});
assertEquals("Hello cglib", bean.getValue());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
使用注意:
1. 避免每次進行BulkBean.create建立物件,一般將其宣告為static的
2. 應用場景:針對特定屬性的get,set操作,一般適用通過xml配置注入和注出的屬性,執行時才確定處理的Source,Target類,只需要關注屬性名即可。
BeanMap
BeanMap類實現了Java Map,將一個bean物件中的所有屬性轉換為一個String-to-Obejct的Java Map
@Test
public void testBeanMap() throws Exception{
BeanGenerator generator = new BeanGenerator();
generator.addProperty("username",String.class);
generator.addProperty("password",String.class);
Object bean = generator.create();
Method setUserName = bean.getClass().getMethod("setUsername", String.class);
Method setPassword = bean.getClass().getMethod("setPassword", String.class);
setUserName.invoke(bean, "admin");
setPassword.invoke(bean,"password");
BeanMap map = BeanMap.create(bean);
Assert.assertEquals("admin", map.get("username"));
Assert.assertEquals("password", map.get("password"));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
我們使用BeanGenerator生成了一個含有兩個屬性的Java Bean,對其進行賦值操作後,生成了一個BeanMap物件,通過獲取值來進行驗證
keyFactory
keyFactory類用來動態生成介面的例項,介面需要只包含一個newInstance方法,返回一個Object。keyFactory為構造出來的例項動態生成了Object.equals和Object.hashCode方法,能夠確保相同的引數構造出的例項為單例的。
public interface SampleKeyFactory {
Object newInstance(String first, int second);
}
- 1
- 2
- 3
我們首先構造一個滿足條件的介面,然後進行測試
@Test
public void testKeyFactory() throws Exception{
SampleKeyFactory keyFactory = (SampleKeyFactory) KeyFactory.create(SampleKeyFactory.class);
Object key = keyFactory.newInstance("foo", 42);
Object key1 = keyFactory.newInstance("foo", 42);
Assert.assertEquals(key,key1);//測試引數相同,結果是否相等
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
Mixin(混合)
Mixin能夠讓我們將多個物件整合到一個物件中去,前提是這些物件必須是介面的實現。可能這樣說比較晦澀,以程式碼為例:
public class MixinInterfaceTest {
interface Interface1{
String first();
}
interface Interface2{
String second();
}
class Class1 implements Interface1{
@Override
public String first() {
return "first";
}
}
class Class2 implements Interface2{
@Override
public String second() {
return "second";
}
}
interface MixinInterface extends Interface1, Interface2{
}
@Test
public void testMixin() throws Exception{
Mixin mixin = Mixin.create(new Class[]{Interface1.class, Interface2.class,
MixinInterface.class}, new Object[]{new Class1(),new Class2()});
MixinInterface mixinDelegate = (MixinInterface) mixin;
assertEquals("first", mixinDelegate.first());
assertEquals("second", mixinDelegate.second());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
Mixin類比較尷尬,因為他要求Minix的類(例如MixinInterface)實現一些介面。既然被Minix的類已經實現了相應的介面,那麼我就直接可以通過純Java的方式實現,沒有必要使用Minix類。
String switcher
用來模擬一個String到int型別的Map型別。如果在Java7以後的版本中,類似一個switch語句。
@Test
public void testStringSwitcher() throws Exception{
String[] strings = new String[]{"one", "two"};
int[] values = new int[]{10,20};
StringSwitcher stringSwitcher = StringSwitcher.create(strings,values,true);
assertEquals(10, stringSwitcher.intValue("one"));
assertEquals(20, stringSwitcher.intValue("two"));
assertEquals(-1, stringSwitcher.intValue("three"));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Interface Maker
正如名字所言,Interface Maker用來建立一個新的Interface
@Test
public void testInterfaceMarker() throws Exception{
Signature signature = new Signature("foo", Type.DOUBLE_TYPE, new Type[]{Type.INT_TYPE});
InterfaceMaker interfaceMaker = new InterfaceMaker();
interfaceMaker.add(signature, new Type[0]);
Class iface = interfaceMaker.create();
assertEquals(1, iface.getMethods().length);
assertEquals("foo", iface.getMethods()[0].getName());
assertEquals(double.class, iface.getMethods()[0].getReturnType());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
上述的Interface Maker建立的介面中只含有一個方法,簽名為double foo(int)。Interface Maker與上面介紹的其他類不同,它依賴ASM中的Type型別。由於介面僅僅只用做在編譯時期進行型別檢查,因此在一個執行的應用中動態的建立介面沒有什麼作用。但是InterfaceMaker可以用來自動生成程式碼,為以後的開發做準備。
Method delegate
MethodDelegate主要用來對方法進行代理
interface BeanDelegate{
String getValueFromDelegate();
}
@Test
public void testMethodDelegate() throws Exception{
SampleBean bean = new SampleBean();
bean.setValue("Hello cglib");
BeanDelegate delegate = (BeanDelegate) MethodDelegate.create(bean,"getValue", BeanDelegate.class);
assertEquals("Hello cglib", delegate.getValueFromDelegate());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
關於Method.create的引數說明:
1. 第二個引數為即將被代理的方法
2. 第一個引數必須是一個無引數構造的bean。因此MethodDelegate.create並不是你想象的那麼有用
3. 第三個引數為只含有一個方法的介面。當這個介面中的方法被呼叫的時候,將會呼叫第一個引數所指向bean的第二個引數方法
缺點:
1. 為每一個代理類建立了一個新的類,這樣可能會佔用大量的永久代堆記憶體
2. 你不能代理需要引數的方法
3. 如果你定義的介面中的方法需要引數,那麼代理將不會工作,並且也不會丟擲異常;如果你的介面中方法需要其他的返回型別,那麼將丟擲IllegalArgumentException
MulticastDelegate
- 多重代理和方法代理差不多,都是將代理類方法的呼叫委託給被代理類。使用前提是需要一個介面,以及一個類實現了該介面
- 通過這種interface的繼承關係,我們能夠將介面上方法的呼叫分散給各個實現類上面去。
- 多重代理的缺點是介面只能含有一個方法,如果被代理的方法擁有返回值,那麼呼叫代理類的返回值為最後一個新增的被代理類的方法返回值
public interface DelegatationProvider {
void setValue(String value);
}
public class SimpleMulticastBean implements DelegatationProvider {
private String value;
@Override
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
@Test
public void testMulticastDelegate() throws Exception{
MulticastDelegate multicastDelegate = MulticastDelegate.create(DelegatationProvider.class);
SimpleMulticastBean first = new SimpleMulticastBean();
SimpleMulticastBean second = new SimpleMulticastBean();
multicastDelegate = multicastDelegate.add(first);
multicastDelegate = multicastDelegate.add(second);
DelegatationProvider provider = (DelegatationProvider) multicastDelegate;
provider.setValue("Hello world");
assertEquals("Hello world", first.getValue());
assertEquals("Hello world", second.getValue());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
Constructor delegate
為了對建構函式進行代理,我們需要一個介面,這個介面只含有一個Object newInstance(…)方法,用來呼叫相應的建構函式
interface SampleBeanConstructorDelegate{
Object newInstance(String value);
}
/**
* 對建構函式進行代理
* @throws Exception
*/
@Test
public void testConstructorDelegate() throws Exception{
SampleBeanConstructorDelegate constructorDelegate = (SampleBeanConstructorDelegate) ConstructorDelegate.create(
SampleBean.class, SampleBeanConstructorDelegate.class);
SampleBean bean = (SampleBean) constructorDelegate.newInstance("Hello world");
assertTrue(SampleBean.class.isAssignableFrom(bean.getClass()));
System.out.println(bean.getValue());
}
相關推薦
CGLIB詳解(最詳細)
轉載地址:https://blog.csdn.net/danchu/article/details/70238002 什麼是CGLIB CGLIB是一個強大的、高效能的程式碼生成庫。其被廣泛應用於AOP框架(Spring、dynaop)中,用以提供方法攔截操作。
SVN trunk(主線) branch(分支) tag(標記) 用法詳解和詳細操作步驟
trac load mar span 必須 最可 objc copy 右鍵 原文地址:http://blog.csdn.net/vbirdbest/article/details/51122637 使用場景: 假如你的項目(這裏指的是手機客戶端項目)的某個版本(例如1.0
oracle常用函數詳解(詳細)
sub 最後一天 run -1 fonts ase 必須 顯示 分享 作者:紅旗飄揚 Oracle SQL 提供了用於執行特定操作的專用函數。這些函數大大增強了 SQL 語言的功能。函數可以接受零個或者多個輸入參數,並返回一個輸出結果。 oracle 數據庫中主要使用兩種
詳解 最大子段和
最大 負數 nbsp 端點 關於 一段 描述 計數器 曾經 題目名稱:最大子段和 題目描述:給出一段序列,選出其中連續且非空的一段使得這段和最大。 輸入格式: 第一行是一個正整數N,表示了序列的長度。 第2行包含N個絕對值不大於10000的整數A[i],描述了這段序列。
【機器學習基本理論】詳解最大似然估計(MLE)、最大後驗概率估計(MAP),以及貝葉斯公式的理解
總結 ora 二次 判斷 天都 特性 以及 解釋 意思 【機器學習基本理論】詳解最大似然估計(MLE)、最大後驗概率估計(MAP),以及貝葉斯公式的理解 https://mp.csdn.net/postedit/81664644 最大似然估計(Maximum lik
詳解最大似然估計(MLE)、最大後驗概率估計(MAP),以及貝葉斯公式的理解
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id=
Java進階篇之十五 ----- JDK1.8的Lambda、Stream和日期的使用詳解(很詳細)
前言 本篇主要講述是Java中JDK1.8的一些新語法特性使用,主要是Lambda、Stream和LocalDate日期的一些使用講解。 Lambda Lambda介紹 Lambda 表示式(lambda expression)是一個匿名函式,Lambda表示式基於數學中的λ演算得名,直接對應於
八皇后問題詳解(最短程式碼)
八皇后問題演算法分析: 分析1:八皇后由一個64格的方塊組成,那麼把八個皇后放入不考慮其他情況利用窮舉法,有8^64種 可能。 分析2:顯然任意一行有且僅有1個皇后,使用陣列queen[0->7]表示第i行的皇后位於哪一列。 對於“1->8”這八個字元,呼叫全
python中yield的用法詳解——最簡單,最清晰的解釋
首先我要吐槽一下,看程式的過程中遇見了yield這個關鍵字,然後百度的時候,發現沒有一個能簡單的讓我懂的,講起來真TM的都是頭頭是道,什麼引數,什麼傳遞的,還口口聲聲說自己的教程是最簡單的,最淺顯易懂的,我就想問沒有有考慮過讀者的感受。 接下來是正題: 首先,如果你還沒有
log4j配置詳解(非常詳細轉載)
Log4J的配置檔案(Configuration File)就是用來設定記錄器的級別、存放器和佈局的,它可接key=value格式的設定或xml格式的設定資訊。通過配置,可以創建出Log4J的執行環境。 配置檔案 Log4J配置檔案的基本格式如下:
oracle 分組函式與group by正確用法詳解與詳細解釋
oracle資料庫 分組函式與group by正確用法詳解與詳細解釋1.查詢時同時查詢了分組函式列和非分組函式列就需要使用group by,但是僅僅查詢分組函式列可以不結合group by使用。 SQL> select TABLESPACE_NAME,sum(user
SVN trunk(主線) branch(分支) tag(標記) 用法詳解和詳細操作步驟
使用場景:假如你的專案(這裡指的是手機客戶端專案)的某個版本(例如1.0版本)已經完成開發、測試並已經上線了,接下來接到新的需求,新需求的開發需要修改多個檔案中的程式碼,當需求已經開始開發一段時間的時候,突然接到使用者或測試人員的反饋,專案中有個重大bug需要緊急修復,並且要
ARM指令集詳解(超詳細!帶例項!)
算術和邏輯指令 ADC : 帶進位的加法 (Addition with Carry) ADC{條件}{S} <dest>, <op 1>, <op 2> dest = op_1 + op_2 + carry ADC將把兩個運算元加起來
演算法之詳解最小生成樹
繼續,這次選擇邊CI(當有兩條邊權值相等時,可隨意選一條),此時需做判斷。判斷法則:當將邊CI加入到已找到邊的集合中時,是否會形成迴路? 1.如果沒有形成迴路,那麼直接將其連通。此時,對於邊的集合又要做一次判斷:這兩個點是否在已找到點的集合中出現過? ①.如果兩個點都沒有出現過
Jvm堆記憶體的劃分結構和優化,垃圾回收詳解(詳細解答篇)
1.S0與S1的區間明顯較小,有效新生代空間為Eden+S0/S1,因此有效空間就大,增加了記憶體使用率 2.有利於物件代的計算,當一個物件在S0/S1中達到設定的XX:MaxTenuringThreshold值後,會將其分到老年代中,設想一下,如果沒有S0/S1,直接分成兩個區,該如何計算物件經過了多少次G
android Json解析詳解(詳細程式碼)
廣告時間:張大媽 好看美劇 妹子圖 JSON的定義: 一種輕量級的資料交換格式,具有良好的可讀和便於快速編寫的特性。業內主流技術為其提供了完整的解決方案(有點類似於正則表示式 ,獲得了當今大部分語言的支援),從而可以在不同平臺間進行資料交換。JSON採
Google官方MVP+Dagger2架構詳解----非常詳細,值得多看幾遍(okhttp cache)
原文:http://www.jianshu.com/p/01d3c014b0b1# 1 前言 google官方示例架構專案 在我的前一篇文章分享的時候,當時todo-mvp-dagger/ 這個分支也還沒有開發完畢。最近的專案中也在用到Dagger2 作為依賴注入
Koltin——最詳細的可見性修飾符詳解
如果 子類 但是 同一文件 文章 f2c -o 系列 star 在Kotlin中,不管是類,對象,接口,構造函數,函數,屬性及其設置器都具有可見性修飾符。Kotlin中的可見性修飾符共四種。即public、protected、private、internal。在不同的場景
Kotlin——最詳細的數據類、密封類詳解
實現 分析 及其 驗證 pri gpo ava 兩個 有著 在前面幾個章節章節中,詳細的講解了Koltin中的接口類(Interface)、枚舉類(Enmu),還不甚了解的可以查看我的上一篇文章Kotlin——接口類、枚舉類詳解。當然,在Koltin中,除了接口類、枚舉類
Kotlin——最詳細的抽象類(abstract)、內部類(嵌套類)詳解
unit 建議 git 功能 pen 情況 master 這也 html 在前面幾個章節中,詳細的介紹了Kotlin類的類別中的數據類、密封類、接口類以及枚舉類。在這個章節中會對Koltin的抽象類和內部類作出一個詳細的講解。如果對上面所提到的類的類別還不是很清晰的,請閱