Spring知識點(diǎn)筆記

1.1.1 Spring簡介

Spring是一個(gè)開源的控制反轉(zhuǎn)(IoC)和面向且切面(AOP)的容器框架斑响。

IOC控制反轉(zhuǎn):應(yīng)用本身不負(fù)責(zé)以來對象的創(chuàng)建和維護(hù),以來對象的創(chuàng)建及維護(hù)由外部容器負(fù)責(zé)钳榨。這樣控制權(quán)就轉(zhuǎn)移到了外部容器。

Dependency Injection依賴注入:在運(yùn)行期纽门,由外部容器動態(tài)地將以來對象注入到組件中薛耻。

事務(wù)控制全部交給spring處理,不用手工編寫事務(wù)的創(chuàng)建和提交


1.1.2 Spring配置和搭建

Eclipse下的配置

下載了spring-framework-2.5.6包赏陵,把dist文件夾下的spring.jar添加到工程饼齿,還要添加一個(gè)jar是common-logging的饲漾,在hibernate學(xué)習(xí)的時(shí)候下載過了,也添加進(jìn)去缕溉。

在src目錄下新建beans.xml

 <?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-2.5.xsd">

 </beans>

初始化spring容器

ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"services.xml", "daos.xml"});//可以通過數(shù)組傳遞多個(gè)配置文件考传。

下面使用JUnit4進(jìn)行測試,new->JUnit Test

image.png

(不要勾選setUpBeforeClass)

SprintTest.java

SprintTest.java

  package junit.test;  
    
  import org.junit.Test;  
  import org.springframework.context.ApplicationContext;  
  import org.springframework.context.support.ClassPathXmlApplicationContext;  
    
  public class SpringTest {  
    
      @Test  
      public void instanceSpring(){  
          ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");  
            
      }  
    
  }   

運(yùn)行以后測試成功

下面寫一個(gè)bean

PersonService.java 放在src中service包下

package service.impl; 

public interface PersonService { 

 public abstract void save();

} 

PersonServiceBean.java 放在src中service.impl包下

   package service;  
    
  import service.impl.PersonService;  
    
  public class PersonServiceBean implements PersonService {  
      /* (non-Javadoc) 
       * @see service.impl.PersonService#save() 
       */  
      public void save(){  
          System.out.println("save方法");  
      }  
  }  

下面配置beans.xml

在沒聯(lián)網(wǎng)的情況下需要添加提示文件才會出現(xiàn)關(guān)鍵字提示证鸥。

windows->perferences->XML->XML Catalog

add

Location:在spring包下找到dist/resources/spring-beans-2.5.xsd

Key Type:Namespace Name

Key:http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

完成添加僚楞。

在beans標(biāo)簽中間添加

<bean id="personService" class="service.PersonServiceBean"></bean>

使用bean標(biāo)簽的時(shí)候,id和name的區(qū)別:id不可以包含特殊字符枉层,name可以泉褐。

SpringTest.java

   package junit.test;  
    
  import org.junit.Test;  
  import org.springframework.context.ApplicationContext;  
  import org.springframework.context.support.ClassPathXmlApplicationContext;  
    
  import service.impl.PersonService;  
    
  public class SpringTest {  
      @Test  
      public void instanceSpring(){  
          ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");  
          PersonService personService = (PersonService)context.getBean("personService");  
          personService.save();  
      }  
    
  } 

運(yùn)行成功


1.1.3 Spring管理bean的原理

簡單的一套管理bean的代碼,就是一個(gè)模擬的ApplicationContext類鸟蜡。

主要方法有兩個(gè)

this.readXML(filename);//讀取XML配置文件
this.instanceBeans();//實(shí)例化bean

主要成員變量

private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();//保存XML中的bean信息膜赃,包括id和class,組成BeanDefinition類 
private Map<String, Object> sigletons = new HashMap<String, Object>();//保存實(shí)例化以后的bean

具體的代碼實(shí)現(xiàn)不細(xì)化研究了揉忘,大概流程:

首先使用dom4j讀取XML中的bean標(biāo)簽跳座,把id和class保存在beanDifines數(shù)組中,然后在instanceBeans()方法中遍歷beanDifines數(shù)組泣矛,取出類名疲眷,利用發(fā)射技術(shù)進(jìn)行實(shí)例化,如Class.forName(beanDefinition.getClassName()).newInstance());并把實(shí)例化對象保存在sigletons的哈希表中乳蓄。完成實(shí)例化

接下來就是簡單的get方法來取出對應(yīng)的bean供其他類調(diào)用咪橙。

可以發(fā)現(xiàn)bean的實(shí)例化就是在創(chuàng)建ApplicationContext對象的時(shí)候,通過構(gòu)造方法進(jìn)行實(shí)例化的虚倒。

代碼:

ItcastClassPathApplicationContext.java

  package junit.test;  
    
  import java.net.URL;  
  import java.util.ArrayList;  
  import java.util.HashMap;  
  import java.util.HashSet;  
  import java.util.List;  
  import java.util.Map;  
    
  import org.dom4j.Document;  
  import org.dom4j.Element;  
  import org.dom4j.XPath;  
  import org.dom4j.io.SAXReader;  
  
  public class ItcastClassPathXMLApplicationContext {  
      private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();  
      private Map<String, Object> sigletons = new HashMap<String, Object>();  
        
      public ItcastClassPathXMLApplicationContext(String filename){  
          this.readXML(filename);  
          this.instanceBeans();  
      }  
      /** 
       * 完成bean的實(shí)例化 
       */  
      private void instanceBeans() {  
          for(BeanDefinition beanDefinition : beanDefines){  
              try {  
                  if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))  
                      sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());  
              } catch (Exception e) {  
                  e.printStackTrace();  
              }  
          }  
            
      }  
      /** 
       * 讀取xml配置文件 
       * @param filename 
       */  
      private void readXML(String filename) {  
             SAXReader saxReader = new SAXReader();     
              Document document=null;     
              try{  
               URL xmlpath = this.getClass().getClassLoader().getResource(filename);  
               document = saxReader.read(xmlpath);  
               Map<String,String> nsMap = new HashMap<String,String>();  
               nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空間  
               XPath xsub = document.createXPath("http://ns:beans/ns:bean");//創(chuàng)建beans/bean查詢路徑  
               xsub.setNamespaceURIs(nsMap);//設(shè)置命名空間  
               List<Element> beans = xsub.selectNodes(document);//獲取文檔下所有bean節(jié)點(diǎn)   
               for(Element element: beans){  
                  String id = element.attributeValue("id");//獲取id屬性值  
                  String clazz = element.attributeValue("class"); //獲取class屬性值          
                  BeanDefinition beanDefine = new BeanDefinition(id, clazz);  
                  beanDefines.add(beanDefine);  
               }     
              }catch(Exception e){     
                  e.printStackTrace();  
              }  
      }  
      /** 
       * 獲取bean實(shí)例 
       * @param beanName 
       * @return 
       */  
      public Object getBean(String beanName){  
          return this.sigletons.get(beanName);  
      }  
  } 

BeanDefinition.java

  package junit.test;  
    
  public class BeanDefinition {  
      private String id;  
      private String className;  
        
      public BeanDefinition(String id, String className) {  
          this.id = id;  
          this.className = className;  
      }  
      public String getId() {  
          return id;  
      }  
      public void setId(String id) {  
          this.id = id;  
      }  
      public String getClassName() {  
          return className;  
      }  
      public void setClassName(String className) {  
          this.className = className;  
      }  
        
  }   

測試代碼

ItcastClassPathXMLApplicationContext ctx = new ItcastClassPathXMLApplicationContext("beans.xml");  

 PersonService personService = (PersonService)ctx.getBean("personService");

 personService.save();

1.1.4 三種實(shí)例化bean的方式

1.使用類構(gòu)造器實(shí)例化

<bean id="personService" class="service.impl.PersonServiceBean"></bean>

2.使用靜態(tài)工廠方法實(shí)例化

beans.xml

<bean id="personService2" class="service.impl.PersonServiceBeanFactory" factory-method="createPersonServiceBean"></bean>  

PersonServiceBeanFactory.java

1. public static PersonServiceBean createPersonServiceBean(){  

2.     return new PersonServiceBean();  

3. } 

3.使用實(shí)例工廠方法實(shí)例化

beans.xml

 <bean id="personServiceFactory" class="service.impl.PersonServiceBeanFactory"></bean>

 <bean id="personService3" factory-bean="personServiceFactory" factory-method="createPersonServiceBean2"></bean>

PersonServiceBeanFactory.java

1. public PersonServiceBean createPersonServiceBean2(){  

2.         return new PersonServiceBean();  

3.     }  

三種方式測試成功

絕大部分都使用第一種實(shí)例化方法


1.1.5 spring管理bean的作用域

來看一段代碼

 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

 PersonService personService1 = (PersonService)context.getBean("personService");

 PersonService personService2 = (PersonService)context.getBean("personService");

 System.out.println(personService1==personService2);

輸出結(jié)果為true美侦,說明getBean方法獲取的是單實(shí)例

那么就來看bean的作用域

bean標(biāo)簽的scope屬性來設(shè)置

singleton

默認(rèn)情況下的單例模式,每次調(diào)用getBean方法獲取的都是同一個(gè)bean對象

默認(rèn)情況喜愛會在容器啟動時(shí)初始化bean魂奥,但我們可以指定Bean節(jié)點(diǎn)的lazy-init屬性來延遲初始化bean菠剩,這時(shí)候,只有第一次獲取bean才會初始化bean耻煤。

<bean id="xxx" class="xxx" lazy_init="true"/>

如果對所有bean都應(yīng)用延遲初始化

<beans default-lazy-init="true"...>

prototype

每次從容器獲取bean都是一個(gè)新的對象

request

session

global session


1.1.6 spring管理bean的生命周期

從前面可以看到具壮,

singleton模式下bean的實(shí)例化的時(shí)機(jī)是在ApplicationContext實(shí)例化的時(shí)候進(jìn)行實(shí)例化,然而設(shè)置lazy-init=true的情況下在getBean方法調(diào)用的時(shí)候進(jìn)行實(shí)例化哈蝇。

prototype模式下bean的實(shí)例化時(shí)機(jī)是在getBean方法調(diào)用的時(shí)候進(jìn)行實(shí)例化棺妓。

如果我們需要在bean初始化的時(shí)候,打開數(shù)據(jù)庫資源等連接炮赦,那么指定一個(gè)初始化方法怜跑,這樣在bean實(shí)例化的時(shí)候就會自動初始化。同樣的指定一個(gè)銷毀方法。

beans.xml

<bean id="personService" class="service.impl.PersonServiceBean" init-method="init" destroy-method="destroy"></bean>  

PersonServiceBean.java

 public void init(){

 System.out.println("inti初始化方法");  

 }

 public void destroy(){

 System.out.println("destroy銷毀方法");  

 }

SprintTest.java

1.AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");  
  1. context.close();

測試結(jié)果輸出

inti初始化方法destroy銷毀方法

lazy-init應(yīng)該是一個(gè)優(yōu)化spring很好的東西性芬,就像hibernate里的懶加載峡眶,有些bean可能一直沒有用到過,根本沒必要初始化植锉,但是視頻里說盡量使用lazy-init=false辫樱,為了發(fā)現(xiàn)所有bean可能出現(xiàn)的錯(cuò)誤,難道測試的時(shí)候要這么做俊庇?另外JUnit的測試也不是很明白狮暑。。暇赤。感覺和一般的差不多啊


1.1.7 剖析Spring依賴注入的原理

通過set方法注入

  package dao.impl;  
    
  import dao.PersonDao;  
    
  public class PersonDaoBean implements PersonDao {  
      /* (non-Javadoc) 
       * @see dao.impl.PersonDao#add() 
       */  
      public void add(){  
          System.out.println(this.getClass().getName()+" add方法");  
      }  
  } 

PersonServiceBean.java

?  package service.impl;  
?    
?  import dao.PersonDao;  
?  import service.PersonService;  
?    
?    
?  public class PersonServiceBean implements PersonService {  
?      private PersonDao personDao;  
?        
?      public PersonDao getPersonDao() {  
?          return personDao;  
?      }  
?    
?      public void setPersonDao(PersonDao personDao) {  
?          this.personDao = personDao;  
?      }  
?    
?      public void save(){  
?          personDao.add();  
?      }  
?        
?      public void init(){  
?          System.out.println("inti初始化方法");  
?      }  
?        
?      public void destroy(){  
?          System.out.println("destroy銷毀方法");  
?      }  
?  }   

beans.xml

?  <?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-2.5.xsd">  
?    
?      <bean id="personDao" class="dao.impl.PersonDaoBean"></bean>  
?      <bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">  
?          <property name="personDao" ref="personDao"></property>  
?      </bean>  
?            
?      </beans>   

SpringTest.java

?  package junit.test;  
?        
?  import org.junit.Test;  
?  import org.springframework.context.support.AbstractApplicationContext;  
?  import org.springframework.context.support.ClassPathXmlApplicationContext;  
?    
?  import service.PersonService;  
?    
?  public class SpringTest {  
?      @Test  
?      public void instanceSpring(){  
?          AbstractApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");  
?          PersonService personService = (PersonService)context.getBean("personService");  
?          personService.save();  
?          context.close();  
?    
?      }  
?    
?  }  

運(yùn)行結(jié)果:

dao.impl.PersonDaoBean add方法

還有一種內(nèi)部bean的注入方式

·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">  

· <property name="personDao">

· <bean class="dao.impl.PersonDaoBean"></bean>

· </property>

· </bean>

即在property標(biāo)簽內(nèi)部添加bean標(biāo)簽心例,在bean標(biāo)簽里寫出類名即可。效果一樣鞋囊。

實(shí)際上spring的操作流程是吧bean中的property也保存在一個(gè)數(shù)組中止后,初始化的時(shí)候遍歷數(shù)組,找到需要注入的bean溜腐,利用反射技術(shù)調(diào)用set方法译株,參數(shù)為配置文件中的ref應(yīng)用的bean,就完成了注入挺益。


1.1.8 Spring裝配基本屬性的原理

上一篇的注入都是對象的注入歉糜,這篇來分析基本屬性的注入。

以String類型為例

PersonServiceBean.java中加入

private String name;  

beans.xml

·<bean id="personService" class="service.impl.PersonServiceBean" lazy-init="false" init-method="init" destroy-method="destroy">  

· <property name="personDao">

· <bean class="dao.impl.PersonDaoBean"></bean>

· </property>

· <property name="name" value="pf"></property>

· </bean>

其他基本類型類似望众,均采用value屬性方式賦值匪补。


1.1.9 Spring如何裝配各種集合類型的屬性

set:
private Set<String> set;

· <property name="set">

· <set>

· <value>第一個(gè)</value>  

· <value>第二個(gè)</value>  

· <value>第三個(gè)</value>  

· </set>

· </property>

list:

private List<String> list;

· <property name="list">

· <list>

· <value>第一個(gè)</value>  

· <value>第二個(gè)</value>  

· <value>第三個(gè)</value>  

· </list>

· </property>

properties:

private Properties properties;

· <property name="properties">

· <props>

· <prop key="key1">value1</prop>

· <prop key="key2">value2</prop>

· <prop key="key3">value3</prop>

· </props>

· </property>

map:
peivate Map<String,String> map;

· <property name="map">

· <map>

· <entry key="key1" value="value1"></entry>

· <entry key="key2" value="value2"></entry>

· <entry key="key3" value="value3"></entry>

· </map>

· </property>

1.1.10 使用構(gòu)造器裝配屬性

PersonServiceBean.java

?  package service.impl;  
?    
?  import java.util.Set;  
?    
?  import dao.PersonDao;  
?  import service.PersonService;  
?    
?    
?  public class PersonServiceBean implements PersonService {  
?      private PersonDao personDao;  
?      private String name;  
?        
?      public PersonServiceBean(PersonDao personDao, String name) {  
?          this.personDao = personDao;  
?          this.name = name;  
?      }  
?      @Override  
?      public String getName() {  
?          // TODO Auto-generated method stub  
?          return this.name;  
?      }  
?      @Override  
?      public PersonDao getPersonDao() {  
?          // TODO Auto-generated method stub  
?          return this.personDao;  
?      }  
?      @Override  
?      public void save() {  
?          // TODO Auto-generated method stub  
?          System.out.println(this.getClass().getName()+" save方法");  
?      }  
?      public void setPersonDao(PersonDao personDao) {  
?          this.personDao = personDao;  
?      }  
?      public void setName(String name) {  
?          this.name = name;  
?      }  
?        
?  }  

beans.xml

?  <bean id="personService" class="service.impl.PersonServiceBean">  
?            
?          <constructor-arg index="0" ref="personDao"></constructor-arg>  
?          <constructor-arg index="1" value="pf"></constructor-arg>  
?            
?              </bean>  

index指明了參數(shù)的位置,加了index就不用加type來說明了烂翰。


1.1.11 用@Resource注解完成屬性裝配

java代碼注入配置夯缺,需要spring解壓文件夾下lib/j2ee/common-annotation.jar這個(gè)庫文件,添加玩以后甘耿,修改beans.xml

?  <?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:context="http://www.springframework.org/schema/context"  
?         xsi:schemaLocation="http://www.springframework.org/schema/beans  
?             http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
?             http://www.springframework.org/schema/context  
?             http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
?    
?      <context:annotation-config/>  
?               </beans>   

由于現(xiàn)在家里上spring的網(wǎng)站總是上不去踊兜,如果要出現(xiàn)標(biāo)簽提示,那么和前面一樣在本地添加spring-context-2.5.xsd

現(xiàn)在配置工作完成了佳恬,現(xiàn)在開始用java代碼來完成注入

@Autowired方式:默認(rèn)按類型裝配捏境,默認(rèn)情況下它要求以來對象必須存在,如果允許null值毁葱,可以設(shè)置它required屬性為false垫言。如果我們想使用按名稱裝配,可以結(jié)合@Qualifier注解一起使用

@Autowired @Qualifier("personDaoBean")

private PersonDao personDao;

@Resource方式:默認(rèn)按名稱裝配倾剿,名稱可以通過@Resource的name屬性指定筷频,如果沒有指定的name屬性,當(dāng)注解標(biāo)注在字段上,即默認(rèn)取字段的名稱作為bean名稱尋找以來對象截驮,當(dāng)注解標(biāo)注在屬性的setter方法上,即迷人取屬性名作為bean名稱尋找以來對象际度。

@Resource(name="personDaoBean")

private PersonDao personDao;

推薦使用@Resource方式葵袭,因?yàn)锧Resource是j2ee里的一個(gè)注解,而@AutoWired是spring里的注解乖菱,使用@Resource可以降低與框架的耦合度坡锡。

beans.xml

?  <?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:context="http://www.springframework.org/schema/context"  
?         xsi:schemaLocation="http://www.springframework.org/schema/beans  
?             http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
?             http://www.springframework.org/schema/context  
?             http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
?    
?      <context:annotation-config/>  
?            
?      <bean id="personDao" class="dao.impl.PersonDaoBean"/>  
?      <bean id="personService" class="service.impl.PersonServiceBean"/>  
?        
?  </beans>   

PersonServiceBean.java

?  package service.impl;  
?    
?  import javax.annotation.Resource;  
?    
?  import dao.PersonDao;  
?  import service.PersonService;  
?    
?    
?  public class PersonServiceBean implements PersonService {  
?      @Resource private PersonDao personDao;  
?      private String name;  
?        
?      public PersonServiceBean() {  
?    
?      }  
?      @Override  
?      public String getName() {  
?          // TODO Auto-generated method stub  
?          return this.name;  
?      }  
?      @Override  
?      public PersonDao getPersonDao() {  
?          // TODO Auto-generated method stub  
?          return this.personDao;  
?      }  
?      @Override  
?      public void save() {  
?          // TODO Auto-generated method stub  
?          System.out.println(this.getClass().getName()+" save方法");  
?      }  
?      public void setPersonDao(PersonDao personDao) {  
?          this.personDao = personDao;  
?      }  
?      public void setName(String name) {  
?          this.name = name;  
?      }  
?  }   

運(yùn)行發(fā)現(xiàn)注入成功,string類型就不需要用注解注入了窒所,直接賦值就可以了鹉勒。

另外吧@Resource放在setter方法上也是可以的,效果一樣吵取。

1.1.12 編碼剖析@Resource注解的實(shí)現(xiàn)原理

ItcastResource.java

?  package junit.test;  
?    
?  import java.lang.annotation.ElementType;  
?  import java.lang.annotation.Retention;  
?  import java.lang.annotation.RetentionPolicy;  
?  import java.lang.annotation.Target;  
?    
?  @Retention(RetentionPolicy.RUNTIME)  
?  @Target({ElementType.FIELD, ElementType.METHOD})  
?  public @interface ItcastResource {  
?      public String name() default "";  
?  }  

PropertyDefinition .java

?  package junit.test;  
?    
?  public class PropertyDefinition {  
?      private String name;  
?      private String ref;  
?      private String value;  
?        
?      public String getValue() {  
?          return value;  
?      }  
?    
?      public void setValue(String value) {  
?          this.value = value;  
?      }  
?    
?      public PropertyDefinition(String name, String ref, String value) {  
?          this.name = name;  
?          this.ref = ref;  
?          this.value = value;  
?      }  
?        
?      public String getName() {  
?          return name;  
?      }  
?      public void setName(String name) {  
?          this.name = name;  
?      }  
?      public String getRef() {  
?          return ref;  
?      }  
?      public void setRef(String ref) {  
?          this.ref = ref;  
?      }  
?        
?  }  

BeanDefinition.java

?  package junit.test;  
?    
?  import java.util.ArrayList;  
?  import java.util.List;  
?    
?  public class BeanDefinition {  
?      private String id;  
?      private String className;  
?      private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>();  
?        
?      public BeanDefinition(String id, String className) {  
?          this.id = id;  
?          this.className = className;  
?      }  
?      public String getId() {  
?          return id;  
?      }  
?      public void setId(String id) {  
?          this.id = id;  
?      }  
?      public String getClassName() {  
?          return className;  
?      }  
?      public void setClassName(String className) {  
?          this.className = className;  
?      }  
?      public List<PropertyDefinition> getPropertys() {  
?          return propertys;  
?      }  
?      public void setPropertys(List<PropertyDefinition> propertys) {  
?          this.propertys = propertys;  
?      }  
?  }  

ItcastClassPathXMLApplicationContext.java

?  package junit.test;  
?    
?  import java.beans.Introspector;  
?  import java.beans.PropertyDescriptor;  
?  import java.lang.reflect.Field;  
?  import java.lang.reflect.Method;  
?  import java.net.URL;  
?  import java.util.ArrayList;  
?  import java.util.HashMap;  
?  import java.util.List;  
?  import java.util.Map;  
?    
?  import org.apache.commons.beanutils.ConvertUtils;  
?  import org.dom4j.Document;  
?  import org.dom4j.Element;  
?  import org.dom4j.XPath;  
?  import org.dom4j.io.SAXReader;  

?  public class ItcastClassPathXMLApplicationContext {  
?      private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();  
?      private Map<String, Object> sigletons = new HashMap<String, Object>();  
?        
?      public ItcastClassPathXMLApplicationContext(String filename){  
?          this.readXML(filename);  
?          this.instanceBeans();  
?          this.annotationInject();  
?          this.injectObject();  
?      }  
?      /** 
?       * 通過注解實(shí)現(xiàn)注入依賴對象 
?       */  
?      private void annotationInject() {  
?          for(String beanName : sigletons.keySet()){  
?              Object bean = sigletons.get(beanName);  
?              if(bean!=null){  
?                  try {  
?                      PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();  
?                      for(PropertyDescriptor properdesc : ps){  
?                          Method setter = properdesc.getWriteMethod();//獲取屬性的setter方法  
?                          if(setter!=null && setter.isAnnotationPresent(ItcastResource.class)){  
?                              ItcastResource resource = setter.getAnnotation(ItcastResource.class);  
?                              Object value = null;  
?                              if(resource.name()!=null && !"".equals(resource.name())){  
?                                  value = sigletons.get(resource.name());  
?                              }else{  
?                                  value = sigletons.get(properdesc.getName());  
?                                  if(value==null){  
?                                      for(String key : sigletons.keySet()){  
?                                          if(properdesc.getPropertyType().isAssignableFrom(sigletons.get(key).getClass())){  
?                                              value = sigletons.get(key);  
?                                              break;  
?                                          }  
?                                      }  
?                                  }                                 
?                              }  
?                              setter.setAccessible(true);  
?                              setter.invoke(bean, value);//把引用對象注入到屬性  
?                          }  
?                      }  
?                      Field[] fields = bean.getClass().getDeclaredFields();  
?                      for(Field field : fields){  
?                          if(field.isAnnotationPresent(ItcastResource.class)){  
?                              ItcastResource resource = field.getAnnotation(ItcastResource.class);  
?                              Object value = null;  
?                              if(resource.name()!=null && !"".equals(resource.name())){  
?                                  value = sigletons.get(resource.name());  
?                              }else{  
?                                  value = sigletons.get(field.getName());  
?                                  if(value==null){  
?                                      for(String key : sigletons.keySet()){  
?                                          if(field.getType().isAssignableFrom(sigletons.get(key).getClass())){  
?                                              value = sigletons.get(key);  
?                                              break;  
?                                          }  
?                                      }  
?                                  }                                 
?                              }  
?                              field.setAccessible(true);//允許訪問private字段  
?                              field.set(bean, value);  
?                          }  
?                      }  
?                  } catch (Exception e) {  
?                      e.printStackTrace();  
?                  }  
?              }  
?          }  
?      }  
?    
?      /** 
?       * 為bean對象的屬性注入值 
?       */  
?      private void injectObject() {  
?          for(BeanDefinition beanDefinition : beanDefines){  
?              Object bean = sigletons.get(beanDefinition.getId());  
?              if(bean!=null){  
?                  try {  
?                      PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();  
?                      for(PropertyDefinition propertyDefinition : beanDefinition.getPropertys()){  
?                          for(PropertyDescriptor properdesc : ps){  
?                              if(propertyDefinition.getName().equals(properdesc.getName())){  
?                                  Method setter = properdesc.getWriteMethod();//獲取屬性的setter方法 ,private  
?                                  if(setter!=null){  
?                                      Object value = null;  
?                                      if(propertyDefinition.getRef()!=null && !"".equals(propertyDefinition.getRef().trim())){  
?                                          value = sigletons.get(propertyDefinition.getRef());  
?                                      }else{  
?                                          value = ConvertUtils.convert(propertyDefinition.getValue(), properdesc.getPropertyType());  
?                                      }  
?                                      setter.setAccessible(true);  
?                                      setter.invoke(bean, value);//把引用對象注入到屬性  
?                                  }  
?                                  break;  
?                              }  
?                          }  
?                      }  
?                  } catch (Exception e) {  
?                  }  
?              }  
?          }  
?      }  
?      /** 
?       * 完成bean的實(shí)例化 
?       */  
?      private void instanceBeans() {  
?          for(BeanDefinition beanDefinition : beanDefines){  
?              try {  
?                  if(beanDefinition.getClassName()!=null && !"".equals(beanDefinition.getClassName().trim()))  
?                      sigletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());  
?              } catch (Exception e) {  
?                  e.printStackTrace();  
?              }  
?          }  
?            
?      }  
?      /** 
?       * 讀取xml配置文件 
?       * @param filename 
?       */  
?      private void readXML(String filename) {  
?             SAXReader saxReader = new SAXReader();     
?              Document document=null;     
?              try{  
?               URL xmlpath = this.getClass().getClassLoader().getResource(filename);  
?               document = saxReader.read(xmlpath);  
?               Map<String,String> nsMap = new HashMap<String,String>();  
?               nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空間  
?               XPath xsub = document.createXPath("http://ns:beans/ns:bean");//創(chuàng)建beans/bean查詢路徑  
?               xsub.setNamespaceURIs(nsMap);//設(shè)置命名空間  
?               List<Element> beans = xsub.selectNodes(document);//獲取文檔下所有bean節(jié)點(diǎn)   
?               for(Element element: beans){  
?                  String id = element.attributeValue("id");//獲取id屬性值  
?                  String clazz = element.attributeValue("class"); //獲取class屬性值          
?                  BeanDefinition beanDefine = new BeanDefinition(id, clazz);  
?                  XPath propertysub =  element.createXPath("ns:property");  
?                  propertysub.setNamespaceURIs(nsMap);//設(shè)置命名空間  
?                  List<Element> propertys = propertysub.selectNodes(element);  
?                  for(Element property : propertys){                    
?                      String propertyName = property.attributeValue("name");  
?                      String propertyref = property.attributeValue("ref");  
?                      String propertyValue = property.attributeValue("value");  
?                      PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName, propertyref, propertyValue);  
?                      beanDefine.getPropertys().add(propertyDefinition);  
?                  }  
?                  beanDefines.add(beanDefine);  
?               }   
?              }catch(Exception e){     
?                  e.printStackTrace();  
?              }  
?      }  
?      /** 
?       * 獲取bean實(shí)例 
?       * @param beanName 
?       * @return 
?       */  
?      public Object getBean(String beanName){  
?          return this.sigletons.get(beanName);  
?      }  
?  }  

實(shí)際上也就是通過了反射技術(shù)來構(gòu)造對象并且賦值禽额,只是用到了注解的方法,并且利用@interface來構(gòu)造自定義的注解類型皮官。

1.1.13 @Autowire注解與自動裝配

使用了@Autowired的注解方式脯倒,這種默認(rèn)按類型查找符合的bean注入
@Autowired **private** PersonDao personDao;
使用@Qualifier注明bean名稱注入
@Autowired @Qualifier("personDao") **private** PersonDao personDao;

還可以添加required屬性,在沒找到bean的情況下捺氢,如果required為false藻丢,則注入null,required為true摄乒,則報(bào)錯(cuò)悠反。
@Autowired(required=true) @Qualifier("personDao") private PersonDao personDao;
自動裝配:

通過bean標(biāo)簽的autowire屬性來配置,有5種值

no 不使用自動裝配馍佑,必須通過ref元素指定依賴斋否,默認(rèn)設(shè)置。

byName 根據(jù)屬性名自動裝配挤茄。此選項(xiàng)將檢查容器并根據(jù)名字查找與

屬性完全一致的bean如叼,并將其與屬性自動裝配。

byType 如果容器中存在一個(gè)與指定屬性類型相同的bean穷劈,那么將與

該屬性自動裝配笼恰;如果存在多個(gè)該類型bean,那么拋出異

常歇终,并指出不能使用byType方式進(jìn)行自動裝配社证;如果沒有找

到相匹配的bean,則什么事都不發(fā)生评凝,也可以通過設(shè)置

dependency-check="objects"讓Spring拋出異常追葡。

constructor 與byType方式類似,不同之處在于它應(yīng)用于構(gòu)造器參數(shù)。如

果容器中沒有找到與構(gòu)造器參數(shù)類型一致的bean宜肉,那么拋出

異常匀钧。

autodetect 通過bean類的自省機(jī)制(introspection)來決定是使用

constructor還是byType方式進(jìn)行自動裝配。如果發(fā)現(xiàn)默認(rèn)的

構(gòu)造器谬返,那么將使用byType方式之斯。


1.1.14 讓Spring自動掃描和管理Bean

讓Spring自動掃描和管理Bean

<context:component-scan base-package="cn.test"></context:component-scan>      

其中base-package為需要掃描的包(含子包)

@Service用于標(biāo)注業(yè)務(wù)層組件,@Controller用于標(biāo)注控制層組件(如struts中的action),@Repository用于標(biāo)注數(shù)據(jù)訪問組件遣铝,即DAO組件佑刷,而@Component泛指組件,當(dāng)組件不好歸類的時(shí)候酿炸,我們可以使用這個(gè)注解進(jìn)行標(biāo)注瘫絮。

bean的默認(rèn)名稱是類名,然后把第一個(gè)字母改為小寫填硕÷笥可以通過@Service("xxx")修改bean名稱。

這種bean默認(rèn)是單例的廷支,如果想改變频鉴,可以使用@Service(“aaaaa”) @Scope(“prototype”)來改變。

還可以通過@PostConstruct @PreDestroy設(shè)置初始化和銷毀的函數(shù)

?  @PostConstruct  
?   public void init(){  
?    System.out.println("初始化");  
?   }  
?    
?    
?    
?  @PreDestroy  
?   public void destory(){  
?    System.out.println("開閉資源");  
?   }   

1.1.15 使用JDK中的Proxy技術(shù)實(shí)現(xiàn)AOP功能

通過代理對象來調(diào)用對象的方法恋拍,從而做出權(quán)限控制垛孔。

目標(biāo)對象必須實(shí)現(xiàn)接口才能使用proxy技術(shù)創(chuàng)建代理對象。

PersonService.java

?  package cn.pf.aop.service;  
?    
?  public interface PersonService {  
?      public void save(String name);  
?      public void update(String name, Integer personId);  
?      public String getName(Integer personId);  
?  }

PersonServiceBean.java

?  package cn.pf.aop.service.impl;  
?    
?  import cn.pf.aop.service.PersonService;  
?    
?  public class PersonServiceBean implements PersonService {  
?      private String user = null;  
?        
?      public PersonServiceBean() {  
?    
?      }  
?    
?      public PersonServiceBean(String user) {  
?          this.setUser(user);  
?      }  
?    
?      @Override  
?      public String getName(Integer personId) {  
?          System.out.println(this.getClass().getName()+" getName方法");  
?          return "pf";  
?      }  
?    
?      @Override  
?      public void save(String name) {  
?          System.out.println(this.getClass().getName()+" save方法");  
?      }  
?    
?      @Override  
?      public void update(String name, Integer personId) {  
?          System.out.println(this.getClass().getName()+" update方法");  
?      }  
?    
?      public void setUser(String user) {  
?          this.user = user;  
?      }  
?    
?      public String getUser() {  
?          return user;  
?      }  
?    
?  }  

JDKProxyFactory.java

?  package cn.pf.aop;  
?    
?  import java.lang.reflect.InvocationHandler;  
?  import java.lang.reflect.Method;  
?  import java.lang.reflect.Proxy;  
?    
?  import cn.pf.aop.service.impl.PersonServiceBean;  
?    
?  public class JDKProxyFactory implements InvocationHandler {  
?      private Object targetObject;  
?        
?      public Object createProxyIntance(Object targetObject){  
?          this.targetObject = targetObject;  
?          return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),   
?                  this.targetObject.getClass().getInterfaces(), this);  
?      }  
?    
?      @Override  
?      public Object invoke(Object proxy, Method method, Object[] arg2)  
?              throws Throwable {  
?          PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;  
?          Object result = null;  
?          if(personServiceBean.getUser() != null){  
?              result = method.invoke(targetObject, arg2);  
?          }  
?          return null;  
?      }  
?  }  

AOPTest.java

?  package junit.test;  
?    
?  import org.junit.Test;  
?    
?  import cn.pf.aop.JDKProxyFactory;  
?  import cn.pf.aop.service.PersonService;  
?  import cn.pf.aop.service.impl.PersonServiceBean;  
?    
?    
?  public class AOPTest {  
?      @Test public void proxyTest(){  
?          JDKProxyFactory factory = new JDKProxyFactory();  
?          PersonService personService = (PersonService) factory.createProxyIntance(new PersonServiceBean());  
?          personService.save("111");  
?      }  
?  }   

Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this);

創(chuàng)建代理對象的時(shí)候施敢,加入了該目標(biāo)對象所有的接口周荐,即對所有的方法進(jìn)行監(jiān)聽,任何一個(gè)方法的調(diào)用都會觸發(fā)代理對象的invoke方法僵娃。this表示觸發(fā)哪個(gè)代理對象的invoke方法概作,這里我們設(shè)置當(dāng)前代理對象。

調(diào)用personService的save方法的時(shí)候默怨,可以理解為讯榕,save方法被監(jiān)聽,進(jìn)入代理對象的invoke方法匙睹,如果user愚屁!=null,則invoke方法中調(diào)用了personService的save方法痕檬,如果user==null霎槐,則什么也不做。

通過反射技術(shù)調(diào)用方法其實(shí)可以簡單的理解為

xxx.invoke(obj,args)返回的結(jié)果是obj.xxx(args)


1.1.16 使用CGLIB實(shí)現(xiàn)AOP功能與AOP概念解釋

前面的proxy技術(shù)必須在類實(shí)現(xiàn)了接口的前提下才可以實(shí)現(xiàn)權(quán)限的控制梦谜,cglb可以在類不實(shí)現(xiàn)接口的情況下完成丘跌。

在spring文件夾下lib/cglib下找到cglib的jar庫文件袭景,加入工程。

CGlibProxyFactory.java

?  package cn.pf.aop;  
?    
?  import java.lang.reflect.Method;  
?    
?  import cn.pf.aop.service.impl.PersonServiceBean;  
?    
?  import net.sf.cglib.proxy.Enhancer;  
?  import net.sf.cglib.proxy.MethodInterceptor;  
?  import net.sf.cglib.proxy.MethodProxy;  
?    
?  public class CGlibProxyFactory implements MethodInterceptor{  
?      private Object targetObject;  
?        
?      public Object createProxyIntance(Object targetObject){  
?          this.targetObject = targetObject;  
?          Enhancer enhancer = new Enhancer();  
?          enhancer.setSuperclass(this.targetObject.getClass());  
?          enhancer.setCallback(this);  
?          return enhancer.create();  
?      }  
?    
?      @Override  
?      public Object intercept(Object proxy, Method method, Object[] arg2,  
?              MethodProxy arg3) throws Throwable {  
?          PersonServiceBean personServiceBean = (PersonServiceBean)targetObject;  
?          Object result = null;  
?          if(personServiceBean.getUser() != null){  
?              result = method.invoke(targetObject, arg2);  
?          }  
?          return result;  
?      }  
?  }   

AOPTest.java

?  @Test public void proxyTest2(){  
?          CGlibProxyFactory factory = new CGlibProxyFactory();  
?          PersonServiceBean personServiceBean = (PersonServiceBean) factory.createProxyIntance(new PersonServiceBean("1"));  
?          personServiceBean.save("111");  
?      }   

CGlib的enhance繼承了目標(biāo)類所有非final方法闭树,對這些方法進(jìn)行覆蓋耸棒。創(chuàng)建的代理對象是目標(biāo)對象的子類


1.1.17 使用Spring的注解方式實(shí)現(xiàn)AOP入門

首先添加包
``
/spring.jar

/lib/aspectj/aspectjrt.jar

/lib/aspectj/aspectjweaver.jar

/lib/j2ee/common-annotations.jar

/lib/jakarta-commons/common_logging.jar

/lib/cglib/cglib-nodep-2.1-3.jar
``
beans.xml

?  <?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:aop="http://www.springframework.org/schema/aop"  
?         xsi:schemaLocation="http://www.springframework.org/schema/beans  
?             http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
?             http://www.springframework.org/schema/aop  
?             http://www.springframework.org/schema/context/spring-aop-2.5.xsd">  
?    
?          <aop:aspectj-autoproxy/>  
?    </beans>   

PersonService.java和PersonServiceBean.java和上篇一樣

MyInterceptor.java

@Ascept聲明了切面,即進(jìn)行攔截的類报辱。

@Pointcut聲明了切入點(diǎn)榆纽,即進(jìn)行攔截的方法。

@Pointcut("execution(* cn.itcast.service...(..))")

  • 代表返回值類型

cn.pf.service 需要攔截的包名

.. 代表隊(duì)子包的類進(jìn)行攔截

  • 代表進(jìn)行攔截的類

  • 代表進(jìn)行攔截的方法

(..) 代表方法的參數(shù)隨意

(*代表任意)

下面來測試前置通知捏肢,后置通知,最終通知饥侵,例外通知以及環(huán)繞通知鸵赫。

MyInterceptor.java

?  package cn.pf.aop.service;  
?    
?  import org.aspectj.lang.ProceedingJoinPoint;  
?  import org.aspectj.lang.annotation.After;  
?  import org.aspectj.lang.annotation.AfterReturning;  
?  import org.aspectj.lang.annotation.AfterThrowing;  
?  import org.aspectj.lang.annotation.Around;  
?  import org.aspectj.lang.annotation.Aspect;  
?  import org.aspectj.lang.annotation.Before;  
?  import org.aspectj.lang.annotation.Pointcut;  
?    
?  @Aspect   
?  public class MyInterceptor {  
?      @Pointcut("execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))")  
?      private void anyMethod(){}  
?        
?      @Before("anyMethod()")  
?      public void doAccessCheck(){  
?          System.out.println("前置通知");  
?      }  
?        
?      @AfterReturning("anyMethod()")  
?      public void doAfterReturning(){  
?          System.out.println("后置通知");  
?      }  
?        
?      @After("anyMethod()")  
?      public void doAfter(){  
?          System.out.println("最終通知");  
?      }  
?        
?      @AfterThrowing("anyMethod()")  
?      public void doAfterThrowing(){  
?          System.out.println("例外通知");  
?      }  
?        
?      @Around("anyMethod()")  
?      public Object doBasicProfiling(ProceedingJoinPoint pjp)throws Throwable{  
?          System.out.println("進(jìn)入環(huán)繞方法");  
?          Object result = pjp.proceed();  
?          System.out.println("退出環(huán)繞方法");  
?          return result;  
?      }  
?  }   

SpringAOPTest.java

?  import org.junit.Test;  
?  import org.springframework.context.ApplicationContext;  
?  import org.springframework.context.support.ClassPathXmlApplicationContext;  
?    
?  import cn.pf.aop.service.PersonService;  
?    
?    
?    
?  public class SpringAOPTest {  
?      @Test public void interceptorTest(){  
?          ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");  
?          PersonService personService = (PersonService)context.getBean("personService");  
?          personService.save("1");  
?      }  
?  }   

控制臺輸出:

前置通知

進(jìn)入環(huán)繞方法

cn.pf.aop.service.impl.PersonServiceBean save方法

后置通知

最終通知

退出環(huán)繞方法

那么如何獲得輸入?yún)?shù),返回值躏升,異常呢辩棒,那么稍作修改

?  @Before("anyMethod() && args(name)")  
?      public void doAccessCheck(String name) {  
?          System.out.println("前置通知:"+ name);  
?      }  
?      @AfterReturning(pointcut="anyMethod()",returning="result")  
?      public void doAfterReturning(String result) {  
?          System.out.println("后置通知:"+ result);  
?      }  
?      @After("anyMethod()")  
?      public void doAfter() {  
?          System.out.println("最終通知");  
?      }  
?      @AfterThrowing(pointcut="anyMethod()",throwing="e")  
?      public void doAfterThrowing(Exception e) {  
?          System.out.println("例外通知:"+ e);  
?      }  

其實(shí)切面就感覺像servlet里面的過濾器,在方法的前后加上一些關(guān)卡膨疏,進(jìn)行篩選一睁,判定權(quán)限,通過指定好的一些切面后佃却,才可以真正調(diào)用目標(biāo)對象的方法者吁。


1.1.18 基于XML配置方式聲明切面

基于XML配置方式聲明切面

與注釋方法沒什么太大的區(qū)別

?  <bean id=”orderservice” class=”cn.service.OrderServiceBean” />  
?    
?  <bean id=”log” class=”cn.service.LogPrint” />  
?    
?  <aop:config>  
?    
?         <aop:aspect id=”myaop” ref=”log”>  
?    
?         <aop:pointcut id=”mycut” expression=”execution(* cn.service..*.*(..))”/>  
?    
?         <aop:before pointcut-ref=”mycut” method=”doAccessCheck” />  
?    
?         <aop:after-returning pointcut-ref=”mycut” method=”doReturnCheck” />  
?    
?         <aop:after-throwing pointcut-ref=”mycut” method=”doExceptionAction” />  
?    
?         <aop:after pointcut-ref=”mycut” method=”doReleaseAction” />  
?    
?         <aop:around pointcut-ref=”mycut” method=”doBasicProfiling” />  
?    
?  </aop:aspect>  
?    
?  </aop:config>  


1.1.19 aspectj的切入點(diǎn)語法定義細(xì)節(jié)

execution(* cn.pf.aop.service.impl.PersonServiceBean.*(..))所有非final方法

execution(!void cn.pf.aop.service.impl.PersonServiceBean.*(..))非void非final方法

execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(..))非final且返回類型為String的方法

execution(java.lang.String cn.pf.aop.service.impl.PersonServiceBean.*(java.lang.String,..))第一個(gè)參數(shù)為String的非final方法

execution(* cn.pf.aop.service.impl..*.*(..))對包下所有類進(jìn)行攔截


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市饲帅,隨后出現(xiàn)的幾起案子复凳,更是在濱河造成了極大的恐慌,老刑警劉巖灶泵,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件育八,死亡現(xiàn)場離奇詭異,居然都是意外死亡赦邻,警方通過查閱死者的電腦和手機(jī)髓棋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惶洲,“玉大人按声,你說我怎么就攤上這事∨热担” “怎么了儒喊?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長币呵。 經(jīng)常有香客問我怀愧,道長侨颈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任芯义,我火速辦了婚禮哈垢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扛拨。我一直安慰自己耘分,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布绑警。 她就那樣靜靜地躺著求泰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪计盒。 梳的紋絲不亂的頭發(fā)上渴频,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音北启,去河邊找鬼卜朗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛咕村,可吹牛的內(nèi)容都是我干的场钉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼懈涛,長吁一口氣:“原來是場噩夢啊……” “哼逛万!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起批钠,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泣港,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后价匠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體当纱,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年踩窖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坡氯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洋腮,死狀恐怖箫柳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啥供,我是刑警寧澤悯恍,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站伙狐,受9級特大地震影響涮毫,放射性物質(zhì)發(fā)生泄漏瞬欧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一罢防、第九天 我趴在偏房一處隱蔽的房頂上張望艘虎。 院中可真熱鬧,春花似錦咒吐、人聲如沸野建。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽候生。三九已至,卻和暖如春绽昼,著一層夾襖步出監(jiān)牢的瞬間陶舞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工绪励, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唠粥。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓疏魏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親晤愧。 傳聞我的和親對象是個(gè)殘疾皇子大莫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

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

  • 1.1 spring IoC容器和beans的簡介 Spring 框架的最核心基礎(chǔ)的功能是IoC(控制反轉(zhuǎn))容器,...
    simoscode閱讀 6,714評論 2 22
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理官份,服務(wù)發(fā)現(xiàn)只厘,斷路器,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 1.1 Spring IoC容器和bean簡介 本章介紹了Spring Framework實(shí)現(xiàn)的控制反轉(zhuǎn)(IoC)...
    起名真是難閱讀 2,583評論 0 8
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,811評論 6 342
  • 昨天業(yè)績完成99%也就再進(jìn)來個(gè)3000元就完成100%,想著今天可以完成舅巷,當(dāng)時(shí)想的時(shí)候羔味,就冒出來個(gè)念頭,明天完成不...
    艷敏姐閱讀 232評論 2 3