Spring
簡介
spring,英文單詞為春天,表示為軟體行業帶來了春天。
2002年,首次推出了Spring框架的雛形:interface21框架。官網為:www.interface21.io但是現在進去時間略長。
Spring框架是以上面這個框架為基礎重新設計,並不斷對其進行改進豐富其內涵,在2004年3月24日釋出了1.0正式版本。
框架的創始人是Rod Johnson,此人原先是悉尼大學音樂學的博士,讓人大吃一驚。
Spring的理念:從官網進入?頂部導航欄projects?Spring framework?Learn?手冊?歷史?設計理念
When you learn about a framework, it’s important to know not only what it does but what principles it follows. Here are the guiding principles of the Spring Framework:
Provide choice at every level. Spring lets you defer design decisions as late as possible. For example, you can switch persistence providers through configuration without changing your code. The same is true for many other infrastructure concerns and integration with third-party APIs.
Accommodate diverse perspectives. Spring embraces flexibility and is not opinionated about how things should be done. It supports a wide range of application needs with different perspectives.
Maintain strong backward compatibility. Spring’s evolution has been carefully managed to force few breaking changes between versions. Spring supports a carefully chosen range of JDK versions and third-party libraries to facilitate maintenance of applications and libraries that depend on Spring.
Care about API design. The Spring team puts a lot of thought and time into making APIs that are intuitive and that hold up across many versions and many years.
Set high standards for code quality. The Spring Framework puts a strong emphasis on meaningful, current, and accurate javadoc. It is one of very few projects that can claim clean code structure with no circular dependencies between packages.
這是官網上說的自身的特點:?
導包
搜尋spring maven
到maven spring 搜尋web mvc,現在也搜尋不到,直接網站送上:
www.mvnrepository.com,進入網站後搜尋web mvc,接著是點選第一個,點選最新的版本,裡面有幾行程式碼,複製到配置檔案中就可以。
除了這個包,還需要一個jdbc,
http://www.java2s.com/Open-Source/Maven_Repository/Spring/spring-webmvc/spring_webmvc_2_5_1.htm
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>2.5.1</version>
</dependency>
介紹
官網:https://spring.io/projects/spring-framework
https://spring.io/projects/spring-framework#overview
官方下載地址:https://repo.spring.io/ui/native/release/org/springframework/spring
進入官網之後,https://docs.spring.io/spring-framework/docs/4.3.9.RELEASE/spring-framework-reference/html/
進入路徑沒理清楚。進入該網站後,尋找Distribution Zip Files,找到它的網站說明,就可以。
GitHub:https://github.com/spring-projects/spring-framework
優點
- Spring是一個開源的免費的框架(容器)
- Spring是一個輕量級的、非入侵式的框架
- 控制反轉(IOC),面向切面程式設計(AOP)(面試肯定會問)
- 支援事務的處理,對框架整合的支援,幾乎市面上所有的框架都支援。
總結:Spring是一個輕量級的控制反轉(IOC)和麵向切面程式設計(AOP)的框架
組成
擴充
官網介紹說,現代化的Java開發,說白就是基於spring的開發。
Spring Boot
-
- 一個快速開發的腳手架,用了之後,只需要做一些簡單的配置,便可以快速開發一個網站
- 基於SpringBoot可以快速開發單個微服務
- 約定>配置
- Spring Cloud
- SpringCloud是基於SpringBoot實現的
因為現在大多數公司都在使用SpringBoot進行快速開發,學習SpringBoot的前提,需要完全掌握Spring及SpringMVC——這兩者具有承上啟下的作用。
缺點:發展過久,違背最初理念。配置繁瑣,直到SpringBoot才解放,人稱“配置地獄”。
IOC 理論指導
-
UserDao介面(在com.kuang.dao包中新建UserDao介面)
public interface UserDao { void GetUser(); }
-
UserDaoImpl實現類(在com.kuang.dao包中新建UserDaoImpl實現類)
public class UserDaoImpl implements UserDao{ public void GetUser(){ System.out.println("預設獲取使用者資料"); } }
-
UserService介面(在com.kuang.service包中新建UserService介面)
public interface UserService { void GetUser(); void setUserDao(UserDao userdao); }
-
UserServiceImpl業務實現類(在com.kuang.service包中新建UserServiceImpl介面)
public class UserServiceImpl implements UserService {
// 業務層調Dao層,Java中除了繼承還有個組合的概念。用的話直接放過來就行。
// 下面這一行程式碼,能不能解決這個問題,使得有不同的類,也可以用不同的方法。雖然是同一個名字。
private UserDao userDao;
// 利用Set動態進行值的注入
public void setUserDao( UserDao userDao){
this.userDao = userDao;
}
// 像這樣就是寫死的,真正呼叫的是業務層調Dao層。
public void GetUser(){
userDao.GetUser();
}
}
在我們之前的業務中,使用者的需求可能會影響我們原來的程式碼,我們需要根據使用者的需求不停地修改程式碼。如果程式程式碼量十分大,修改一次的成本代價十分昂貴。
但是使用一個set介面實現,會實現控制反轉的效果。原先是我們來控制是使用哪個類執行,現在使用者只需要傳個引數,就可以動態的控制使用哪個類執行。
程式碼:(同時介面也得宣告,否則沒法呼叫。)
private UserDao userDao;
// 利用Set動態進行值的注入
public void setUserDao( UserDao userDao){
this.userDao = userDao;
}
- 之前程式是主動建立物件,控制權在程式設計師手上,所以使用者的每個需求都會讓程式設計師改變程式碼,利用set注入後,程式沒有主動性。而是變成被動的接收物件。
- 這種思想,從本質上解決了問題,程式設計師不必再管理物件的建立。系統的耦合性大大降低,可以更加專注的在業務的實現上。
- 業務層呼叫dao層,service呼叫的是dao層。
IOC本質
控制反轉(Inversion of Control)是一種設計思想,依賴注入是實現IOC的一種方法,也有人認為DI(依賴注入)只是IOC的另一種說法。沒有IOC的程式中(就是沒有控制反轉這樣一個概念),我們使用物件導向程式設計,物件的建立與物件間的依賴關係完全硬編碼在程式中,物件的建立由程式自己控制,控制反轉後將物件轉移給第三方,個人認為所謂控制反轉就是,獲得依賴物件的方式反轉了。
IOC是Spring框架的核心內容,使用多種方式完美的實現了IOC,可以使用XML配置,也可以使用註解,新版本的Spring也可以零配置實現IOC。
Spring容器在初始化時先讀取配置檔案,根據配置檔案或者後設資料建立或者組織物件存入容器中,程式使用時再從IOC容器中取出需要的物件。
採用XML方式配置Bean時,Bean的定義資訊和實現是分離的,而採用註解的方式可以把兩者合為一體,Bean的定義資訊直接以註解的形式定義在實現類中,從而達到了零配置的目的。
控制反轉是一種通過描述(XML或者註解)並通過第三方去生產或者獲取特定物件的方式。在Spring中實現控制反轉的是IOC容器,其實現方式是依賴注入。(Dependency Injection,DI)
HelloSpring
xml註解來源:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/core.html#beans
Spring-02-helloSPring流程
如何實現流程:
建立一個新的模組,spring-02-hellospring,接著建立com.kuang.dao包,新建hello類:
package com.kuang.pojo;
public class Hello {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
再新建beans.xml檔案,檔案中的內容由來:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/core.html#beans
以下示例顯示了基於 XML 的配置後設資料的基本結構:
中間的bean標籤刪除,要自己寫。
<?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="..." class="..."> (1) (2)
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
這是自己寫的內容:
<!-- 使用Spring來建立物件,在Spring中這些都稱為Bean-->
<!-- 原先:型別 變數名 = new 型別();
Hello hello = new Hello();
現在:bean = 物件 new Hello();
id = 物件名;
class = new 的物件;
這裡的str是普通屬性,用value即可。
當屬性標籤中,不是普通屬性,而是一個類屬性時,需要用到ref引用。
-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
此時,類就對映為bean,id為hello,類中的屬性也隨之對映為value。
在測試包中新建MyTest.java測試:
第9行程式碼是官方文件指定,不能更改。
import com.kuang.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
// 獲取Spring中的上下文物件
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//我們的物件現在都在Spring中的管理了,要使用,直接取出即可。
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.toString());
}
}
測試結果:
根據這篇文章所說,Spring是一個將類放到容器中,對映為對應的bean,隨用隨取的框架。加深理解:https://blog.csdn.net/Yang_Hui_Liang/article/details/88977548
Spring-ioc-01流程
根據以上的Spring容器框架使用,現對之前的專案進行改動。
新建beans.xml,檔案中內容來源:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/core.html#beans
把bean標籤刪除,填為自己的內容:
<bean id="mysqlImpl" class="com.kuang.dao.UserDaoMySQLImpl">
</bean>
<bean id="oracleImpl" class="com.kuang.dao.UserDaoOracleImpl">
</bean>
<bean id="userImpl" class="com.kuang.dao.UserDaoImpl">
</bean>
<!--當屬性標籤中,不是普通屬性,而是一個類屬性時,需要用到ref引用。-->
<bean id="UserServiceImpl" class="com.kuang.service.UserServiceImpl">
<property name="userDao" ref="oracleImpl"/>
</bean>
在測試類中進行測試,其中,第9行程式碼是官網指定,不能更改。
import com.kuang.service.UserServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MTest {
public static void main(String[] args) {
// 獲取ApplicationContext,拿到Spring的容器
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 方法中獲取的是id
// 有了容器之後,為所欲為。
UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("UserServiceImpl");
userServiceImpl.GetUser();
}
}
測試結果:
在beans.xml檔案中更改不同的引用:測試結果也會有不同
<bean id="UserServiceImpl" class="com.kuang.service.UserServiceImpl">
<property name="userDao" ref="userImpl"/>
結果:
更改:
<bean id="UserServiceImpl" class="com.kuang.service.UserServiceImpl">
<property name="userDao" ref="mysqlImpl"/>
結果:
思考問題
-
hello物件是由誰建立的?
-
hello物件是由Spring建立的
-
Hello物件是怎麼設定的?
-
hello 物件是由Spring容器設定的
這個過程就叫控制反轉。我的理解是,本來使用者提出需求,程式設計師更改程式碼完成需求。現在程式設計師先把程式碼寫好,由使用者自己決定需求,直接在配置檔案中進行修改即可。程式設計師不必每次在更換需求時更改大量程式碼。
如果上述不理解,舉個例子去飯館吃飯,原先沒有控制反轉時,食客(使用者)想吃什麼菜(需求),廚師(程式設計師)就做什麼菜(改動對應的程式碼)。有了控制反轉後,現在飯館變成了自助餐廳,食客想吃什麼,自取即可,廚師只需要提供食客想吃的菜品即可。把選擇權交給了食客,廚師就不必根據不同的食客點菜來單獨的炒菜。
如果還是不理解控制反轉,建議先多讀幾遍,爭取記住它的內容,接著實戰程式設計,相信會理解的。
以下是狂神的筆記,有一定的抽象性,結合例子來看更易理解。
控制:誰來控制物件的建立,傳統應用程式的物件是由程式本身控制建立的,使用Spring後,物件是由Spring來建立的。
反轉:程式本身不建立物件,而變成被動的接收物件。
依賴注入:就是利用set方法來進行注入。
IOC是一種程式設計思想,由主動地程式設計變成被動地接收。
可以通過newClassPathXmlApplicaitonContext去瀏覽一下底層原始碼。
至此,我們已不必再去程式中進行改動,需要實現不同的操作,只需要在xml配置檔案中進行修改。總結IOC就是物件由Spring來建立、管理和裝配。
IOC建立檔案方式
準備:建立物件,寫入beans.xml,建立測試類
建立User物件?
package com.kuang.pojo;
public class User {
public User() {
System.out.println("李豐平!");
}
public User(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("name = " + name+" ***");
}
}
寫入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.xsd">
<bean id="user" class="com.kuang.pojo.User">
<property name="name" value="李豐平"/>
</bean>
</beans>
編寫測試類?
import com.kuang.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 在getBean時,物件已經被建立。
User user = (User) context.getBean("user");
user.show();
}
}
新建beans.xml後頂部有一行提示,”Application context not configured for this file“,點選提示最右方”Configure application context "即可。以下三步?
1、使用無參構造建立物件,預設!
2、使用有參構造建立物件,
-
通過下標賦值
<!-- 使用下標來構造有參物件--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg index="0" value="李豐平"/> </bean>
測試結果如下?
-
通過引數的型別匹配
基本型別如int可以直接用,引用型別如string,必須用以下方式
<!-- 通過引數型別類匹配有參物件--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg type="java.lang.String" value="李豐平"/> </bean>
但如果兩種或者以上引數都是String,容易混亂,也不建議使用。
測試結果如下?
-
直接通過引數名來設定
<!-- 通過引數名來配置--> <bean id="user" class="com.kuang.pojo.User"> <constructor-arg name="name" value="李豐平"/> </bean>
測試結果如下?
測試一個好玩的:
一個無參的其他類——建立一個兄弟物件。在beans.xml檔案中註冊。再到測試檔案中測試,結果是無論是否用該物件,該物件都會自動建立。且同一次只能有一個例項物件被建立。
流程?
新建UserTwo?
package com.kuang.pojo;
public class UserTwo {
private String name;
public UserTwo() {
System.out.println("UserTwo 被建立了。");
}
public UserTwo(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
寫入bean.xml?
<bean id="usertwo" class="com.kuang.pojo.UserTwo">
</bean>
在測試案例中測試?
import com.kuang.pojo.User;
import com.kuang.pojo.UserTwo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 在getBean時,物件已經被建立。
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("user");
user.show();
System.out.println(user == user2);
}
}
測試結果?
總結:在配置檔案載入時,容器中所管理物件已經初始化。
Spring配置
Alias別名
無論是之前的,還是起的別名,在getBean時都可以使用。
將?下面的程式碼放入Beans.xml中
<!-- 如果新增了別名,我們也可以使用別名獲取到該物件-->
<alias name="user" alias="USER"/>
測試檔案中下面兩種獲取物件方式等同?
import com.kuang.pojo.User;
import com.kuang.pojo.UserTwo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// 在getBean時,物件已經被建立。
User user = (User) context.getBean("user");
User user2 = (User) context.getBean("USER");
user.show();
System.out.println(user == user2);
}
}
執行結果?
Bean配置
<!--id : bean 的唯一識別符號,也就是相當於我們學過的物件名
class : bean物件所對應的全限定名(包名+類名);必須這麼寫
name : 也是別名,而且name可以同時取多個別名,可以通過空格分割,也可以通過“,”或者“;”分割
-->
<bean id="usertwo" class="com.kuang.pojo.UserTwo" name="usert ut,u2;utwo">
<constructor-arg name="name" value="李豐平的別名"/>
</bean>
import匯入
一般用於團隊開發,該配置標籤可以將多個配置檔案匯入合併為一個。
假設專案中有多個人開發,ABC,三人分別負責不同的類開發,不同的類需要註冊在不同的bean中,此時就可以利用import標籤,將所有人的bean.xml檔案合併成一個bean.xml檔案,使用時使用一個總的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">
<!--匯入之後,相當於三者檔案合併,可以直接通過本檔案獲取所有配置檔案中的內容。-->
<import resource="beans.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
</beans>
依賴注入
構造器注入
Set方式注入【重點】
- 依賴注入:Set注入
- 依賴(依賴什麼)?bean物件的建立依賴於容器
- 注入(注入什麼)?bean物件中的所有屬性由容器來注入
【環境搭建】
-
複雜型別
Address?
package com.kuang.pojo; public class Address { private String address; public Address(String address) { this.address = address; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
真實測試物件Student
package com.kuang.pojo; public class Student { // 儘量涵蓋所有型別 private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String , String > card; private Set<String> games; private String wife; private Properties info; public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String[] getBooks() { return books; } public void setBooks(String[] books) { this.books = books; } public List<String> getHobbys() { return hobbys; } public void setHobbys(List<String> hobbys) { this.hobbys = hobbys; } public Map<String, String> getCard() { return card; } public void setCard(Map<String, String> card) { this.card = card; } public Set<String> getGames() { return games; } public void setGames(Set<String> games) { this.games = games; } public String getWife() { return wife; } public void setWife(String wife) { this.wife = wife; } public Properties getInfo() { return info; } public void setInfo(Properties info) { this.info = info; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", address=" + address + ", books=" + Arrays.toString(books) + ", hobbys=" + hobbys + ", card=" + card + ", games=" + games + ", wife='" + wife + '\'' + ", info=" + info + '}'; } }
-
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.xsd"> <bean id="address" class="com.kuang.pojo.Address"> <constructor-arg name="address" value=""/> </bean> <!--第一種,普通值注入,value--> <bean id="student" class="com.kuang.pojo.Student"> <property name="address" ref="address"/> <!--第一種,普通值注入,value--> <!-- <property name="name" value="李豐平"/>--> <!-- 也可以用下面的--> <property name="name" > <value>李豐平</value> </property> </bean> </beans>
-
測試類
import com.kuang.pojo.Student; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); } }
執行效果?