spring4-1-基礎(chǔ)bean的配置

一.概念

1.輕量級(jí)開(kāi)源框架;
2.簡(jiǎn)化企業(yè)級(jí)應(yīng)用而生;
3.是一個(gè)IOC(DI)依賴(lài)注入和AOP(面向切面編程)容器框架;
4.在IOC和AOP基礎(chǔ)上可以整合各種企業(yè)應(yīng)用的開(kāi)源框架和優(yōu)秀的第三方類(lèi)庫(kù)

二.配置eclipse

以下是不同的eclipse對(duì)應(yīng)的sts版本,請(qǐng)查看好自己的eclipse下載對(duì)應(yīng)的版本

eclipse-kepler.4.3.1–>springsource-tool-suite-RELEASE-e4.3.1-updatesite.zip
eclipse-Mars.4.5.1–>springsource-tool-suite-3.7.2RELEASE-e4.5.1-updatesite.zip
eclipse-Mars.4.5.2–>springsource-tool-suite-3.7.3RELEASE-e4.5.2-updatesite.zip
eclipse-neno.4.6–>springsource-tool-suite-3.7.3RELEASE-e4.6-updatesite.zip

我的版本是eclipse-Mars.4.5.2安裝完畢后,welcome頁(yè)面顯示Spring tool,但是在window->properties中卻沒(méi)有spring選項(xiàng),而且安裝完畢后在properties中連maven選項(xiàng)也不見(jiàn)了,這個(gè)問(wèn)題整整折騰了我半天時(shí)間,卸了裝,裝了卸還是不行,最后問(wèn)同事用的是eclipse-neno.4.6.3發(fā)行版,下載eclipse-neno.4.6.3

eclipse-1.jpg

下載后打開(kāi),提示JDK環(huán)境必須是JDK1.8以上,于是從JDK1.7升級(jí)到JDK1.8,

安裝步驟如下:我用的是(springsource-tool-suite-3.8.4.RELEASE-e4.6.3-updatesite.zip)下載地址:https://spring.io/tools/sts/all

Paste_Image.png

spring-2.jpg
spring-3.jpg

安裝完畢后在window->Preperences中就可以看到spring選項(xiàng)了;


spring-5.jpg

到此為止eclipse中spring插件配置結(jié)束,這個(gè)汗....

三.第一個(gè)helloWorld

  • 下載spring最新穩(wěn)定版本,下載方式請(qǐng)查看:
    http://www.cnblogs.com/yy3b2007com/p/6783699.html

  • 新建java下過(guò)目,spring-1

  • 加載jar包(以下橙色背景的四個(gè)核心jar包必須加載)


    spring-6.jpg
  • 自己下載commons-logging.jar包并加載進(jìn)去

  • 目錄結(jié)構(gòu):


    spring-7.jpg
  • HelloWorld內(nèi)容:

package com.lxf.spring.bean;

public class HelloWorld {
    private String name;
    public void setName(String name)
    {
        System.out.println("setName:"+name);
        this.name = name;
    }
    public void hello()
    {
        System.out.println("hello: "+this.name);
    }
    public  HelloWorld()
    {
        System.out.println("init HelloWorld...");
    }
}
  • Spring Bean Configuration File內(nèi)容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

     <!-- 配置bean -->
    <bean id="helloWorld" class="com.lxf.spring.bean.HelloWorld">
        <property name="name" value="zhangsan"></property>
    </bean>
</beans>
  • Main.java內(nèi)容
package com.lxf.spring.bean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args)
    {
        /*
        HelloWorld helloworld = new HelloWorld();
        helloworld.setName("liangxifeng");
        */
        //1.創(chuàng)建spring的IOC容器對(duì)象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.從IOC容器中獲取Bean實(shí)例
        //HelloWorld helloworld = (HelloWorld)ctx.getBean("helloWorld");
        //helloworld.setName("aaa");
        //3.調(diào)用hello方法
       // helloworld.hello();
    }
}

四.IOC(inversion of Control)和 DI(Dependency Injection)

  • IOC概念:
  • 思想是反轉(zhuǎn)資源獲取的方向
  • 傳統(tǒng)的資源查找方式要求組件向容器發(fā)起請(qǐng)求查找資源.作為回應(yīng),容器實(shí)時(shí)的返回資源;
  • 應(yīng)用了IOC之后,則是容器主動(dòng)地就將資源推送給它所管理的組件
  • 組件所要做的只是選擇一種合適的方式來(lái)接收資源;(也稱(chēng)查找的被動(dòng)形式)
  • DI概念
  • IOC的另一種表達(dá)方式:即組件以一些預(yù)定義好的方式(例如:setter方法)接收來(lái)自容器的資源注入,相對(duì)IOC而言,這種表述更直接;
Paste_Image.png

五.IOC的前生

ioc-1.jpg

ReprotService要想生成報(bào)表需要依賴(lài)三個(gè)類(lèi),耦合度很高

ioc-2.jpg

使用工廠模式ReprotService要想生成報(bào)表需要依賴(lài)兩個(gè)類(lèi),耦合度降低

ioc-3.jpg

使用反轉(zhuǎn)控制ReprotService要想生成報(bào)表需要依賴(lài)一個(gè)類(lèi),耦合度最低

六.spring容器

  • 在Spring IOC容器讀取Bean配置之前,自身要先實(shí)例化;

  • Spring提供了兩種類(lèi)型的IOC容器實(shí)現(xiàn);
    (1). BeanFactory:IOC容器的基本實(shí)現(xiàn)(BeanFactory是spring框架的基礎(chǔ)設(shè)施,面向Spring本身)
    (2). ApplicationContext 是BeanFactory的子接口,提供了更豐富的功能;(ApplicationContext 面向Spring框架的開(kāi)發(fā)者,幾乎所有應(yīng)用場(chǎng)合都直接使用ApplicationContext而非底層的BeanFactory)

  • ApplicationContext有兩個(gè)實(shí)現(xiàn)類(lèi)


    Paste_Image.png
  • 屬性的注入方式一(setter方法)

     <!-- 
        配置bean
        class:bean全類(lèi)名,通過(guò)反射的方式在IOC容器中創(chuàng)建Bean,所以要求Bean中必須有無(wú)參構(gòu)造器
        id:標(biāo)識(shí)容器中的bean 必須唯一 
     -->
    <bean id="helloWorld" class="com.lxf.spring.bean.HelloWorld">
        <property name="name" value="zhangsan"></property>
    </bean>
    private String name;
    public void setName(String name)
    {
        System.out.println("setName:"+name);
        this.name = name;
    }
  • 屬性的注入方式二(構(gòu)造方法注入)
  • 通過(guò)構(gòu)造方法注入Bean的屬性值或依賴(lài)的對(duì)象,它保證了Bean實(shí)例在實(shí)例化后就可以使用
  • 構(gòu)造器注入在<constructor-arg>元素里聲明屬性,<constructor-arg>中沒(méi)有name屬性;
  • 使用構(gòu)造器注入屬性值可以指定參數(shù)的位置和參數(shù)類(lèi)型,以區(qū)分重載構(gòu)造器
    <!-- 通過(guò)構(gòu)造器的方式實(shí)現(xiàn)屬性注入,可以指定參數(shù)的位置和參數(shù)類(lèi)型,以區(qū)分重載構(gòu)造器-->
    <bean id="car1" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽車(chē)1" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
        <!-- 通過(guò)構(gòu)造器的方式實(shí)現(xiàn)屬性注入,可以指定參數(shù)的位置和參數(shù)類(lèi)型,以區(qū)分重載構(gòu)造器 -->
    <bean id="car2" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽車(chē)2" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120" index="1" type="int"></constructor-arg>
    </bean>

以上兩個(gè)bean,根據(jù)參數(shù)類(lèi)型區(qū)分實(shí)例化的時(shí)候調(diào)用哪個(gè)構(gòu)造器
Bean類(lèi)Car

package com.lxf.spring.bean;
public class Car {
    private String cName;
    private double price;
    private int speed;  
    /**
     * 構(gòu)造器,為汽車(chē)名字和汽車(chē)價(jià)格屬性初始化值
     * @param cName
     * @param price
     */
    public Car(String cName, double price) {
        super();
        this.cName = cName;
        this.price = price;
    }

    /**
     * 重載構(gòu)造器,為汽車(chē)名字和汽車(chē)速度初始化值
     * @param cName
     * @param speed
     */
    public Car(String cName, int speed) {
        super();
        this.cName = cName;
        this.speed = speed;
    }
    @Override
    public String toString() {
        return "Car [cName=" + cName + ", price=" + price + ", speed=" + speed + "]";
    }
}

測(cè)試:

        Car car = (Car) ctx.getBean("car1");
        System.out.println(car);
        Car car = (Car) ctx.getBean("car2");
        System.out.println(car);

輸出:

Car [cName=汽車(chē)1, price=150000.0, speed=0]
Car [cName=汽車(chē)2, price=0.0, speed=120]
  • 屬性值也可以使用value子結(jié)點(diǎn)進(jìn)行那個(gè)配置(字面值)
    <!-- 使用value子結(jié)點(diǎn)進(jìn)行配置value值,如果value值包含特殊字符,則使用<![CDATA[]]包裹起來(lái)  -->
     <bean id="car1-1" class="com.lxf.spring.bean.Car">
        <constructor-arg  index="0" type="java.lang.String">
            <value><![CDATA[<汽車(chē)1-1*>]]></value>
        </constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
        Car car3 = (Car) ctx.getBean("car1-1");
        System.out.println(car3);

輸出:

Car [cName=<汽車(chē)1-1*>, price=150000.0, speed=0]
  • bean之間的相關(guān)互引用

person類(lèi)

package com.lxf.spring.bean;

public class Person {
    //姓名
    private String name;
    //年齡
    private int age;
    //該人所擁有的汽車(chē)
    private Car car;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
    }
}

配置bean文件:

    <bean id="car1" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽車(chē)1" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
    <bean id="persion" class="com.lxf.spring.bean.Person">
        <property name="name" value="lisi"></property>
        <property name="age" value="28"></property>
        <!-- 使用property的ref屬性建立bean之間的引用關(guān)系 -->
        <!--  <property name="car" ref="car1"></property>-->
        <!-- 和上面等效 -->
        <!--  
        <property name="car">
            <ref bean="car1"/>
        </property>-->
    
        <!-- 使用內(nèi)部bean,等效與以上,內(nèi)部bean不能被外部訪(fǎng)問(wèn) -->
        <property name="car">
            <bean  class="com.lxf.spring.bean.Car">
                <constructor-arg value="汽車(chē)1" index="0" type="java.lang.String"></constructor-arg>
                <constructor-arg value="150000" index="1" type="double"></constructor-arg>
            </bean>
        </property>

七.級(jí)聯(lián)屬性賦值

    <bean id="car" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽車(chē)2" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120" index="1" type="int"></constructor-arg>
    </bean>
    
    <bean id="persion2" class="com.lxf.spring.bean.Person">
        <constructor-arg value="wangwu"></constructor-arg>
        <constructor-arg value="30"></constructor-arg>
        <!-- 測(cè)試賦值null 
        <constructor-arg><null/></constructor-arg>-->
        <constructor-arg ref="car"></constructor-arg>
        <!-- 級(jí)聯(lián)屬性賦值 -->
        <property name="car.price"  value="12"></property>     
    </bean>

八.集合屬性賦值

(1)List類(lèi)型配置
Person類(lèi)中有屬性

    //該人所擁有的汽車(chē)集合
    private List<Car> cars;
    public List<Car> getCars() {
        return cars;
    }
    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

spring配置bean

    <!-- 通過(guò)構(gòu)造器的方式實(shí)現(xiàn)屬性注入,可以指定參數(shù)的位置和參數(shù)類(lèi)型,以區(qū)分重載構(gòu)造器-->
    <bean id="car1" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽車(chē)1" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="150000" index="1" type="double"></constructor-arg>
    </bean>
        <!-- 通過(guò)構(gòu)造器的方式實(shí)現(xiàn)屬性注入,可以指定參數(shù)的位置和參數(shù)類(lèi)型,以區(qū)分重載構(gòu)造器 -->
    <bean id="car2" class="com.lxf.spring.bean.Car">
        <constructor-arg value="汽車(chē)2" index="0" type="java.lang.String"></constructor-arg>
        <constructor-arg value="120" index="1" type="int"></constructor-arg>
    </bean>
    <!--集合屬性賦值  -->
    <bean id="person3" class="com.lxf.spring.bean.Person">
                <constructor-arg value="wangwu--1"></constructor-arg>
                <constructor-arg value="30"></constructor-arg>
                <constructor-arg><null/></constructor-arg>
                <property name="cars">
                    <list>
                        <ref bean="car1"/>
                        <ref bean="car2"/>
                    </list>
                </property>
    </bean>

(2)Map類(lèi)型屬性賦值
Person類(lèi)中的屬性

    //map類(lèi)型的汽車(chē)
    private Map<String,Car> mapCars;
  public Map<String, Car> getMapCars() {
        return mapCars;
    }
    public void setMapCars(Map<String, Car> mapCars) {
        this.mapCars = mapCars;
    }

Spring配置

    <!-- 集合map屬性賦值 -->
    <bean id="person4" class="com.lxf.spring.bean.Person" >
                <constructor-arg value="趙六"></constructor-arg>
                <constructor-arg value="33"></constructor-arg>
                <constructor-arg><null/></constructor-arg>
                <property name="mapCars">
                    <!-- 使用map節(jié)點(diǎn)以及map節(jié)點(diǎn)子節(jié)entry配置 map類(lèi)型的屬性 -->
                    <map>
                        <entry key="AA"  value-ref="car1"></entry>
                        <entry key="BB" value-ref="car2"></entry>
                    </map>
                </property>
    </bean>

(3)Properites屬性賦值
DataSource實(shí)體類(lèi)

package com.lxf.spring.bean;

import java.util.Properties;

public class DataSource {
    private Properties properties;

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public String toString() {
        return "DataSource [properties=" + properties + "]";
    }
}

(4)使用Utility scheme定義集合
使用Utility schema里的集合標(biāo)簽定義獨(dú)立的集合Bean,必須在Bean根元素里面定義;
這樣定義的集合就可以被其他Bean共享
注意:需要導(dǎo)入util命名空間

col-5.jpg

Person實(shí)體類(lèi)屬性

    //該人所擁有的汽車(chē)集合
    private List<Car> cars;
    public List<Car> getCars() {
        return cars;
    }
    public void setCars(List<Car> cars) {
        this.cars = cars;
    }

Spring配置

    <!-- 配置單獨(dú)的集合bean,以供多個(gè)bean進(jìn)行引用,注意:需要導(dǎo)入util命名空間 -->
    <util:list id="cars">
        <ref bean="car1"/>
        <ref bean="car2"/>
    </util:list>
    <bean id="person5" class="com.lxf.spring.bean.Person">
               <constructor-arg value="小青"></constructor-arg>
                <constructor-arg value="33"></constructor-arg>
                <constructor-arg><null/></constructor-arg>
                <property name="cars" ref="cars"></property>
    </bean>

從Spring2.5起可以使用P命名空間為bean屬性賦值

Spring配置(注意:需導(dǎo)入 p 命名空間,導(dǎo)入方式類(lèi)似util命名空間

  <!-- 通過(guò) p 命名空間為bean屬性賦值,需要先導(dǎo)入 p 命名空間,相對(duì)于傳統(tǒng)方式更加簡(jiǎn)潔  -->
    <bean id="person6" class="com.lxf.spring.bean.Person"  p:age="29"  p:name="小網(wǎng)" p:cars-ref="cars"></bean>

九.bean的自動(dòng)裝配

使用autowire屬性指定自動(dòng)裝配的方式,
(1) byName根據(jù)其他bean的id值和當(dāng)前bean的setter風(fēng)格屬性名進(jìn)行自動(dòng)裝配,如果沒(méi)有匹配的bean則不裝配
(2)byType根據(jù)其他bean的類(lèi)型和當(dāng)前bean屬性的類(lèi)型進(jìn)行自動(dòng)裝配,若IOC容器中有一個(gè)以上的類(lèi)型匹配的bean,則拋異常

    <bean id="address" class="com.lxf.spring.autowire.Address" p:city="Beijing" p:street="huilongguan">
    </bean>
    <bean id="car" class="com.lxf.spring.autowire.Car" p:brand="Audi" p:price="300000"></bean>
    <!-- 使用autowire屬性指定自動(dòng)裝配的方式,
           byName根據(jù)其他bean的id值和當(dāng)前bean的setter風(fēng)格屬性名進(jìn)行自動(dòng)裝配,如果沒(méi)有匹配的bean則不裝配
           byType根據(jù)其他bean的類(lèi)型和當(dāng)前bean屬性的類(lèi)型進(jìn)行自動(dòng)裝配,若IOC容器中有一個(gè)以上的類(lèi)型匹配的bean,則拋異常 -->
    <bean id="person" class="com.lxf.spring.autowire.Person"  p:name="zs" autowire="byType"></bean>

自動(dòng)裝配缺點(diǎn):
(1)如果使用autowire屬性自動(dòng)裝配,那么只要使用autowire的bean中引用其他bean屬性的值都自動(dòng)裝載,不能指定某一個(gè)或幾個(gè)屬性使用自動(dòng)裝載;
(2)自動(dòng)裝載byType方式,如果一個(gè)Spring配置文件中有多個(gè)引用類(lèi)型,則該方式會(huì)不知道裝載哪個(gè),會(huì)拋出異常;
所以在平時(shí)開(kāi)發(fā)中盡量使用手動(dòng)配置,整合其他框架的時(shí)候才使用自動(dòng)裝載

十.Bean之間的關(guān)系繼承

  • Spring允許繼承Bean的配置,稱(chēng)為父Bean和子Bean;
  • 子Bean從父Bean中繼承配置,包括Bean的屬性配置;
  • 子Bean也可以覆蓋父Bean繼承來(lái)的配置;
  • 父Bean可以作為配置模板,也可以作為Bean實(shí)例,可以設(shè)置<bean>的abstract屬性為true,這樣Spring將不會(huì)實(shí)例化這個(gè)Bean;
  • 并不是Bean元素里面的所有屬性都會(huì)被繼承,比如:autowire abstract等不會(huì)被繼承
  • 也可以忽略父Bean的class屬性,讓子Bean指定自己的類(lèi),而共享相同屬性配置,但此時(shí)abstract必須設(shè)為true
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 屬性abstract=true代表設(shè)置該bean為模板(不能被容器實(shí)例化),如果為模板則不需要配置 -->
    <bean id="address1" class="com.lxf.spring.autowire.Address"  p:city="Beijing" p:street="wudaokou" abstract="true"></bean>
    <!-- 使用parent屬性繼承其他bean配置 -->
    <bean id="address2"  p:street="dazhongsi"  parent="address1"></bean>
</beans>

十一.Bean之間的關(guān)系依賴(lài)

  • 使用depends-on屬性設(shè)定Bean前置依賴(lài)的Bean,前置依賴(lài)的Bean會(huì)在本Bean實(shí)例化之前創(chuàng)建好;
  • 如果前置依賴(lài)多個(gè)Bean,則可以通過(guò)逗號(hào),空格或的方式配置Bean的名稱(chēng);
    <bean id="car" class="com.lxf.spring.autowire.Car"></bean>
    <!-- 使用depends-on屬性設(shè)定Bean前置依賴(lài)的Bean,必須提前配置car的bean -->
    <bean id="person" class="com.lxf.spring.autowire.Person"  p:name="zs"  depends-on="car" p:car-ref="car"></bean>

十二.Bean的作用域

  • 默認(rèn)單例: bean屬性scope=singleton(默認(rèn)值),IOC容器初始化的時(shí)創(chuàng)建bean實(shí)例,在整個(gè)容器的生命周期內(nèi)只創(chuàng)建一個(gè)bean,單例的; 比如:
    Car類(lèi)有無(wú)參構(gòu)造器:
public class Car {
    private String brand;
    private double price;
    public Car()
    {
        System.out.println("Car is Constract...");
    }
}

Spring配置

    <!-- 
        使用bean的scope屬性配置bean的作用域,scope值如下:
        singleton:默認(rèn)值,IOC容器初始化的時(shí)創(chuàng)建bean實(shí)例,在整個(gè)容器的生命周期內(nèi)只創(chuàng)建一個(gè)bean,單例的;
        -->
    <bean id="car" class="com.lxf.spring.autowire.Car" p:brand="Audi" p:price="300000" scope="singleton"></bean>

main方法測(cè)試:

        //創(chuàng)建spring的IOC容器對(duì)象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");   

輸出:

Car is Constract...

證明容器在初始化的時(shí)候創(chuàng)建單例bean;
main方法測(cè)試:

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml");   
        Car car1 = (Car)ctx.getBean("car");
        Car car2 = (Car)ctx.getBean("car");
        System.out.println(car1==car2);//輸出true

以上car1=car2,證明每次調(diào)用bean的使用都是

  • 多例: bean屬性scope=prototype(原型的),IOC容器初始化時(shí)不創(chuàng)建bean實(shí)例,而在每次請(qǐng)求時(shí)都創(chuàng)建一個(gè)新的Bean實(shí)例,并返回,比如:
    修改上面的Spring配置
    <!-- 
        使用bean的scope屬性配置bean的作用域,scope值如下:
        singleton:默認(rèn)值,IOC容器初始化的時(shí)創(chuàng)建bean實(shí)例,在整個(gè)容器的生命周期內(nèi)只創(chuàng)建一個(gè)bean,單例的;
        prototype:原型的,IOC容器初始化時(shí)不創(chuàng)建bean實(shí)例,而在每次請(qǐng)求時(shí)都創(chuàng)建一個(gè)新的Bean實(shí)例,并返回;
        -->
    <bean id="car" class="com.lxf.spring.autowire.Car" p:brand="Audi" p:price="300000" scope="prototype"></bean>

main方法測(cè)試:

        //創(chuàng)建spring的IOC容器對(duì)象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-scope.xml"); //不創(chuàng)建bean實(shí)例  
        Car car1 = (Car)ctx.getBean("car"); //創(chuàng)建一個(gè)bean實(shí)例
        Car car2 = (Car)ctx.getBean("car");//再次創(chuàng)建一個(gè)bean實(shí)例
        System.out.println(car1==car2);//返回false

十三.配置bean中使用外部屬性文件

Paste_Image.png
  • 首先在Spring配置文件中打開(kāi)context命名空間;
  • src下新建db.properties屬性配置文件
userName=root
pass=123456
driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql:///test
  • 在Spring配置文件(beans-properties.xml)中引用該配置文件中的屬性變量
    <!-- 導(dǎo)入屬性文件 classpath代表類(lèi)路徑 -->
    <context:property-placeholder location="classpath:db.properties"/> 
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 使用外部屬性文件的屬性 -->
        <property name="user" value="${userName}"></property>
        <property name="password" value="${pass}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
    </bean>
  • main方法測(cè)試:
        //1.創(chuàng)建spring的IOC容器對(duì)象
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-properties.xml");   
        //獲取數(shù)據(jù)源
        DataSource dataSource = (DataSource)ctx.getBean("dataSource");
        //打印數(shù)據(jù)庫(kù)連接
        System.out.println(dataSource.getConnection());

輸出結(jié)果:

com.mchange.v2.c3p0.impl.NewProxyConnection@ee3d0a5
  • 以上測(cè)試需要加入:c3p0-0.9.1.2.jar 和 jdbc jar包,其中C3P0是一個(gè)開(kāi)源的JDBC連接池

十四.Spring的spel表達(dá)式

<bean id="address" class="com.lxf.spring.spel.Address">
    <property name="city" value="#{'北京'}"></property>
    <property name="street" value="五道口"></property>
</bean>

<bean id="car" class="com.lxf.spring.spel.Car">
    <property name="brand" value="Audi"></property>
    <property name="price" value="500000"></property>
    <!-- 使用SPEL引用類(lèi)的靜態(tài)屬性,并在SPEL表達(dá)式中進(jìn)行計(jì)算 -->
    <property name="tyrePerimeter" value="#{T(java.lang.Math).PI*80}"></property>
</bean>

<bean id="person" class="com.lxf.spring.spel.Person">
    <property name="name" value="張三"></property>
    <!-- 使用spel來(lái)引用其他bean -->
    <property name="car" value="#{car}"></property>
    <!-- 使用spel來(lái)引用其他bean屬性 -->
    <property name="city" value="#{address.city}"></property>
    <!-- 在spel中使用運(yùn)算符 -->
    <property name="work" value="#{car.price>300000 ? '金領(lǐng)' : '白領(lǐng)'}"></property>
</bean>

十五.IOC容器中Bean的生命周期

Paste_Image.png

舉例如下:
(1)Car實(shí)體類(lèi):

package com.lxf.spring.cycle;
/**
 * 汽車(chē)實(shí)體類(lèi)
 * @author lxf
 */
public class Car {
    private String brand;
    private double price;
    //輪胎周長(zhǎng)
    private double tyrePerimeter;
    /**
     * 無(wú)參數(shù)構(gòu)造器
     */
    public Car()
    {
        System.out.println("Car is Constract...");
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        System.out.println("setBrand為品牌屬性賦值");
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public double getTyrePerimeter() {
        return tyrePerimeter;
    }
    public void setTyrePerimeter(double tyrePerimeter) {
        this.tyrePerimeter = tyrePerimeter;
    }
    @Override
    public String toString() {
        return "Car [brand=" + brand + ", price=" + price + ", tyrePerimeter=" + tyrePerimeter + "]";
    }
    
    /**
     * Bean初始化方法,在IOC容器啟動(dòng)的時(shí)候調(diào)用
     */
    public void myInit()
    {
        System.out.println("創(chuàng)建了IOC容器:調(diào)用Bean的初始化方法");
    }
    /**
     * Bean銷(xiāo)毀方法,在IOC容器關(guān)閉的時(shí)候調(diào)用
     */
    public void myDestroy()
    {
        System.out.println("調(diào)用Bean的銷(xiāo)毀方法,IOC容器關(guān)閉了");
    }
}

(2)Spring配置文件(beans-cycle.xml)

    <!-- 
        bean的init-method屬性調(diào)用對(duì)應(yīng)類(lèi)中的自定義方法myInit方法,在IOC容器創(chuàng)建的時(shí)候執(zhí)行
        bean的destroy-method屬性調(diào)用對(duì)應(yīng)類(lèi)中的自定義方法myDestroy,在IOC容器關(guān)閉的時(shí)候執(zhí)行
     -->
    <bean id="car" class="com.lxf.spring.cycle.Car" 
            init-method="myInit" 
            destroy-method="myDestroy">
        <property name="brand" value="Audi"></property>
    </bean>

(3)main方法測(cè)試

        //創(chuàng)建spring的IOC容器對(duì)象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");   
        Car car1 = (Car)ctx.getBean("car");
        System.out.println("使用Bean: "+car1);
        //關(guān)閉IOC容器
        ctx.close();

輸出結(jié)果如下:

Car is Constract...
setBrand為品牌屬性賦值
創(chuàng)建了IOC容器:調(diào)用Bean的初始化方法
使用Bean: Car [brand=Audi, price=0.0, tyrePerimeter=0.0]
...
調(diào)用Bean的銷(xiāo)毀方法,IOC容器關(guān)閉了

Paste_Image.png

Paste_Image.png

修改以上代碼加入后置處理器bean的生命周期:
(1)新建后置處理器MyBeanPostProcessor

package com.lxf.spring.cycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     *  bean的init-method方法之后調(diào)用
     *  參數(shù):
     *  bean: IOC傳遞進(jìn)來(lái)的bean實(shí)例
     *  beanName:bean的id
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        if("car".equals(beanName))
        {
            System.out.println("bean的init-method方法之后調(diào)用:"+bean);
        }      
        return bean;
    }
    /*
     * bean的init-method方法之前調(diào)用
     *  參數(shù):
     *  bean: IOC傳遞進(jìn)來(lái)的bean實(shí)例
     *  beanName:bean的id
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // TODO Auto-generated method stub
        if("car".equals(beanName))
        {
            System.out.println("bean的init-method方法之前調(diào)用:"+bean);
            Car car = new Car();
            car.setBrand("大眾");
            //對(duì)bean重新賦值
            bean = car;
        }       
        return bean;
    }
}

(2)Spring配置文件beans-cycle.xml修改

    <!-- 
        bean的init-method屬性調(diào)用對(duì)應(yīng)類(lèi)中的自定義方法myInit方法,在IOC容器創(chuàng)建的時(shí)候執(zhí)行
        bean的destroy-method屬性調(diào)用對(duì)應(yīng)類(lèi)中的自定義方法myDestroy,在IOC容器關(guān)閉的時(shí)候執(zhí)行
     -->
    <bean id="car" class="com.lxf.spring.cycle.Car" 
            init-method="myInit" 
            destroy-method="myDestroy">
        <property name="brand" value="Audi"></property>
    </bean>
    
    <!-- 配置bean的后置處理器: 不需要配置id, IOC容器自動(dòng)識(shí)別 -->
    <!-- 
        實(shí)現(xiàn)BeanPostProcessor接口,并具體實(shí)現(xiàn)
        postProcessBeforeInitialization(Object bean, String beanName):init-method之前被調(diào)用
        postProcessAfterInitialization(Object bean, String beanName) :init-method之后被調(diào)用
        
        以上兩個(gè)方法參數(shù):
        bean: bean實(shí)例本身
        beanName: IOC容器配置的bean的id
        
        以上兩個(gè)方法返回值:
        實(shí)際上返回給用戶(hù)的那個(gè)Bean,注意:可以在以上兩個(gè)方法中修改返回的Bean,甚至返回一個(gè)新Bean
     -->   
    <bean class="com.lxf.spring.cycle.MyBeanPostProcessor"></bean>

(3)main方法測(cè)試:

        //創(chuàng)建spring的IOC容器對(duì)象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");   
        Car car1 = (Car)ctx.getBean("car");
        System.out.println("使用Bean: "+car1);
        //關(guān)閉IOC容器
        ctx.close();

輸出結(jié)果:

Car is Constract...
setBrand為品牌屬性賦值
bean的init-method方法之前調(diào)用:Car [brand=Audi, price=0.0, tyrePerimeter=0.0]
Car is Constract...
setBrand為品牌屬性賦值
創(chuàng)建了IOC容器:調(diào)用Bean的初始化方法
bean的init-method方法之后調(diào)用:Car [brand=大眾, price=0.0, tyrePerimeter=0.0]
使用Bean: Car [brand=大眾, price=0.0, tyrePerimeter=0.0]
調(diào)用Bean的銷(xiāo)毀方法,IOC容器關(guān)閉了

十六.使用工廠方法創(chuàng)建Bean

Paste_Image.png

舉例:
(1)新建靜態(tài)工廠方法類(lèi):StaticFactory

package com.lxf.spring.factory;

import java.util.HashMap;
import java.util.Map;

/**
 * 靜態(tài)工廠方法:直接調(diào)用某一個(gè)類(lèi)的靜態(tài)方法可以返回Bean實(shí)例
 * @author lxf
 *
 */
public class StaticFactory {
    private static Map<String,Car> cars = new HashMap<String,Car>();
    static{
        cars.put("audi", new Car("audi"));
        cars.put("ford", new Car("ford"));
    }
    /**
     * 靜態(tài)工廠方法返回對(duì)應(yīng)Bean實(shí)例
     * @param name
     * @return
     */
    public static Car getCar(String name)
    {
        return cars.get(name);
    }
}

(2)Spring配置文件beans-factory.xml

    <!-- 通過(guò)靜態(tài)方法工廠配置bean, 注意不是配置靜態(tài)方法實(shí)例,而是bean實(shí)例 -->
    <!-- 
        class屬性:指向靜態(tài)工廠方法的全類(lèi)名
        factory-method屬性:指向靜態(tài)工廠方法名
        constructor-arg標(biāo)簽:如果工廠方法需要傳入?yún)?shù),則使用constructor-arg標(biāo)簽來(lái)配置
     -->
    <bean id="car1" 
        class="com.lxf.spring.factory.StaticFactory"
        factory-method="getCar">
        <constructor-arg value="audi" /></bean>

(3)man方法測(cè)試:

        //創(chuàng)建spring的IOC容器對(duì)象
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");   
        Car car1 = (Car)ctx.getBean("car1");
        System.out.println("使用Bean: "+car1);

輸出:

使用Bean: Car [brand=audi, price=0.0, tyrePerimeter=0.0]
Paste_Image.png

舉例:
(1)新建實(shí)例工廠方法類(lèi):InstanceFactory

package com.lxf.spring.factory;
import java.util.HashMap;
import java.util.Map;
/**
 * 實(shí)例工廠方法:實(shí)例工廠的方法,即先需要?jiǎng)?chuàng)建工廠本身實(shí)例,
 *                             再調(diào)用工廠的實(shí)例方法來(lái)返回bean的實(shí)例.
 * @author lxf
 */
public class InstanceFactory {
    private Map<String,Car> cars = null;
    public InstanceFactory()
    {
        cars = new HashMap<String,Car>();
        cars.put("audi", new Car("audi"));
        cars.put("ford", new Car("ford"));
    }
    public Car getCar(String name)
    {
        return cars.get(name);
    }
}

(2)Spring配置文件(beans-factory.xml)

     <!-- 先創(chuàng)建工廠實(shí)例本身 -->
     <bean id="factory"  class="com.lxf.spring.factory.InstanceFactory"></bean>
     
     <!-- 在通過(guò)實(shí)例工廠方法配置bean實(shí)例 -->
         <!-- 
        factory-bean屬性:指向?qū)嵗S方法的bean
        factory-method屬性:指向?qū)嵗龔S方法名
        constructor-arg標(biāo)簽:如果工廠方法需要傳入?yún)?shù),則使用constructor-arg標(biāo)簽來(lái)配置
     -->
     <bean id="car2"  factory-bean="factory" factory-method="getCar">
        <constructor-arg value="ford"></constructor-arg>
     </bean>

(3)mani方法測(cè)試:

        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factory.xml");           
        Car car2 = (Car)ctx.getBean("car2");
        System.out.println("使用Bean: "+car2);

輸出:

使用Bean: Car [brand=ford, price=0.0, tyrePerimeter=0.0]

使用FactoryBean的方式配置Bean

  • 其實(shí)就是自己定義一個(gè)類(lèi),實(shí)現(xiàn)Spring的FactoryBean接口,實(shí)現(xiàn)該接口的三個(gè)方法即可;(其中有一個(gè)方法返回Bean實(shí)例)
package com.lxf.spring.factorybean;
import org.springframework.beans.factory.FactoryBean;
/**
 * 通過(guò)FactoryBean的方式配置Bean,需要實(shí)現(xiàn)FactoryBean接口
 * @author lxf
 * @param <T>
 */
public class CarFactoryBean<T> implements FactoryBean<T> {
    private String brand;
    public void setBrand(String brand) {
        this.brand = brand;
    }
    @Override
    /**
     * 返回bean對(duì)象
     */
    public T getObject() throws Exception {
        // TODO Auto-generated method stub
        return (T) new Car(brand,5000000);
    }
    @Override
    /**
     * 返回Bean類(lèi)型
     */
    public Class<?> getObjectType() {
        // TODO Auto-generated method stub
        return Car.class;
    }
    @Override
    /**
     * 是否是單例
     */
    public boolean isSingleton() {
        // TODO Auto-generated method stub
        return true;
    }
}
  • 在Spring配置文件中beans-facotrybean.xml中配置自己定義的類(lèi)
    <!-- 
        通過(guò)FactoryBean類(lèi)配置Bean的實(shí)例
        class:指向FactoryBean的全類(lèi)名
        property:配置FactoryBean的屬性
        但實(shí)際返回的實(shí)例確實(shí)FacboryBean的getObject()方法返回的實(shí)例
     -->
    <bean id="car" class="com.lxf.spring.factorybean.CarFactoryBean">
        <property name="brand" value="BMW"></property>
    </bean>
  • main方法測(cè)試:
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-factorybean.xml");   
        Car car1 = (Car)ctx.getBean("car");
        System.out.println("使用Bean: "+car1);

輸出:

使用Bean: Car [brand=BMW, price=5000000.0, tyrePerimeter=0.0]

使用注解的方式配置bean

注意:需要引入spring-aop-4.3.9.RELEASE.jar

Paste_Image.png

Paste_Image.png
Paste_Image.png

Spring配置文件beans-annotation.xml配置

        <!-- 
          指定Spring IOC容器的掃描包,IOC容器在啟動(dòng)的時(shí)候,
          會(huì)掃表base-package指定的當(dāng)前包以及子包下所有包含指定注解的bean,并將其實(shí)例
          可通過(guò)resource-pattern="repository/*.class"指定掃描資源
        
        <context:component-scan 
                base-package="com.spring.beans.annotation"
                resource-pattern="repository/*.class">
         </context:component-scan> -->
        
       <!-- 排除指定注解下的目標(biāo)類(lèi) 
       <context:component-scan 
                base-package="com.spring.beans.annotation" >
                <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
         </context:component-scan>--> 
         
       <!-- 包含指定注解下的目標(biāo)類(lèi) context:include-filter要和use-default-filters屬性配合使用(默認(rèn)掃描所有相關(guān)注解)
       <context:component-scan 
                base-package="com.spring.beans.annotation" use-default-filters="false" >
                <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
         </context:component-scan>--> 
         
        <!-- 排除指定類(lèi)bean的加載 
       <context:component-scan 
                base-package="com.spring.beans.annotation" >
                <context:exclude-filter type="assignable" expression="com.spring.beans.annotation.repository.UserRepositoryImpl"/>
         </context:component-scan>--> 
         
       <!-- 包含指定類(lèi)bean的加載 context:include-filter需要和use-default-filter屬性配合使用--> 
       <context:component-scan 
                base-package="com.spring.beans.annotation"  use-default-filters="false">
                <context:include-filter type="assignable" expression="com.spring.beans.annotation.repository.UserRepositoryImpl"/>
         </context:component-scan>

以上目錄結(jié)構(gòu):

src
 |__com.spring.beans.annotation
 |   |__Main.java
 |   |__TestObject.java
 |   |__service
 |       |__UserService.java
 |   |__respository
 |       |__UserRespository.java
 |   |__controller
 |       |__UserController.java
 |__beans.annotation.xml

UserService.java類(lèi)帶有@Service注解

package com.spring.beans.annotation.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    public void add()
    {
        System.out.println("UserService add...");
    }
}

其他User*.java分別帶有如下注解

@Repository("userRepository")
@Controller
@Repository("userRepository"

Bean之間屬性互相依賴(lài)的解決方案:

Paste_Image.png
Paste_Image.png
package com.spring.beans.annotation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import com.spring.beans.annotation.TestObject;
import com.spring.beans.annotation.repository.UserRepository;
import com.spring.beans.annotation.service.UserService;

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    
    @Autowired
    //如果IOC容器中存在多個(gè)UserRepository接口的實(shí)現(xiàn)類(lèi),
    //可以指定具體裝在哪個(gè)實(shí)現(xiàn)類(lèi),注意首字母小寫(xiě)(spring規(guī)定)
    @Qualifier("jdbcRespository")
    private UserRepository userRepository;
    
   //如果IOC容器中沒(méi)有testObj這個(gè)bean,則使用@Autowired(required=false,否則異常
    @Autowired(required=false)    
    private TestObject testObj;
    public void execute()
    {
        System.out.println("UserControler execute...");
        userService.add();
    }
}

十七.Spring4.* 泛型的依賴(lài)注入

在實(shí)際開(kāi)發(fā)中,往往我們可以將一些結(jié)構(gòu)職能相似的類(lèi)抽象出一定的泛型超類(lèi)。這樣相應(yīng)結(jié)構(gòu)中的類(lèi)之間的依賴(lài)關(guān)系我們可以通過(guò)泛型超類(lèi)來(lái)建立,而其他的子類(lèi)只要繼承了它們,就能夠建立相應(yīng)的依賴(lài)關(guān)系:

參考鏈接:https://my.oschina.net/happyBKs/blog/482508

代碼演示點(diǎn)擊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 178,672評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,965評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評(píng)論 6 413
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 56,019評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 43,188評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評(píng)論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,252評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,590評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,384評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評(píng)論 2 380

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