<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          不用找了,30分鐘讓你學(xué)會(huì)Spring框架的使用

          共 14140字,需瀏覽 29分鐘

           ·

          2021-06-07 15:50


          汪偉俊 作者

          Java技術(shù)迷 | 出品

          IOC

          Spring框架中非常重要的兩個(gè)內(nèi)容,IOC和AOP,其中IOC指的是控制反轉(zhuǎn)(Inversion Of Control),AOP指的是面向切面編程,現(xiàn)在我們就來看看IOC究竟是何方神圣?

          控制反轉(zhuǎn)其實(shí)是一種思想,指的是將對(duì)象的操作權(quán)限交給Spring容器管理,傳統(tǒng)的編程中,我們都是自己創(chuàng)建對(duì)象,自己管理,比如:

          class A{
          }
          class B{ private A a; public B(A a){ this.a = a; }}
          class Main{ public static void main(String[] args){ A a = new A(); B b = new B(a); }}

          這段程序中,類A和類B是由我們自己創(chuàng)建,類B的構(gòu)造方法需要一個(gè)類A,這種類之間的關(guān)系也需要我們自己去維護(hù),顯然,當(dāng)系統(tǒng)中的類逐漸增多,類與類之間的關(guān)系錯(cuò)綜復(fù)雜,靠人為地維護(hù)它們不僅費(fèi)時(shí)費(fèi)力,而且容易出錯(cuò)。基于此,IOC的概念成功提出,它的思想是通過容器來實(shí)現(xiàn)對(duì)象的裝配和管理,在Spring框架中,對(duì)IOC思想的具體實(shí)現(xiàn)是依賴注入(Dependency Injection),即:Spring容器管理著所有的Bean(Spring中的類被稱為Bean),當(dāng)發(fā)現(xiàn)某些Bean依賴別的Bean時(shí),Spring會(huì)自動(dòng)將所需要的Bean注入給它。

          這里尤其需要區(qū)分兩個(gè)概念,IOC和DI,IOC指控制反轉(zhuǎn),是一種思想;DI指依賴注入,是Spring框架對(duì)IOC思想的具體實(shí)現(xiàn)。

          Spring初體驗(yàn)

          回憶我們?cè)贘avaWeb階段學(xué)習(xí)的Servlet,需要?jiǎng)?chuàng)建一個(gè)類實(shí)現(xiàn)Servlet接口或者繼承HttpServlet,并在web.xml中進(jìn)行配置,然而在整個(gè)項(xiàng)目中,我們并沒有去顯式地創(chuàng)建Servlet,那Servlet對(duì)象從何而來呢?原來,這個(gè)創(chuàng)建Servlet的過程是Tomcat容器幫助我們實(shí)現(xiàn)的。Spring容器與其非常類似,通過Spring容器,我們也無需自己創(chuàng)建對(duì)象,而只需要在配置文件中進(jìn)行配置即可,類與類之間的依賴關(guān)系也無需我們操心。

          創(chuàng)建一個(gè)普通的Maven項(xiàng)目,并引入依賴:

          <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-context</artifactId>  <version>5.2.5.RELEASE</version></dependency>

          該依賴中集成了使用Spring框架所需要的一些依賴,比如:spring-core、spring-beans、spring-expression等等,現(xiàn)在我們就可以開始使用Spring了,先創(chuàng)建一個(gè)Bean:

          public class User {
          private String name; private Integer age;
          public User(){ System.out.println("對(duì)象被創(chuàng)建了"); }
          @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; }}

          然后在resource目錄下創(chuàng)建Spring的配置文件:

          <?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.wwj.spring.bean.User"/></beans>

          非常有必要來解釋一下這個(gè)配置文件,該配置文件以 <beans> 標(biāo)簽作為根標(biāo)簽,在該標(biāo)簽內(nèi)配置的就是一個(gè)一個(gè)的Bean,所以通過 <bean> 標(biāo)簽即可配置一個(gè)Bean,其中:

          ?id:Bean的唯一標(biāo)識(shí),Spring容器是一個(gè)Map集合,其中Map的鍵就是Bean的id,所以id不能重復(fù)?class:Bean的全類路徑,用于反射創(chuàng)建對(duì)象

          配置完成后,編寫測(cè)試代碼:

          public class Main {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        User user = (User) context.getBean("user");        System.out.println(user);    }}

          通過類路徑下的配置文件構(gòu)造出容器對(duì)象(ApplicationContext),此時(shí)便可以通過對(duì)象的id獲取到容器中的Bean,運(yùn)行結(jié)果:

          對(duì)象被創(chuàng)建了User{name='null', age=null}

          也可以通過對(duì)象的類型獲?。?/p>

           User user = context.getBean(User.class);

          但很顯然,這樣的方式存在一個(gè)弊端,當(dāng)容器中存在著相同類型的多個(gè)Bean時(shí),它將無法正確獲取到Bean,我們來看看Spring會(huì)拋出什么樣的錯(cuò)誤,修改配置文件:

          <?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.wwj.spring.bean.User"/> <bean id="user2" class="com.wwj.spring.bean.User"/></beans>

          此時(shí)容器中就配置了兩個(gè)相同類型的Bean,重新執(zhí)行測(cè)試代碼,運(yùn)行結(jié)果:

          Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.wwj.spring.bean.User' available: expected single matching bean but found 2: user,user2

          意思是期望匹配到一個(gè)Bean,但是卻發(fā)現(xiàn)了兩個(gè)Bean:user和user2,此時(shí),我們就必須通過id來分別獲?。?/p>

          User user = (User) context.getBean("user");User user2 = (User) context.getBean("user2");

          Spring還提供了另一種重載的getBean方法:

          User user = context.getBean("user", User.class);User user2 = context.getBean("user2", User.class);

          使用這種方式將免去強(qiáng)制類型轉(zhuǎn)換的苦惱。

          通過查看繼承關(guān)系,我們可以發(fā)現(xiàn)ApplicationContext的頂層接口為BeanFactory:

          image.png

          事實(shí)上,BeanFactory和ApplicationContext都可以用來獲取Bean,但它倆又有一些區(qū)別:


          1.BeanFactory是Spring框架中比較原始的接口,所以它無法支持Spring插件,例如:web-mvc、aop等2.BeanFactory采用的是延遲加載策略,即:讀取完配置文件后,僅僅是實(shí)例化了容器,只有調(diào)用getBean方法時(shí)才會(huì)實(shí)例化Bean3.ApplicationContext采用的是立即加載策略,讀取完配置文件后,Bean就被實(shí)例化了

          我們可以來測(cè)試一下,首先測(cè)試BeanFactory:

          public class Main {    public static void main(String[] args) {        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));    }}

          執(zhí)行該程序,控制臺(tái)沒有任何輸出,再來測(cè)試一下ApplicationContext:

          public class Main {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");    }}

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

          對(duì)象被創(chuàng)建了對(duì)象被創(chuàng)建了

          對(duì)于兩者的選擇,推薦選擇后者,因?yàn)锽eanFactory能做的事情,ApplicationContext都能做,而ApplicationContext能做的事情,BeanFactory也許就無能為力了。

          創(chuàng)建Bean的方式

          在Spring中,共有三種創(chuàng)建Bean對(duì)象的方式:

          1.通過構(gòu)造方法創(chuàng)建2.通過靜態(tài)工廠創(chuàng)建3.通過實(shí)例工廠創(chuàng)建

          在剛才的案例中其實(shí)就是通過構(gòu)造方法創(chuàng)建的Bean對(duì)象,當(dāng)我們?cè)谂渲梦募信渲昧艘粋€(gè) <bean> 標(biāo)簽后,Spring就會(huì)自動(dòng)調(diào)用該類的無參構(gòu)造方法創(chuàng)建Bean實(shí)例,所以我們一定要保證類中含有一個(gè)無參的構(gòu)造方法,否則程序就會(huì)報(bào)錯(cuò),可以來測(cè)試一下:

          public class User {
          private String name; private Integer age;
          public User(String name){ System.out.println("對(duì)象被創(chuàng)建了"); }}

          因?yàn)樵擃愶@式地聲明了一個(gè)帶參的構(gòu)造方法,所以默認(rèn)的無參構(gòu)造方法就不會(huì)生成,現(xiàn)在來運(yùn)行一下程序:

          Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.wwj.spring.bean.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.wwj.spring.bean.User.<init>()

          意思是沒有找到默認(rèn)的構(gòu)造方法。

          第二種方式是通過靜態(tài)工廠創(chuàng)建,所以先來創(chuàng)建一個(gè)工廠:

          public class MyStaticBeanFactory {
          public static User getBean(){ return new User(); }}

          然后進(jìn)行配置:

          <bean id="user" class="com.wwj.spring.bean.MyStaticBeanFactory" factory-method="getBean"/>

          此時(shí)class屬性配置的就是工廠類的全類名了,還需要指定factory-method屬性為工廠類中的getBean方法。

          最后一種方式就是通過實(shí)例工廠創(chuàng)建,它與靜態(tài)工廠的唯一區(qū)別就是方法不為靜態(tài),但此時(shí)就需要先獲取工廠的實(shí)例了,還是先來創(chuàng)建一個(gè)工廠:

          public class MycBeanFactory {
          public User getBean(){ return new User(); }}

          然后進(jìn)行配置:

          <bean id="myBeanFactory" class="com.wwj.spring.bean.MycBeanFactory"/><bean id="user" factory-bean="myBeanFactory" factory-method="getBean"/>

          factory-bean需要指定實(shí)例工廠的id,factory-method仍然是指定實(shí)例工廠中的getBean方法。

          Bean的作用范圍

          在Spring中,Bean可以有五種作用范圍:

          1.singleton2.prototype3.request4.session5.global-session

          其中,Spring中的Bean默認(rèn)是singleton,表示單例的Bean,比如:

          public class Main {    public static void main(String[] args) {        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        User user = context.getBean(User.class);        User user2 = context.getBean(User.class);        System.out.println(user == user2);    }}

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

          對(duì)象被創(chuàng)建了true

          通過 <bean> 標(biāo)簽的scope屬性可以設(shè)置Bean的作用范圍:

          <bean id="user" class="com.wwj.spring.bean.User" scope="prototype"/>

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

          對(duì)象被創(chuàng)建了對(duì)象被創(chuàng)建了false

          能夠很清楚地看到,prototype值會(huì)使我們每次從容器中獲取到的Bean都是新創(chuàng)建的。由于request、session、global-session都需要建立在Web工程中,所以這里就不做測(cè)試了。

          Bean的生命周期

          任何對(duì)象都有其生命周期,在Spring中,單例Bean和非單例Bean的生命周期是不相同的,對(duì)于單例Bean,它隨著容器的創(chuàng)建而創(chuàng)建,隨著容器的銷毀而銷毀:

          public class User {
          private String name; private Integer age;
          public User(){ System.out.println("對(duì)象被創(chuàng)建了"); }
          public void init(){ System.out.println("對(duì)象被初始化了"); }
          public void destroy(){ System.out.println("對(duì)象被銷毀了"); }}

          在User類中添加兩個(gè)方法:init和destroy,它將作為類的初始化和銷毀方法,當(dāng)然了,這兩個(gè)方法需要進(jìn)行配置,否則是不起作用的:

          <bean id="user" class="com.wwj.spring.bean.User" init-method="init" destroy-method="destroy"/>

          init-method屬性用于指定當(dāng)前Bean的初始化方法,那么destroy-method屬性就是用于指定當(dāng)前Bean的銷毀方法了,測(cè)試代碼:

          public class Main {    public static void main(String[] args) {        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");        User user = context.getBean(User.class);        context.close();    }}

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

          對(duì)象被創(chuàng)建了對(duì)象被初始化了對(duì)象被銷毀了

          而非單例對(duì)象只有在被使用時(shí)才會(huì)創(chuàng)建(比如調(diào)用getBean方法),至于什么時(shí)候銷毀,因?yàn)镾pring容器無法知曉該對(duì)象什么時(shí)候需要被銷毀,所以非單例對(duì)象的銷毀是由垃圾回收器決定的,當(dāng)沒有對(duì)象引用它,并且觸發(fā)GC時(shí),垃圾回收器便會(huì)回收該對(duì)象(關(guān)于垃圾回收的內(nèi)容不是本文的重點(diǎn))。

          依賴注入

          在文章的最開始,我們就提到了,依賴注入,是Spring框架對(duì)于IOC的一種具體實(shí)現(xiàn),那么依賴注入體現(xiàn)在何處呢?來看一個(gè)案例:

          class A {    private B b = new B();
          public void saveUser() { b.save(); }}
          class B { public void save() { System.out.println("保存成功"); }}

          可以看到,類A是依賴于類B的,而兩個(gè)類之間的關(guān)系是由我們自己來維護(hù)的,而有了Spring之后,對(duì)于類的依賴關(guān)系,我們就徹底解放了,Spring將替我們管理類與類之間的關(guān)系,并將所需的類自動(dòng)注入到類中。

          Spring中共有三種方式實(shí)現(xiàn)依賴注入:

          1.通過構(gòu)造方法注入2.通過setXXX方法注入3.通過注解注入(注解注入方式將在后續(xù)提及)

          支持注入基本類型、String、引用類型(集合、其它的Bean),先來看看基本類型和String:

          public class User {
          private String name; private int age;
          public User(String name, int age) { this.name = name; this.age = age; }
          @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; }}

          User類中分別有一個(gè)String類型和一個(gè)int類型變量,該如何通過構(gòu)造方法注入呢?

          <bean id="user" class="com.wwj.spring.bean.User">  <constructor-arg type="int" value="30"/>  <constructor-arg type="java.lang.String" value="zhangsan"/></bean>

          其中type用于指定屬性的類型,value用于指定屬性的值,這種方式顯然是有弊端的,比如:

          public class User {
          private String name; private int age; private int salary;
          public User(String name, int age,int salary) { this.name = name; this.age = age; this.salary = salary; }}

          此時(shí)User類的構(gòu)造方法中有兩個(gè)int類型的變量,那么在進(jìn)行注入時(shí)我們就很容易被混淆,所以,我們可以借助另一個(gè)屬性進(jìn)行控制:

          <bean id="user" class="com.wwj.spring.bean.User">  <constructor-arg index="0" value="zhangsan"/>  <constructor-arg index="1" value="20"/>  <constructor-arg index="2" value="3000"/></bean>

          通過index可以指定當(dāng)前注入的參數(shù)在構(gòu)造方法中的索引位置,這樣就能夠準(zhǔn)確地將數(shù)據(jù)注入到屬性中。還可以通過如下方式進(jìn)行設(shè)置:

          <bean id="user" class="com.wwj.spring.bean.User">  <constructor-arg name="name" value="zhangsan"/>  <constructor-arg name="age" value="20"/>  <constructor-arg name="salary" value="3000"/></bean>

          name屬性用于指定當(dāng)前注入的參數(shù)在構(gòu)造方法中的屬性名稱,通過它也能夠準(zhǔn)確地將數(shù)據(jù)注入到屬性中,它的好處是能夠更加直觀地看到注入的是構(gòu)造方法中的哪個(gè)屬性值。對(duì)于一些特殊的類型變量,比如日期類型,就需要先配置日期類的Bean,再將其注入到屬性中:

          <bean id="user" class="com.wwj.spring.bean.User">  <constructor-arg name="name" value="zhangsan"/>  <constructor-arg name="age" value="20"/>  <constructor-arg name="salary" value="3000"/>  <constructor-arg name="birthday" ref="date"/></bean>
          <bean id="date" class="java.time.LocalDateTime" factory-method="now"/>

          由于LocalDateTime類的構(gòu)造方法被私有化了,所以我們必須指定factory-method屬性,讓其通過now方法創(chuàng)建對(duì)象實(shí)例,此時(shí)在注入的時(shí)候就得使用ref屬性指定Bean的id。

          同樣的參數(shù),也可以使用setXXX方法進(jìn)行注入,所以先提供對(duì)應(yīng)的setXXX方法:

          public class User {
          private String name; private int age; private int salary; private LocalDateTime birthday;
          public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public void setSalary(int salary) { this.salary = salary; } public void setBirthday(LocalDateTime birthday) { this.birthday = birthday; }}

          setXXX方法是較為常用的一種注入方式,它使用property標(biāo)簽來實(shí)現(xiàn):

          <bean id="user" class="com.wwj.spring.bean.User">  <property name="name" value="zhangsan"/>  <property name="age" value="30"/>  <property name="salary" value="3000"/>  <property name="birthday" ref="date"/></bean>
          <bean id="date" class="java.time.LocalDateTime" factory-method="now"/>

          需要區(qū)分property和constructor-arg中的name屬性,在constructor-arg標(biāo)簽中,name指定的是構(gòu)造方法中的屬性名,而在property標(biāo)簽中,name指定的是setXXX中的XXX部分,比如: setAge ,則name值就為 age 。

          知道了如何注入之后,我們就可以修改最開始的程序:

          public class A {    private B b;
          public void setB(B b) { this.b = b; }
          public void saveUser() { b.save(); }}
          public class B { public void save() { System.out.println("保存成功"); }}

          配置一下:

          <bean id="a" class="com.wwj.spring.bean.A">  <property name="b" ref="b"/></bean><bean id="b" class="com.wwj.spring.bean.B"/>

          此時(shí)程序中的耦合就被解除了。

          對(duì)于集合類型,它的注入方式就特殊一些,比如:

          public class User {
          private String[] array; private List<String> list; private Set<String> set; private Map<String, String> map; private Properties prop;
          public void setArray(String[] array) { this.array = array; }
          public void setList(List<String> list) { this.list = list; }
          public void setSet(Set<String> set) { this.set = set; }
          public void setMap(Map<String, String> map) { this.map = map; }
          public void setProp(Properties prop) { this.prop = prop; }}

          它們的注入方式分別如下:

          <?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.wwj.spring.bean.User"> <!--注入數(shù)組--> <property name="array"> <array> <value>zhangsan</value> <value>lisi</value> <value>wangwu</value> </array> </property> <!--注入List--> <property name="list"> <list> <value>zhangsan</value> <value>lisi</value> <value>wangwu</value> </list> </property> <!--注入Set--> <property name="set"> <set> <value>zhangsan</value> <value>lisi</value> <value>wangwu</value> </set> </property> <!--注入Map--> <property name="map"> <map> <entry key="001" value="zhangsan"/> <entry key="002" value="lisi"/> <entry key="003" value="wangwu"/> </map> </property> <!--注入Properties--> <property name="prop"> <props> <prop key="001">zhangsan</prop> <prop key="002">lisi</prop> <prop key="003">wangwu</prop> </props> </property> </bean></beans>

          集合類型的注入其實(shí)非常簡(jiǎn)單,而且結(jié)構(gòu)相同的集合類型,標(biāo)簽還可以互相使用,比如:

          <property name="array">  <list>    <value>zhangsan</value>    <value>lisi</value>    <value>wangwu</value>  </list></property>

          在數(shù)組類型中使用 <list> 標(biāo)簽是沒有任何問題的,甚至可以使用 <set> 標(biāo)簽,但最好還是和類型對(duì)應(yīng)使用,這樣可以使程序的可讀性更高。

          本文作者:汪偉俊 為Java技術(shù)迷專欄作者 投稿,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載。


          1、Intellij IDEA這樣 配置注釋模板,讓你瞬間高出一個(gè)逼格!
          2、基于SpringBoot的迷你商城系統(tǒng),附源碼!
          3、最牛逼的 Java 日志框架,性能無敵,橫掃所有對(duì)手!
          4、把Redis當(dāng)作隊(duì)列來用,真的合適嗎?
          5、驚呆了,Spring Boot居然這么耗內(nèi)存!你知道嗎?
          6、全網(wǎng)最全 Java 日志框架適配方案!還有誰不會(huì)?
          7、Spring中毒太深,離開Spring我居然連最基本的接口都不會(huì)寫了

          點(diǎn)分享

          點(diǎn)收藏

          點(diǎn)點(diǎn)贊

          點(diǎn)在看

          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  douhuaav91 | 特级西西444www大胆免费看 | 老黄色网址 | 日韩一级国产电影 | 日本无码熟妇五十路视频 |