反射及動態(tài)代理模式

Java反射機制

Java反射機制指的是在Java程序運行狀態(tài)中蝌箍,對于任何一個類,都可以獲得這個類的所有屬性和方法牺汤;對于給定的一個對象辽旋,都能夠調(diào)用它的任意一個屬性和方法。這種動態(tài)獲取類的內(nèi)容以及動態(tài)調(diào)用對象的方法稱為反射機制檐迟。

作用:

  • 可以在程序運行時對類和對象進行操作补胚,提高程序的靈活性和擴展性
  • 反射機制是構建框架技術的基礎所在,使用反射可以避免將代碼寫死在框架中

反射機制相關的類:

  • Class對象:一個類的class對象
  • Constructor對象:一個類的構造器對象
  • Method對象:一個類的方法對象
  • Field對象:一個類的屬性對象

獲取類的Class對象

        Boolean flag = true;
        Class booleanClass = flag.getClass();
        try {
            Class booleanClass1 = Class.forName("java.lang.Boolean");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Class booleanClass2 = Boolean.class;
        Class booleanClass3 = Boolean.TYPE;
class對象的相關方法
        // 創(chuàng)建當前class對象類的一個實例(Boolean)
        try {
            Boolean is = (Boolean) booleanClass.newInstance();
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        // 將obj cast成一個class對象的實例(如果不是同一個類會出現(xiàn)ClassCastException)
        booleanClass.cast(false);
        booleanClass.getInterfaces(); // 獲取當前類的接口的class對象
        booleanClass.getSuperclass();
        booleanClass.getAnnotation(Override.class);
        booleanClass.getClassLoader();// 獲取類加載器
        booleanClass.getPackage();
        booleanClass.getName();// 獲取類的全名
        booleanClass.getSimpleName();// 獲取類名
        booleanClass.getModifiers();// 獲取類修飾符

通過class對象獲取類的Constructor對象

        try {
            // 獲取公有構造方法
            Constructor constructor = booleanClass.getConstructor();
            // 獲取所有的公有構造方法
            Constructor [] constructors = booleanClass.getConstructors();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
Constructor對象的相關方法:
            constructor.getModifiers();
            constructor.getName();
            constructor.getParameters();
            constructor.getAnnotations();
            constructor.setAccessible(true);// 將方法的限定符設置為public
            constructor.newInstance();

通過class對象獲取類的Method對象

         /**
         *  flag.booleanValue()
         * */

        // 獲取指定方法名的method對象(arg:方法名追迟,arg2:方法參數(shù)類型class)
        Method methodBooleanValue = booleanClass.getDeclaredMethod("booleanValue",null);
        // 獲取所有的公有方法
        Method [] methods = booleanClass.getDeclaredMethods();
Method對象的相關方法:
        // 設置public
        methodBooleanValue.setAccessible(true);
        Object flag2 = booleanClass.newInstance();
        // (arg1:當前method所屬對象的實例對象溶其,arg2:當前method的參數(shù)值),調(diào)用flag2對象的booleanValue方法敦间,參數(shù)是null
        methodBooleanValue.invoke(flag2,null);

通過class對象獲取類的Field對象

        // arg1 : 屬性名稱
        Field field = booleanClass.getDeclaredField("serialVersionUID");
        Field [] fields = booleanClass.getDeclaredFields();
Field對象的相關方法:
        // (arg1 :當前field對象所屬class對象的實例對象)瓶逃,獲取對象的field屬性值
        String uid = (String) field.get(flag2);
        // (arg1:同上,arg2:需要設置的屬性的值)每瞒,設置arg1實例對象的field對象的屬性值
        field.set(field,"-3665804199014368530L");
        // 判斷field屬性值是否和傳入的obj的field屬性值相等
        field.equals(true);

Demo

Person Class
public class Person {

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

    public void eat(){
        System.out.println("im eating");
    }

    private void shit(){
        System.out.println("im shitting");
    }
}

反射Person類
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

        Person person = new Person("tony");

        // 獲取class對象
        Class personClass = person.getClass();  // 帶實例生成

        Constructor constructorWithNoParams = personClass.getConstructor();
        Constructor constructorWithParams = personClass.getConstructor(String.class);

        // 通過constructor對象生成Person實例
        Person reflectPerson = (Person) constructorWithNoParams.newInstance();
        reflectPerson.eat();

        // 通過反射調(diào)用拉屎方法
        Method methodShit = personClass.getDeclaredMethod("shit",null);
        System.out.println("methodShit modifiers before setAccessible:"+methodShit.getModifiers());
        methodShit.setAccessible(true);
        System.out.println("methodShit modifiers after setAccessible:"+methodShit.getModifiers());
        methodShit.invoke(person,null);
        // 通過反射調(diào)用吃飯方法
        Method methodEat = personClass.getDeclaredMethod("eat",null);
        System.out.println("methodEat modifiers:"+methodEat.getModifiers());

        // 獲取私有field
        Field fieldName = personClass.getDeclaredField("name");
        fieldName.setAccessible(true);
        System.out.println("fieldName get -> person before set:"+fieldName.get(person));
        fieldName.set(person,"new tony");
        System.out.println("fieldName get -> person after set:"+fieldName.get(person));

    }
運行結果

在運行結果第三行金闽,我們發(fā)現(xiàn)setAccessible并不會改變modifiers的值纯露,但是會影響他的訪問剿骨,如果不設置為true 會拋出IllegalAccessException異常;

注意直接get和getDeclared的區(qū)別

get:獲得某個類的所有的公共(public)的字段埠褪,包括父類中的字段浓利。
getDeclared:獲得某個類的所有聲明的字段,即包括public钞速、private和proteced贷掖,但是不包括父類的申明字段。

代理模式

定義:

給目標對象設置一個代理對象渴语,并由代理對象控制目標對象的引用

目的:
  • 間接訪問目標苹威,防止直接訪問帶來不必要的問題
  • 通過代理對象對原有的業(yè)務增強
靜態(tài)代理模式:

代理類,被代理對象驾凶,需求對象牙甫,業(yè)務接口掷酗;被代理類提供某種服務,需求對象需要這種服務窟哺,但是不能直接訪問泻轰,需要借助代理類訪問;
下面舉個中介賣房子的例子:
定義業(yè)務接口

public interface SaleHouse {
    void saleHouse(String s);
}

定義代理類和被代理類(他們都要實現(xiàn)業(yè)務接口且轨,因為他們都要提供服務給買房子的人)

public class Seller implements SaleHouse {
    @Override
    public void saleHouse(String s) {
        System.out.println("我是賣家浮声,我要賣房子");
    }
}


public class Agent implements SaleHouse {
    private Seller seller;   //  代理類持有被代理對象的引用
    public Agent(Seller seller) {
        this.seller = seller;
    }
    @Override
    public void saleHouse(String s) {
        System.out.println("我是中介代理");
        seller.saleHouse(s);
    }
}

我們真是的購房者通過中介購買房子

public static void main(String[] args) {
        Seller seller = new Seller();
        Agent agent = new Agent(seller);
        agent.saleHouse("房子的要求");
    }

這樣就完成了需求對象通過代理對象到真實對象的訪問

上面介紹的代理模式是靜態(tài)代理模式,這種代理模式嚴重違反了設計模式的開閉原則:
  • 擴展能力差
  • 可維護性差
    如果這個時候又有一個人來賣車子旋奢,這是中介Agent對象又需要實現(xiàn)一個賣車接口泳挥,賣車人對象,當被代理對象越來越多黄绩,代碼越來越臃腫羡洁,這種方式肯定是不可取的;

動態(tài)代理模式:

  • Proxy:用于代理類創(chuàng)建代理對象
  • InvocationHandler(interface):代理類實現(xiàn)接口

動態(tài)代理類

public class DynamicAgent implements InvocationHandler {

    //  代理類持有的真實對象的引用
    private Object seller;

    // 設置代理的真實對象
    public void setSeller(Object seller) {
        this.seller = seller;
    }

    // 獲取動態(tài)代理對象(1:被代理對象的類加載器爽丹,2:被代理對象實現(xiàn)的接口筑煮,)
    public Object getProxy() {
        return Proxy.newProxyInstance(seller.getClass().getClassLoader(), seller.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        System.out.println("我是中介");
        //  通過反射調(diào)用被代理對象的業(yè)務方法
        Object result = method.invoke(seller, objects);
        return result;
    }
}

此時我們有兩個類需要代理

interface SaleCar {
    void saleCar();
}

class CarSeller implements SaleCar {
    @Override
    public void saleCar() {
        System.out.println("我是賣車人,我要賣車");
    }
}

public interface SaleHouse {
    void saleHouse(String s);
}

class HouseSeller implements SaleHouse {
    @Override
    public void saleHouse(String s) {
        System.out.println("我是賣房者粤蝎,我要賣房子");
    }
}

通過代理類去代理兩個實際類

// 創(chuàng)建動態(tài)代理對象(中介公司)
        DynamicAgent dynamicAgent = new DynamicAgent();

        // 第一個被代理的賣房子的人
        SaleHouse houseSeller = new HouseSeller();
        // 設置被代理的對象是賣房者
        dynamicAgent.setSeller(houseSeller);
        // 獲取一個代理對象Proxy(中介)真仲,服務賣房者  注意:代理對象只能強轉為接口
        SaleHouse houseAgent = (SaleHouse) dynamicAgent.getProxy();
        houseAgent.saleHouse("賣房子");

        // 第二個被代理賣車的人
        SaleCar carSeller = new CarSeller();
        // 設置中介公司的被代理人
        dynamicAgent.setSeller(carSeller);
        // 分配一個中介去賣車
        SaleCar carAgent = (SaleCar) dynamicAgent.getProxy();
        carAgent.saleCar();

創(chuàng)建一個代理類實現(xiàn)InvocationHandler 接口,創(chuàng)建一個業(yè)務類初澎,一個業(yè)務接口秸应,業(yè)務類實現(xiàn)業(yè)務接口,把業(yè)務類傳給代理類碑宴,通過代理類生成一個Proxy代理對象软啼,通過這個代理對象去調(diào)用業(yè)務接口的方法;

例子舉完了我覺得有必要畫一個思維導圖幫助理解記憶

動態(tài)代理原理:

newProxyInstance():
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        //  1.通過classLoader和interfaces獲取代理類的class對象
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //   2.通過class對象獲取constructor對象
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //   3.通過constructor對象返回一個instance實例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

三步走:

  • 1.通過classLoader和interfaces獲取代理類的class對象 Class<?> cl = getProxyClass0(loader, intfs)
  • 2.通過class對象獲取constructor對象final Constructor<?> cons = cl.getConstructor(constructorParams)
  • 3.通過constructor對象返回一個instance實例return cons.newInstance(new Object[]{h})

下面重點講解第一步:getProxyClass0()延柠;

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

在Proxy類的內(nèi)部有一個proxyClassCache成員變量祸挪,Proxy內(nèi)部使用的緩存機制,這是一個WeakCache對象贞间,具體實現(xiàn)是一個ConcurrentMap贿条;如果根據(jù)提供的類加載器和接口數(shù)組能在緩存中找到代理類就直接返回該代理類,否則會調(diào)用ProxyClassFactory工廠去生成代理類增热。這里用到的緩存是二級緩存整以,它的一級緩存key是根據(jù)類加載器生成的,二級緩存key是根據(jù)接口數(shù)組生成的峻仇;

兩個重點:
  • 緩存機制
  • 如何創(chuàng)建代理類

WeakCache緩存機制:

WeakCache<K,P,V>中公黑,K代表key值,P代表參數(shù),V代表存儲的值凡蚜。此類用于緩存{(key奠骄,sub-key)-->value}鍵值對。具體實現(xiàn)是一個ConcurrentMap<Object,ConcurrentMap<Object,Supplier<V>>>(Supplier是一個接口番刊,就一個get方法用于獲得值含鳞,不過是V的包裹類),第一個Object就是key(這里表達式不用K是因為key值可以為null)芹务,第二個Object就是sub-key蝉绷。

 public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        // 調(diào)用apply方法創(chuàng)建代理類
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

ProxyClassFactory:

類變量

// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";

// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();

apply()

@Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             */
            //  代理類的java字節(jié)碼(字節(jié)流形式)
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                 //  通過native方法將java字節(jié)碼的二進制流轉為Class對象
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);

在apply方法中我們發(fā)現(xiàn)byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)這行代碼生成代理類的字節(jié)碼文件,ProxyGenerator是由編譯器創(chuàng)建的類枣抱;最后調(diào)用defineClass0native方法將流轉變?yōu)镃lass對象熔吗,然后反射,method佳晶,invoke桅狠。。轿秧。
通過constructor.newInstance()創(chuàng)建代理實例對象時傳入了h(invocationHander)對象中跌,在代理實例類中會調(diào)用h.invoke();

由native方法生成的class對象,本質(zhì)是Proxy的子類繼承了代理類的業(yè)務接口,其內(nèi)部在接口的實現(xiàn)中菇篡,調(diào)用了h.invoke();和靜態(tài)代理的代理類和被代理帶實現(xiàn)了同一個接口是一個道理漩符,

public final class $Proxy0 extends Proxy implements SaleHouse{

...

// Proxy生成的動態(tài)代理類(實現(xiàn)業(yè)務接口:在實現(xiàn)中調(diào)用invoke())

public final void saleHouse(String paramString){
        try {
            this.h.invoke(this,m3,new Object[]{paramString});
            return;
        } catch (RuntimeException e){
            
        }
    }

...

}

m3是saleHouse方法的method對象;

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末驱还,一起剝皮案震驚了整個濱河市嗜暴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌议蟆,老刑警劉巖闷沥,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咐容,居然都是意外死亡舆逃,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門疟丙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颖侄,“玉大人鸟雏,你說我怎么就攤上這事享郊。” “怎么了孝鹊?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵炊琉,是天一觀的道長。 經(jīng)常有香客問我,道長苔咪,這世上最難降的妖魔是什么锰悼? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮团赏,結果婚禮上箕般,老公的妹妹穿的比我還像新娘。我一直安慰自己舔清,他們只是感情好丝里,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著体谒,像睡著了一般杯聚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上抒痒,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天幌绍,我揣著相機與錄音,去河邊找鬼故响。 笑死傀广,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的彩届。 我是一名探鬼主播主儡,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惨缆!你這毒婦竟也來了糜值?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤坯墨,失蹤者是張志新(化名)和其女友劉穎寂汇,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捣染,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡骄瓣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耍攘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榕栏。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蕾各,靈堂內(nèi)的尸體忽然破棺而出扒磁,到底是詐尸還是另有隱情,我是刑警寧澤式曲,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布妨托,位于F島的核電站缸榛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏兰伤。R本人自食惡果不足惜内颗,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敦腔。 院中可真熱鬧均澳,春花似錦、人聲如沸符衔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柏腻。三九已至纸厉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間五嫂,已是汗流浹背颗品。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留沃缘,地道東北人躯枢。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像槐臀,于是被迫代替她去往敵國和親锄蹂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 超級英雄們水慨,拼盡全力得糜,也沒能阻擋到滅霸,手指輕輕一扣晰洒,人口消失一半朝抖。世界瞬間清凈了許多,活著的那一半人谍珊,因...
    風嘻嘻閱讀 422評論 0 0
  • 【節(jié)氣詩】夏至│蟬一遍遍說小心 文/養(yǎng)安子 還有一個個仲夏夜治宣,接踵而至還有熱,更熱的熱我們都在等砌滞,最像夏天的夏天在...
    養(yǎng)安子曰閱讀 530評論 2 5
  • 暑假過去了一個月侮邀,在這個一個月中,自認為還是不錯的贝润。 我每天計劃三小時必須寫作業(yè)绊茧,一小時運動,其他時間就看看書题暖、電...
    luguanyu閱讀 187評論 0 0