1. 程式人生 > >Java匹馬行天下之J2EE框架開發——Spring—>用IDEA開發Spring程式(01)

Java匹馬行天下之J2EE框架開發——Spring—>用IDEA開發Spring程式(01)

一、心動不如行動

一、建立專案

*注:在IDEA中我建立的Maven專案,不瞭解Maven的朋友可以看我之前的部落格“我們一起走進Maven——知己知彼”,瞭解Maven後可以看我之前的部落格“Maven的安裝與配置”,自行安裝,行動起來吧。

 

二、載入依賴

 在pom.xml檔案中新增Spring依賴和日誌相關依賴

<dependencies>
  <!--測試相關-->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
  <!--Spring核心基礎依賴-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <!--日誌相關-->
  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
  </dependency>
</dependencies>

  

三、專案結構

在main目錄下面建立java和resources目錄

         

    

 

       

 

 

四、基礎程式碼實現(練手)

    • 建立TestDao介面和實現類
    • 獲得TestDao實現類的例項
      • 在之前開發中,我們是直接new一個物件即可。即:`private TestDao dao = new TestDaoImpl();`
      • 在學習Spring之後,將由Spring來建立物件的例項 --> 即:`IoC 控制反轉(Inverse of Control)`
        之後需要例項物件時,從Spring工廠(容器)中獲得即可,需要將實現類的全限定名稱配置到xml檔案中。

建立dao包,在dao包下建立TestDao介面和TestDao介面的實現類,結構如下圖:

TestDao介面程式碼示例:

package dao;

public interface TestDao {
    public void sayHello();
}

TestDaoImpl實現類程式碼示例:

package dao;

public class TestDaoImpl implements TestDao{
    @Override
    public void sayHello() {
        System.out.println("Hello,Spring!");
    }
}

  

在resources資源目錄點選右鍵,依次選擇New-->XML Configuration File-->Spring Config,建立applicationContext.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.xsd">

    <!--把testDao物件的建立權交給Spring-->
    <!-- <bean> 配置需要建立的物件
id :用於之後從Spring容器中獲得例項時使用的
class :需要建立例項的全限定類名-->
<bean id="testDao" class="dao.TestDaoImpl"></bean> </beans>

建立test包,在test包下建立測試類SpringTest  

 

 

package test;
import dao.TestDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
@Test
public void demo1(){
// 之前開發,自己手寫new出物件
TestDao dao= new TestDaoImpl();
dao.sayHello();
}

@Test
public void demo2() {
// 現在從spring容器中獲得物件例項
// 1 、獲得容器
//初始化Spring容器ApplicationContext,載入配置檔案
ApplicationContext application = new ClassPathXmlApplicationContext("applicationContext.xml");
// 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得
//通過容器獲取testDao例項
TestDao testDao = (TestDao) application.getBean("testDao");
testDao.sayHello();
}
}

  

  • IoC(Inverse of Control)反轉控制的概念,就是將原本在程式中手動建立TestDaoImpl物件的控制權,交由Spring框架管理。
  • 簡單說,就是建立TestDaoImpl物件的控制權被反轉到了Spring框架。


點選測試方法左側的執行按鈕,選擇Run,測試程式碼

 

 

執行後控制檯顯示結果   

 

 

專案執行成功!!!!!!!

   

五、Spring入門案例:DI(掌握)   

  • DI :Dependency Injection :依賴注入
  • is a :是一個,繼承。
  • has a:有一個,成員變數,依賴。
 class B {
       private A a;   // B類依賴A類,B類使用A類。
    }

    依賴:一個物件需要使用另一個物件。
    注入:通過setter方法進行另一個物件例項設定。

  例如:

class BookServiceImpl {
        // 之前開發:介面 = 實現類(service和dao耦合了,寫死了,知道具體的實現類是誰,那麼我的具體實現類變化,那麼這行程式碼也得跟著變)
        // private BookDao bookDao = new BookDaoImpl();

         // spring之後(解耦:service實現類使用了dao的介面,這樣就不知道具體的實現類是誰了)
        private BookDao bookDao;
        setter方法
   }

模擬spring執行過程
    建立service例項:BookService bookService = new BookServiceImpl();   => IoC <bean>
    建立dao例項:BookDao bookDao = new BookDaoImple();                  => IoC
    將dao設定給service:bookService.setBookDao(bookDao);                => DI <property>

  

具體程式碼實現:

實現步驟:

  • 建立BookDao介面和實現類
  • 建立BookService介面和實現類
  • 將dao和service配置到 applicationContext.xml檔案中
  • 使用api測試

專案結構:

 載入依賴:

 在pom.xml檔案中新增Spring依賴和日誌相關依賴

 

<dependencies>
  <!--測試相關-->
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
  </dependency>
  <!--Spring核心基礎依賴-->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.0.2.RELEASE</version>
  </dependency>
  <!--日誌相關-->
  <dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
  </dependency>
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
  </dependency>
</dependencies>

 

  

 

 

BookDao介面和實現類

 

package dao;

public interface BookDao {
    void save();
}

 

  

package dao;

public class BookDaoImpl implements BookDao {
    @Override
    public void save() {
        System.out.println("實現新增功能");
    }
}

  

BookService介面和實現類

package Service;

public interface BookService {
    void addBook();
}

  

package Service;

import dao.BookDao;
import dao.BookDaoImpl;

public class BookServiceImpl implements BookService {

    //方式一:之前,介面=實現類
    //private BookDao bookDao= new BookDaoImpl();

    //方式二:現在,介面+setter方法
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao){
        this.bookDao=bookDao;
    }

    @Override
    public void addBook() {
    this.bookDao.save();

    }
}

  

 

將dao和service配置到 applicationContext.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.xsd">

    <!-- <bean> 配置需要建立的物件
            id :用於之後從Spring容器中獲得例項時使用的
            class :需要建立例項的全限定類名-->
    <!--
   模擬spring執行過程
       建立service例項:BookService bookService = new BookServiceImpl();   => IoC <bean>
       建立dao例項:BookDao bookDao = new BookDaoImple();                  => IoC
       將dao例項設定給service例項:bookService.setBookDao(bookDao);         => DI <property>

       <property> 用於進行屬性注入
           name : Bean的屬性名稱,通過setter方法獲得
               setBookDao  =>  BookDao =>  bookDao
           ref :另一個Bean的id值的引用
   -->
    <!-- 建立service例項 -->
    <bean id="bookServiceId" class="Service.BookServiceImpl">
        <!-- 將dao例項設定給service例項 -->
        <property name="bookDao" ref="bookDaoId"></property> <!-- 用於進行屬性注入 -->
    </bean>
    <!-- 建立dao例項 -->
    <bean id="bookDaoId" class="dao.BookDaoImpl"></bean>
</beans>

  

 

 

使用api測試

建立test包,在test包下建立測試類SpringTest

package test;

import Service.BookService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    @Test
    public void Test1(){
        // 之前開發,自己手寫new出物件
        // BookService bookService = new BookServiceImpl();
        // bookService.addBook();
    }

    @Test
    public void Test2(){
        // 現在從spring容器中獲得物件例項
        // 1 、獲得容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得
        BookService bookService = (BookService) applicationContext.getBean("bookServiceId");

        bookService.addBook();
    }
}

  

  • DI:Dependency Injection 依賴注入,在Spring框架負責建立Bean物件時,動態的將依賴物件注入到Bean元件。
  • getBean("bookServiceId"); 從spring容器中獲得指定名稱物件的例項時,會先判斷本例項物件是否需要使用其他例項化物件,由於設定了< property name="bookDao" ref="bookDaoId">< /property>,說明需要使用其他例項化物件,所以就根據其他Bean的id值的引用,去spring容器中獲得指定名稱物件的例項,相當於將dao例項設定給service例項。

執行後控制檯顯示結果

 

 這就成功了,開心一下,通過這兩個案例,多Spring有了初步的理解,加油!

 

六、Spring的核心API(瞭解)

 

api整體瞭解即可,之後不使用,在學習過程需要。

 

 

  • BeanFactory :這是一個工廠,用於生成任意Bean。
    採取延遲載入,第一次呼叫getBean(); 時才會初始化Bean。(即例項化物件)
  • ApplicationContext :是BeanFactory的子介面,功能更強大。(國際化處理、事件傳遞、Bean自動裝配、各種不同應用層的Context實現)。 採取非延時載入,當配置檔案被載入時,就進行物件的例項化。
    • ClassPathXmlApplicationContext 用於載入classpath(類路徑/src)下的xml
      • 載入xml執行時位置 --> /WEB-INF/classes/xxx.xml
    • FileSystemXmlApplicationContext 用於載入指定碟符下的xml
      • 載入xml執行時位置 --> /WEB-INF/xxx.xml
      • 通過java web學習過的 ServletContext.getRealPath(); 獲得具體碟符

示例程式碼如下:

package test;

import Service.BookService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;

public class SpringTest {
    @Test
    public void Test1(){
        // 之前開發,自己手寫new出物件
        // BookService bookService = new BookServiceImpl();
        // bookService.addBook();
    }

    @Test
    public void Test2(){
        // 現在從spring容器中獲得物件例項
        // 1 、獲得容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 採取非延時載入,當配置檔案被載入時,就進行物件的例項化。
        // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得
        BookService bookService = (BookService) applicationContext.getBean("bookServiceId");

        bookService.addBook();
    }

    @Test
    public void demo3() {
        // 現在從spring容器中獲得物件例項,使用的是BeanFactory,裡面需要一個Resource,該Resource又是一個介面,需要找它的實現類ClassPathResource
        // 1 、獲得容器
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得
        BookService  bookService = (BookService) beanFactory.getBean("bookServiceId"); // 採取延遲載入,第一次呼叫getBean(); 時才會初始化Bean(即例項化物件)。

        bookService.addBook();
    }
}

  

七、裝配Bean:基於XML

 

3種bean例項化方式:

  • 使用`預設構造方法`例項化
  • 使用`靜態工廠方法`例項化
  • 使用`例項工廠方法`例項化

使用預設構造方法例項化

格式:

<bean id="從Spring容器中獲得例項時使用的" class="需要建立例項的全限定類名"></bean>
例如:<bean id="userServiceId" class="Service.UserServiceImpl"></bean>

  

示例中用到的 UserService.java 和 UserServiceImpl.java 程式碼同上面例項中的程式碼,這裡不再贅述!
在spring容器中進行配置:

applicationContext.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.xsd">

 <!--第一種例項化Bean 的方式 :使用預設構造方法例項化,即要例項化的Bean必須要提供預設構造方法  -->
    <bean id="userServiceId" class="Service.UserServiceImpl"></bean>
</beans>

  測試程式碼: 

public class TestIoC {
    @Test
    public void demo01() {
        // 之前開發,自己手寫new出物件
        UserService userService = new UserServiceImpl(); // 直接手動建立例項

        userService.addUser();
    }

    @Test
    public void demo02() {
        // 現在從spring容器中獲得物件例項
        // 1 、獲得容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2、獲得內容 ,注意此時不需要自己new出物件了,都是從spring容器中獲得
        UserService userService = (UserService) applicationContext.getBean("userServiceId");

        userService.addUser();
    }
}

  

使用`靜態工廠方法`例項化

  • 靜態工廠:常用與spring整合其他框架(工具)時。
  • 靜態工廠:用於生成例項物件,所有的方法必須是static。

示例中用到的 UserService.java 和 UserServiceImpl.java 程式碼同上面例項中的程式碼,這裡不再贅述!
格式:

<bean id=""  class="工廠全限定類名"  factory-method="靜態方法名稱">

  

在spring容器中進行配置:

applicationContext.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.xsd">

 <!--第二種例項化Bean 的方式 :使用靜態工廠方法例項化
        將自定義的靜態工廠建立的例項交給spring管理 
        class 自定義靜態工廠全限定類名
        factory-method 靜態方法名
    -->
    <bean id="userServiceId" class="Service.MyBeanFactory" factory-method="createService"></bean> </beans>

  

 靜態工廠類程式碼:
public class MyBeanFactory {

    /**
     * 建立例項的靜態工廠,所有的方法必須是靜態的(static)。
     * 
     * @return
     */
    public static UserService createService() {
        return new UserServiceImpl();
    }

    // 還有建立其他例項的靜態工廠
    // ......
}

  測試程式碼:
TestStaticFactory.java

 
/**
 * 第二種例項化Bean 的方式 :使用靜態工廠方法例項化
 *
 */
public class TestStaticFactory {

    @Test
    public void demo01() {
        // 以前:使用自定義的靜態例項工廠
        UserService userService = MyBeanFactory.createService();

        userService.addUser();
    }

    @Test
    public void demo02() {
        // 現在:使用spring 工廠:將自定義的靜態工廠建立的例項交給spring管理
 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換 
// UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
}
}

  

注意:當使用JDK版本為1.8時,執行上面的測試程式碼會出現一個問題: java.lang.IllegalArgumentException,
問題解決連結:使用Junit測試一個 spring靜態工廠例項化bean 的例子,所有程式碼都沒有問題,但是出現java.lang.IllegalArgumentException異常
小結:在以後的開發中,工廠類不需要我們去手寫,因為別人已經寫好了,我們通過編寫配置檔案,把別人寫好的工廠類拿來,寫上要用的方法名,然後把它生產後的例項給Spring存起來,以後我們要用什麼例項,跟Spring說一下,去拿就可以了。

 

使用`例項工廠方法`例項化

 

例項工廠:必須先有工廠的例項物件,然後通過例項物件去建立物件。特點:提供所有的方法都是“非靜態”的。

示例中用到的 UserService.java 和 UserServiceImpl.java 程式碼同上面例項中的程式碼,這裡不再贅述!

  

在spring容器中進行配置:

applicationContext.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.xsd">
 <!--第三種例項化Bean 的方式 :使用例項工廠方法例項化 -->
    <!--建立工廠例項 -->
    <bean id="myBeanFactoryId" class="com.itheima.c_inject.c_factory.MyBeanFactory" ></bean>
    <!--通過工廠例項,獲得物件      factory-bean 工廠例項名稱        factory-method 普通方法名稱 -->
    <bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"></bean> </beans>

  靜態工廠類程式碼:

 
public class MyBeanFactory {

    /**
     * 建立例項的工廠,所有方法非靜態
     * 
     * @return
     */
    public UserService createService() {
        return new UserServiceImpl();
    }

    // 還有建立其他例項的工廠
    // ......
}

  測試程式碼:
TestFactory.java

 

package test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 第三種例項化Bean 的方式 :使用例項工廠方法例項化
 *  */
public class TestFactory {

    @Test
    public void demo01() {
        // 以前:使用自定義的例項工廠
        // 1、建立工廠例項
        MyBeanFactory myBeanFactory = new MyBeanFactory();
        // 2、通過工廠例項,獲得物件
        UserService userService = myBeanFactory.createService();

        userService.addUser();
    }

    @Test
    public void demo02() {
        // 現在:使用spring 工廠:將自定義的例項工廠建立的例項交給spring管理
 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = applicationContext.getBean("userServiceId", UserService.class); // 這種方式底層會自動轉換
// UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
}
}

 

  

  • 一、Bean的種類

    • 普通bean :之前操作的都是普通bean。例如:< bean id="xxx" class="A" >,這句程式碼的意思是:Spring直接建立A的例項,並返回。

    • FactoryBean :是一個特殊的bean,具有工廠生成物件能力,但是隻能生成特定的物件。
      想要生產物件的bean 必須實現FactoryBean 介面,此介面提供方法getObject(); 用於獲得特定bean。

      • 示例:< bean id="xxx" class="FB">,這句程式碼的意思是:Spring會先建立FB例項,然後呼叫getObject(); 方法,並返回方法的返回值。
        即相當於如下兩行程式碼:
        FB fb = new FB();
        return fb.getObject();
    • BeanFactory 和 FactoryBean 對比?

      • BeanFactory :是一個生產bean的工廠,用於生成任意的bean。
      • FactoryBean :是一個特殊的bean,用於生成另一個特定的bean。
        • 例如:ProxyFactoryBean ,此工廠bean用於生產代理物件的例項。< bean id="xxx" class="….ProxyFactoryBean">,這句程式碼的意思是:獲得代理物件的例項。即AOP使用。
    • spring容器中bean元素id和name屬性的區別?

      • 在spring容器中新增以下配置:
        示例:< bean id="userServiceId" class="com.itheima.a_ioc.UserServiceImpl">

      • bean節點中id和name的區別:

      • 區別一:

        • id : 指定唯一例項引用
        • name : 可以指定多個例項引用,例如name="名稱1, 名稱2"
      • 區別二:

        • id :id的命名要滿足XML對ID屬性命名規範
          例如:必須以字母開始,可以使用字母、數字、連字元、下劃線、句話、冒號

        • name : 如果Bean的名稱中含有特殊字元,就需要使用name屬性
          例如 : < bean name="# boy" class="cn.itheima.ioc.Boy"/>

        • 因為name屬性可以相同,所以後出現Bean會覆蓋之前出現的同名的Bean。

      總結:專案開發的時候,強烈要求用id,因為id可以表示唯一引用。

 

  • 二、Bean的作用域

Bean 的作用域:用於確定spring所建立bean 的例項個數。

 

 

    • 取值:
      • singleton 單例,預設值。
      • prototype 多例,每執行一次getBean() 將獲得一個例項。例如:在struts整合spring時,需要配置action為多例。
    • 配置示例:預設情況下會在容器啟動時初始化bean,但我們可以指定Bean節點的 lazy-init="true" 來延遲初始化bean,這時候,只有第一次獲取bean會才初始化bean。
      • 例如:`< bean id="xxx" class="xxx" scope="xxx">`

    • 例如:< bean id="xxx" class="service.UserServiceImpl" lazy-init="true">
    • 如果想對所有bean都應用延遲初始化,可以在根節點beans設定 default-lazy-init="true"
      例如:< beans default-lazy-init="true“>
    • Portlet是基於java的web元件,由portlet容器管理,並由容器處理請求,生產動態內容。
      • Portals使用portlets作為可插拔使用者介面元件,提供資訊系統的表示層。
      • 作為利用servlets進行web應用程式設計的下一步,portlets實現了web應用的模組化和使用者中心化。

在spring容器中進行配置:

applicationContext.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.xsd">
<bean id="userServiceId" class="com.itheima.d_scope.UserServiceImpl" scope="prototype"></bean>
</beans>

  測試程式碼:
TestScope.java

public class TestScope {

    @Test
    public void demo01() {
        // 現在:使用spring 工廠
 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService1 = applicationContext.getBean("userServiceId", UserService.class);
// 這種方式底層會自動轉換
UserService userService2 = applicationContext.getBean("userServiceId", UserService.class);
// 這種方式底層會自動轉換
// 預設Bean的作用域是單例,所以列印的物件的地址是一樣的
// System.out.println(userService1); // service.UserServiceImpl@2ac273d3
// System.out.println(userService2); // service.UserServiceImpl@2ac273d3
// 現在在配置檔案中新增scope屬性,值為prototype,此時Bean的作用域變為多例了,再次列印,輸出地址不一樣了
System.out.println(userService1);
//service.UserServiceImpl@66480dd7
System.out.println(userService2);
//service.UserServiceImpl@52a86356
}
}

  

 

  • 三、Bean的生命週期

Bean 的生命週期詳情

 

  1. instantiate bean 物件例項化。
  2. populate properties 封裝屬性。
  3. 如果Bean實現 BeanNameAware,則表示執行 setBeanName
  4. 如果Bean實現 BeanFactoryAware 或者 ApplicationContextAware,則表示設定例項工廠(setBeanFactory)或者上下文物件(setApplicationContext)。
  5. 如果存在類實現 BeanPostProcessor(後處理Bean),則表示執行 postProcessBeforeInitialization
  6. 如果Bean實現 InitializingBean,則表示執行 afterPropertiesSet
  7. 呼叫,則表示指定初始化方法 init
  8. 如果存在類實現 BeanPostProcessor(處理Bean),則表示執行 postProcessAfterInitialization
  9. 執行業務處理
  10. 如果Bean實現 DisposableBean,則表示執行 destroy
  11. 呼叫 ,則表示指定銷燬方法 customerDestroy

 

 

  • 四、Bean 的初始化和銷燬

 

目標方法執行前和執行後,將進行Bean的初始化或銷燬。
  示例:<bean id="xxx" class="xxx" init-method="初始化的方法名稱" destroy-method="銷燬的方法名稱"></bean>
示例程式碼如下:

 

編寫目標類程式碼:
UserService.java

public interface UserService {
    void addUser();
}

  

UserServiceImpl.java

public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("e_lifecycle add user");
    }

    public void myInit() {
        System.out.println("我的初始化方法");
    }

    public void myDestory() {
        System.out.println("我的銷燬方法");
    }
}

  

在spring容器中進行配置:

applicationContext.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.xsd">
<!--  
        init-method 用於配置初始化方法,用於準備資料等使用場景
        destroy-method 用於配置銷燬方法,用於清理資源等使用場景
    -->                        
    <bean id="userServiceId" class="service.UserServiceImpl" 
        init-method="myInit" destroy-method="myDestory"></bean> </beans>

  編寫測試程式碼:

 
public class TestLifecycle {

    @Test
    public void demo01() throws Exception {
        // 現在:使用spring 工廠(spring 容器)
 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
 UserService userService = (UserService) applicationContext.getBean("userServiceId"); 
userService.addUser(); // 要想使我的銷燬方法也執行,必須要求:
// 1.容器必須先close,我的銷燬方法才會執行;
// 2.必須是單例的(spring所建立該bean的例項個數只有一個)即bean中的scope配置成預設即可。
// 因為此close方法在介面 ApplicationContext 中沒有定義,而在實現類中提供了該方法,我們可以使用反射,因為反射最後執行的就是實現類中的方法。
applicationContext.getClass().getMethod("close").invoke(applicationContext); } }

  

  • 五、BeanPostProcessor 後處理Bean

  • 是由spring提供的一種機制,只要實現類實現此介面BeanPostProcessor,並將該實現類提供給spring容器,spring容器將自動執行兩個方法:在初始化方法前執行before()方法,在初始化方法後執行after()方法。配置格式: 這句程式碼的意思就是:把實現類提供給了spring容器。
  • Factory hook(勾子) that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies.
  • spring提供工廠勾子,用於修改例項物件,可以生成代理物件。(是AOP底層)
    谷歌翻譯:Factory hook(勾子),允許自定義修改新的bean例項,例如:檢查標記介面或用代理包裝它們。

我們來模擬這句話的意思:

before()    => postProcessAfterInitialization(Object bean, String beanName) 
after()     => postProcessBeforeInitialization(Object bean, String beanName) 

A a = new A();

a = B.before(a); // 將a的例項物件傳遞給後處理bean,可以什麼都沒做,也可以做一些事情,比如:生成jdk代理物件並返回給a,這樣a就從例項物件變成代理物件了,此時的a就具有了AOP功能;再比如,如果把null返回給a,再用a去呼叫方法,就會出現空指標異常。
a.init();
a = B.after(a);

// 以下是AOP演示:
// 我們現在在後處理Bean 程式碼執行完之後,把jdk代理物件返回給a。讓a在呼叫addUser()之前先做一些事情

// 之前要做的事情
a.addUser(); // 在目標方法的前後可以做一些事情,例如:開啟事務、提交事務、效能監控(前後時間)等等
// 之後要做的事情

a.destroy();

  目標類示例程式碼如下:
       UserService.java

 

public interface UserService {
    void addUser();
}

 

  UserServiceImpl.java

 

public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("e_lifecycle add user");
    }

    public void myInit() {
        System.out.println("我的初始化方法");
    }

    public void myDestory() {
        System.out.println("我的銷燬方法");
    }
}

 

  實現類示例程式碼如下:
MyBeanPostProcessor.java

 

public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("執行了前方法:" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
        System.out.println("執行了後方法:" + beanName);
        // 傳入的引數bean是我們的目標物件,此時我們的目標物件只有一個介面,那麼我們的代理物件也只有一個介面
        // 生成jdk代理物件
        return Proxy.newProxyInstance(
                MyBeanPostProcessor.class.getClassLoader(), // 代理物件
                bean.getClass().getInterfaces(), // 目標物件
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("---開啟事務---");
                        Object obj = method.invoke(bean, args); // 執行目標方法,本例中的目標方法是addUser
                        System.out.println("---關閉事務---");
                        return obj;
                    }
                }); // 代理的處理程式
    }
}

 

  

在spring容器中進行配置:

applicationContext.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.xsd">
<!--  
        init-method 用於配置初始化方法,用於準備資料等使用場景
        destroy-method 用於配置銷燬方法,用於清理資源等使用場景
    -->                        
    <bean id="userServiceId" class="service.UserServiceImpl" 
        init-method="myInit" destroy-method="myDestory"></bean>

    <!-- 將後處理的實現類註冊給spring -->
    <bean class="com.itheima.e_lifecycle.MyBeanPostProcessor"></bean> </beans>

  測試示例程式碼如下:
      TestLifecycle.java

 

public class TestLifecycle {

    @Test
    public void demo01() throws Exception {
        // 現在:使用spring 工廠(spring 容器)
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); 
UserService userService = (UserService) applicationContext.getBean("userServiceId");
userService.addUser();
// 要想使我的銷燬方法也執行,必須要求:
// 1.容器必須先close,我的銷燬方法才會執行;
// 2.必須是單例的(spring所建立該bean的例項個數只有一個)即bean中的scope配置成預設即可。
// 因為此close方法在介面 ApplicationContext 中沒有定義,而在實現類中提供了該方法,我們可以使用反射,因為反射最後執行的就是實現類中的方法。
applicationContext.getClass().getMethod("close").invoke(applicationContext);
}
}

 

  

 

    • 執行結果截圖:
    • 問題1:後處理bean作用某一個目標類,還是所有目標類?
      答:所有。
    • 問題2:如何只作用一個?
      答:通過獲取"引數2"beanName進行控制。例如:"xxx".equals(method.getName());

 

  • 六、依賴注入Bean 的屬性(xml)

 

  • 注入依賴物件可以採用:手工裝配自動裝配。在實際應用中建議使用手工裝配,因為自動裝配會產生未知情況,開發人員無法預見最終的裝配結果。
    • 手動裝配:一般進行配置資訊都採用手動裝配。
      • 基於xml裝配基於註解裝配 => 之後講解
        • 構造方法注入
        • 屬性setter方法注入
        • 介面注入(spring不支援)
    • 自動裝配:在struts 和spring 整合的時候使用自動裝配。
      • byType:按型別裝配
      • byName:按名稱裝配
      • constructor:按構造裝配
      • autodetect:不確定裝配(即自動裝配)

 

 

構造方法

Bean物件類:

public class User {

    private Integer uid;
    private String username;
    private Integer age;

    public User(Integer uid, String username) { // 構造方法一
        super();
        this.uid = uid;
        this.username = username;
    }

    public User(String username, Integer age) { // 構造方法二
        super();
        this.username = username;
        this.age = age;
    }
    // 省略getter 和 setter 方法
    // ......

  

在spring容器中進行配置:

applicationContext.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.xsd">
 <!--6.5.1、構造方法注入 
        <constructor-arg> 用於配置構造方法的一個引數argument
            name :引數的名稱
            value :設定普通資料
            ref :設定引用資料,一般是另一個bean 的id值

            index :引數的索引號,從0開始 。如果只有索引,匹配到了多個構造方法時,預設使用第一個。
            type :確定引數型別

        例如1:name屬性開發中不常用,因為使用該屬性需要關聯要例項化物件的原始碼,否則name的值你就不知道。而一般開發中我們我們不會得到原始碼。
            <constructor-arg name="username" value="李曉藝"></constructor-arg>
            <constructor-arg name="age" value="26"></constructor-arg>
        例如2:型別type 和  索引index (這兩者結合使用)
            <constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
            <constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>

            注意:在開發中為了指定執行的是哪個構造方法,一般使用index屬性和type屬性結合的方式。
    -->            
    <bean id="userId" class="entity.User">
        <constructor-arg index="0" type="java.lang.String" value="1"></constructor-arg>
        <constructor-arg index="1" type="java.lang.Integer" value="2"></constructor-arg>
    </bean> </beans>

  

setter方法


在spring容器中進行配置:


applicationContext.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.xsd">
 <!--6.5.2、setter方法注入 
        * 注入的是普通資料 時
            <property name="" value="值"></property>
            等效
            <property name="">
                <value>值</value>
            </property>

        * 注入的是引用資料時
            <property name="" ref="另一個bean的id"></property>
            等效
            <property name="">
                <ref bean="另一個bean的id"></ref>
            </property>
    -->
    <bean id="personId" class="entity.Person">
        <property name="pname" value="曉藝"></property>
        <property name="age">
            <value>26</value>
        </property>

        <property name="homeAddr" ref="homeAddrId"></property>
        <property name="companyAddr">
            <ref bean="companyAddrId"></ref>
        </property>
    </bean>

    <bean id="homeAddrId" class="entity.Address">
        <property name="addr" value="山西運城"></property>
        <property name="tel" value="911"></property>
    </bean>

    <bean id="companyAddrId" class="entity.Address">
        <property name="addr" value="百子灣"></property>
        <property name="tel" value="120"></property>
    </bean> </beans>

  

  • 七、P名稱空間 [瞭解]

    • 是對"setter方法注入"進行簡化,替換

      而是在

    • p名稱空間使用前提:必須新增名稱空間。如下圖所示:

 

在spring容器中進行配置:


applicationContext.xml
 <!--6.5.3、P名稱空間[瞭解]
            是對"setter方法注入"進行簡化,替換`<property name="屬性名稱">`,
            而是在`<bean p:屬性名稱="普通值" 和 p:屬性名稱-ref="引用值">`
            p名稱空間使用前提:必須新增名稱空間。

            注意:開發中一般不這麼用,一般用於裝逼用。
    -->
    <bean id="personId" class="com.itheima.f_xml.c_p.Person" 
        p:pname="明軍" 
        p:age="26" 
        p:homeAddr-ref="homeAddrId" 
        p:companyAddr-ref="companyAddrId">
    </bean>

    <bean id="homeAddrId" class="entity.Address"
        p:addr="河南信陽"
        p:tel="119">
    </bean>

    <bean id="companyAddrId" class="entity.Address"
        p:addr="青年路"
        p:tel="110">
    </bean>

  

 
  • 八、SpEL [瞭解]


  • 進行統一程式設計,所有的內容都使用value。
    格式:
    #{123}、#{'bruce'}、#{2e5} :數字、字串、科學計數法(常量)
    #{beanId} :引用另一個Bean
    #{beanId.propName} :引用Bean 的屬性(操作資料)
    #{beanId.toString()} :引用Bean 的方法(執行方法)
    #{T(類).欄位|方法} :引用靜態方法或欄位,例如:T(java.lang.Math).PI
    #{3 lt 4 == 4 ge 3} :運算子支援
    #{user.name matches ‘[a-z]{6,}’} :正則表示式支援
    #{likes[3]} :集合支援

示例程式碼如下:

在spring容器中進行配置:


applicationContext.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.xsd">
 <!--6.5.4、SpEL 
        <property name="cname" value="#{'Bruce'}"></property>

        <property name="cname" value="#{customerId.cname.toUpperCase()}"></property>
            通過另一個bean,獲得屬性,再呼叫的方法。
        <property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
            ?. 如果物件不為null,將呼叫方法,為null,也去呼叫方法,不報錯。

    -->            
    <bean id="customerId" class="com.itheima.f_xml.d_SpEL.Customer">
        <property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
        <property name="pi" value="#{T(java.lang.Math).PI}"></property>
    </bean> </beans>

  

  • 九、集合注入

在spring容器中進行配置:


applicationContext.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.xsd">
<!--6.5.5、集合注入
        集合的注入都是給<property>新增子標籤
            陣列:<array>
            List:<list>
            Set:<set>
            Map:<map>   注意:map存放的是k/v鍵值對,使用<entry>描述
            Properties  使用 <props> 和 <prop> 描述,示例:<props><prop key=""></prop></props>

                普通資料 放在:<value>
                引用資料 放在:<ref>
    -->            
    <bean id="collectionDataId" class="entity.CollectionData">
        <property name="arrayData">
            <array>
                <value>cmj</value>
                <value>lxy</value>3
                <value>明軍</value>
                <value>曉藝</value>
            </array>