文章目錄
Spring
1、簡介
Spring官網:https://spring.io/
Spring核心:IOC和AOP
Spring的目的:解決企業開發的複雜性,使得JavaEE開發更加容易
Spring框架是一個開放源代碼的 J2EE 應用程序框架,由 Rod Johnson發起,是針對bean的生命週期進行管理的輕量級的**控制反轉(IOC)和面向切面(AOP)**的容器框架。
從2002年第一次出現了Spring的一些核心思想,到目前,經過這麼多年發展,Spring已經沒有那麼簡單了,現在學Spring,是爲了更好學習SpringMVC和SpringBoot
Spring可以做的事
2、IOC
1、通過簡單程序理解控制反轉
控制反轉就是向外部提供調用接口,將控制權交給調用者,程序成了被動的接受對象,程序員不再管理對象的創建,降低程序的耦合性,讓程序高效健壯
-
創建dao層接口及實現類
//dao層接口 public interface UserMapper { public void getUser(); } //實現類 public class UserMapperImpl implements UserMapper { public void getUser() { System.out.println("MySQL獲取用戶數據"); } }
-
創建service層接口及實現類,調用dao層
//service層接口 public interface UserService { public void getUser(); } //實現類 public class UserServiceImpl implements UserService { private UserMapper mapper; //調用dao層接口的實現類得到user的信息 public void getUser() { mapper = new UserMapperImpl(); mapper.getUser(); } }
-
當dao層接口的實現類只有一個的時候,這樣寫沒有問題
但是,如果dao層接口的實現類有多個呢?
//新增dao層接口實現類 public class UserMapperOracleImpl implements UserMapper { public void getUser() { System.out.println("Oracle獲取用戶數據"); } } //service層接口實現類 public class UserServiceImpl implements UserService { //定義Mapper接口對象 private UserMapper mapper; public void getUser() { //具體是Mapper接口的哪個實現類就要修改代碼了 mapper = new UserMapperImpl();//使用MySQL數據庫 mapper = new UserMapperOracleImpl();//使用Oracle數據庫 mapper.getUser(); } }
-
可以看到,當有多個dao層接口的實現類時,我們要修改使用的dao層實現類,就必須要修改service層的代碼,這對於大型的程序來說簡直就是災難,相當於要把底層的代碼大換血一遍。是不可能的,那麼我們來看這種方式
//service層實現類 public class UserServiceImpl implements UserService { private UserMapper mapper; //使用set方法,用戶想使用哪個數據庫,就使用set方法調用哪個數據庫 public void setMapper(UserMapper mapper) { this.mapper = mapper; } public void getUser() { mapper.getUser(); } } //測試類 public class UserMapperTest { @Test public void testGetUser() { //得到service對象 UserService service = new UserServiceImpl(); //通過setMapper方法設置對應的Mapper接口的實現類,將具體實現類的選擇交給調用者去選擇 //解耦,這樣子就不用再改動底層的代碼 service.setMapper(new UserMapperImpl()); //調用dao層接口實現類的方法,得到數據庫中的用戶數據 service.getUser(); } }
-
在上面的代碼中,我們將使用什麼數據庫這個問題拋給了調用者去考慮,這樣子不管調用者傳遞的是哪個實現類,我們service層的代碼都不用改變,降低了程序的耦合度
-
其實在學習了Spring IOC之後,我們連new dao層接口的實現類都不用了,所有的對象都是使用IOC容器自動獲取的。完全解耦,解決此類問題。
2、IOC本質
IOC(控制反轉)是一種設計思想,當應用了IOC,IOC容器在對象初始化時,不等對象請求就主動將依賴注給它,而不是這個對象自己創建依賴對象,將對象的創建任務轉移給第三方,降低程序的耦合度。
當採用XML方式配置Bean的時候,對象的定義(在類中定義屬性和方法)和創建信息(在<bean>標籤中)是分離的,而採用註解的方式可以把兩者合爲一體,對象的信息直接以註解的形式定義在實現類中,從而達到零配置
控制反轉是一種通過第三方(XML或註解)去生產或獲取特定對象的方式,在Spring中實現控制反轉的是IOC容器,其實現方法是依賴注入(Dependency Injection,DI)
3、第一個Spring程序
-
導入依賴包
在Maven項目中導入依賴的時候,Maven會自動幫我們導入所有要用到的依賴
<!--spring框架依賴--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <!-- lombok依賴,簡化類中方法 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
-
創建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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 這裏一個bean標籤對應一個類的對象 id對應對象的對象名,是唯一標識對象的 class表示對象的類型 property標籤中給對象的字段進行賦值 --> <bean id="hello" class="org.westos.pojo.Hello"> <property name="name" value="spring"/> </bean> </beans>
-
實體類
package org.westos.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; //簡化類開發的,使用lombok就不用寫get/set/構造器/tostring等方法了 @Data @NoArgsConstructor @AllArgsConstructor public class Hello { private String name; public void show (){ System.out.println("hello,"+name); } }
-
在applicationContext.xml配置文檔中註冊bean
<!-- 這裏一個bean標籤對應一個類的對象 id對應對象的對象名,是唯一標識對象的 class表示對象的類型 property標籤中給對象的字段進行賦值 --> <bean id="hello" class="org.westos.pojo.Hello"> <property name="name" value="spring"/> </bean>
-
測試類
package org.westos.pojo; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.westos.pojo.Hello; public class HelloTest { @Test public void testShow() { //根據配置文檔獲得連接對象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //根據bean標籤中的id獲得對象 Hello hello = (Hello) context.getBean("hello"); hello.show(); } } //輸出結果 hello,spring
問題
-
hello對象誰創建的
spring
-
hello對象誰賦值的
spring
其實這就是控制反轉,由Spring容器進行對象的創建與注入,我們並沒有在程序中使用new關鍵字創建任何一個對象,對象都是從IOC容器中獲取的。
反轉:程序本身並不創建對象,而只是對象的接收者
控制反轉是一種程序設計思想,由主動的創建變成被動的接收 ,現在我們徹底不用在程序中去改動了 , 要實現不同的操作 , 只需要在xml配置文件中進行修改對象由Spring 來創建、管理 、 裝配
4、IOC創建對象的方式
-
IOC容器對象創建的時間
在加載容器的時候(獲得context連接對象時),所有<bean>標籤中的對象就被創建在容器中了,使用getBean()方法只是將對象取出來
-
對象創建的兩種方式
-
類無參構造創建(默認)
先使用類的無參構造創建,後使用setter方法初始化
使用<property>標籤(P命名注入)
-
類有參構造創建
使用類的有參構造創建並初始化
使用<constructor-arg>標籤(C命名注入),有參構造中的參數由三種寫法
- 使用參數名稱(主要使用)
- 使用參數的類型
- 使用參數下標順序
-
如果屬性是引用類型,要使用
ref
屬性引用別的對象如果是基本類型,直接使用
value
賦值
-
練習
-
創建實體類
package org.westos.pojo; public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; System.out.println("有參構造執行了"); } public User() { System.out.println("無參構造執行了"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("set方法執行了"); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void show() { System.out.println("name:" + name + ",age:" + age); } }
-
創建bean
<bean id="user" class="org.westos.pojo.User"> <!--使用默認的無參構造進行創建對象--> <property name="name" value="張三"/> <property name="age" value="23"/> <!--使用有參構造的參數名進行創建 <constructor-arg name="name" value="李四"/> <constructor-arg name="age" value="24"/>--> <!--使用有參構造的類型創建 <constructor-arg type="java.lang.String" value="王五"/> <constructor-arg type="int" value="25"/>--> <!--使用有參構造的索引創建 <constructor-arg index="0" value="趙六"/> <constructor-arg index="1" value="26"/>--> </bean>
-
測試
package org.westos.pojo; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserTest { @Test public void testShow() { ApplicationContext context = new ClassPathXmlApplicationContext("text.xml"); System.out.println("================="); User user = (User) context.getBean("user"); user.show(); } }
5、spring配置
-
<alias>
可以給bean設置別名,別名可以設置多個,中間使用空格隔開
<alias name="user" alias="user2 user3"/>
-
<bean>
用來表示一個對象,一個bean標籤就是一個對象
id是bean的唯一標誌,只能有一個值
name 可以有多個值,中間使用空格隔開
<bean id="user" name="user3 user4" class="org.westos.pojo.User">
<property name="name" value="張三"/>
<property name="age" value="23"/>
</bean>
-
<import>
用來導入其他xml配置文件,從其他文件中讀取bean標籤
在多人協作中,每個人負責一個模塊,最後使用import標籤導入進去
<import resource="text.xml"/>
6、屬性注入方式
- 創建實體類
package org.westos.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
//使用lombok簡化類的創建
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> list;
private Map<String, String> map;
private Set<String> set;
private String wife;// null
private Properties info;
}
-
創建配置文件,使用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 https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="ZSaddress" class="org.westos.pojo.Address" scope="prototype"> <property name="address" value="陝西西安"/> </bean> <bean id="student" class="org.westos.pojo.Student"> <!--基本類型直接使用value賦值--> <property name="name" value="張三"/> <!--引用類型使用ref引用--> <property name="address" ref="ZSaddress"/> <!--數組使用<array>標籤賦值--> <property name="books"> <array> <value>紅樓夢</value> <value>西遊記</value> <value>水滸傳</value> <value>三國演義</value> </array> </property> <!--List集合使用<list>標籤賦值--> <property name="list"> <list> <value>list1</value> <value>list2</value> <value>list3</value> </list> </property> <!--map集合是鍵值對的形式,使用<map>標籤賦值--> <property name="map"> <map> <entry key="k1" value="v1"/> <entry key="k2" value="v2"/> </map> </property> <!--set集合使用<set>標籤賦值--> <property name="set"> <set> <value>set1</value> <value>set2</value> </set> </property> <!--null直接使用null標籤--> <property name="wife"> <null/> </property> <!--properties配置文件使用<props>標籤賦值--> <property name="info"> <props> <prop key="id">001</prop> <prop key="name">張三</prop> </props> </property> </bean> </beans>
-
測試
package org.westos.pojo; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class StudentTest { @Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("student.xml"); Student student = (Student) context.getBean("student"); System.out.println(student); } } //運行結果 //Student(name=張三, address=org.westos.pojo.Address@eb21112, books=[紅樓夢, 西遊記, 水滸傳, 三國演義], list=[list1, list2, list3], map={k1=v1, k2=v2}, set=[set1, set2], wife=null, info={name=張三, id=001})
7、bean的作用域
在bean標籤中的scope屬性中進行配置
由如下幾個值
-
prototype
原型,每次創建新對象
-
singleton
單例,默認,只有一個對象
-
request,在web中使用
-
response,在web中使用
8、自動裝配
手動裝配:對引用類型的參數使用ref屬性手動分別引用
<bean id="user" class="org.westos.entity.User" >
<property name="name" value="張三"/>
<!--引用id==cat的對象-->
<property name="cat" ref="cat"/>
<!--引用id==dog的對象-->
<property name="dog" ref="dog"/>
</bean>
<bean id="cat" class="org.westos.entity.Cat">
<property name="name" value="貓"/>
</bean>
<bean id="dog" class="org.westos.entity.Dog">
<property name="name" value="狗"/>
</bean>
自動裝配:自動化賦值
分爲根據屬性名和屬性類型兩種方式
1、autowire在xml中實現自動裝配
-
byName
通過屬性名與bean的id進行自動匹配
本質上是setter方法,會自動匹配與屬性名相同的id的bean,id與屬性名不一致,就會賦值失敗
-
byType
通過類型自動匹配,如果同一個類型有多個bean(對象),程序就不知道使用哪個,就會報錯
<bean id="user" class="org.westos.entity.User" autowire="byName"> <property name="name" value="張三"/> <!--使用自動注入以後,就不用寫這兩行代碼了。會自動引用 <property name="cat" ref="cat"/> <property name="dog" ref="dog"/>--> </bean> <!--當使用屬性名進行自動注入的時候,如果id與屬性名不一致,就會賦值失敗--> <bean id="cat1" class="org.westos.entity.Cat"> <property name="name" value="貓"/> </bean> <bean id="dog" class="org.westos.entity.Dog"> <property name="name" value="狗"/> </bean> <!--當使用類型進行自動注入的時候,如果同一類型有bean標籤,那麼就會報錯 <bean id="dog1" class="org.westos.entity.Dog"> <property name="name" value="狗"/> </bean>-->
2、使用註解實現自動裝配
一般在實際的開發中,不會使用xml進行自動配置,而是使用Spring中的註解(@Autowire+@Qualifier)
步驟
-
增加context命名空間
-
開啓註解支持
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--開啓註解支持--> <context:annotation-config/> </beans>
-
使用註解自動裝配(spring的)
-
@Autowired
required=false,表示允許對象是否爲空,默認爲true
默認是使用byType的形式查找,找不到或同類型有多個bean就會報錯
-
@Qualifier(“bean標籤的id”)
按照指定的標籤id進行匹配查找
-
@Autowired+@Qualifier(“bean標籤的id”)
這兩個註解是Spring的,一般組合使用
-
-
Resource註解(java的)
1. 如果同時指定了name和type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常
2. 如果只指定了name,則查找匹配的id進行裝配,找不到則拋出異常
3. 如果指定了type,則查找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常
4. 如果既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;如果沒有匹配,則回退爲一個原始類型進行匹配,如果匹配則自動裝配;
5. 一般用在字段上比較多,不和前兩個混合使用
9、使用註解開發
-
導入aop的依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.2.1.RELEASE</version> </dependency>
-
配置context約束,並且開啓註解支持
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--開啓註解支持--> <context:annotation-config/> </beans>
-
@Component註解
在類上添加,該類就相當於一個<bean>標籤,id 就相當於bean標籤裏的 id,class就是該類
-
添加掃描包
<!--添加以後,所有org.westos包下,包括子包下的所有類都會被掃描--> <context:component-scan base-package="org.westos" />
-
@Value註解
都是給屬性進行賦值
- 在類屬性上使用
- 在setter方法上使用
-
@Component等價的註解
與@Component註解的作用完全一致,區別在於約定俗成的使用地點
- @Repository,在dao層
- @Service,在service層
- @Controller,在controller層
註解和xml的最佳實踐
-
xml
通過xml管理bean
-
註解
通過註解完成屬性的注入
10、spring新特性
最新SpringBoot推薦摒棄使用xml配置文件,而是完全使用註解進行配置
-
@Configuration註解
被修飾的類等價於一個xml文件,爲配置類
-
@Bean註解
被修飾的方法等同於一個<bean>標籤,方法名爲id,class爲具體返回對象的類型
-
@Import(***.class)
類似於<import>標籤,用來導入其他的配置類
但是目前傳統的SSM框架還是使用xml配置文檔居多