chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何在實(shí)際的代碼中使Spring組件的特性?

OSC開(kāi)源社區(qū) ? 來(lái)源:OSCHINA 社區(qū) ? 2023-08-11 09:52 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

首先,我們將探討一些Spring框架中IOC(Inversion of Control)的高級(jí)特性,特別是組件掃描的相關(guān)知識(shí)。組件掃描是Spring框架中一個(gè)重要的特性,它可以自動(dòng)檢測(cè)并實(shí)例化帶有特定注解(如@Component,@Service,@Controller等)的類(lèi),并將它們注冊(cè)為Spring上下文中的bean。這里,我們會(huì)通過(guò)一些詳細(xì)的例子來(lái)闡明這些概念,并且展示如何在實(shí)際的代碼中使用這些特性。

1. 組件掃描路徑

@ComponentScan注解是用于指定Spring在啟動(dòng)時(shí)需要掃描的包路徑,從而自動(dòng)發(fā)現(xiàn)并注冊(cè)組件。 我們?cè)O(shè)置組件掃描路徑包括兩種方式:

直接指定包名:如@ComponentScan("com.example.demo"),等同于@ComponentScan(basePackages = {"com.example.demo"}),Spring會(huì)掃描指定包下的所有類(lèi),并查找其中帶有@Component、@Service、@Repository等注解的組件,然后將這些組件注冊(cè)為Spring容器的bean。

指定包含特定類(lèi)的包:如@ComponentScan(basePackageClasses = {ExampleService.class}),Spring會(huì)掃描ExampleService類(lèi)所在的包以及其所有子包。

接下來(lái),給出了一個(gè)完整的例子,說(shuō)明如何使用第二種方式來(lái)設(shè)置組件掃描路徑。這可以通過(guò)設(shè)置@ComponentScan的basePackageClasses屬性來(lái)實(shí)現(xiàn)。例如:

@Configuration
@ComponentScan(basePackageClasses = {ExampleService.class})
public class BasePackageClassConfiguration {
}
以上代碼表示,Spring會(huì)掃描ExampleService類(lèi)所在的包以及其所有子包。 全部代碼如下: 首先,我們創(chuàng)建一個(gè)名為ExampleService的服務(wù)類(lèi)
package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class ExampleService {
}
接著在bean目錄下創(chuàng)建DemoDao
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class DemoDao {
}
然后在配置類(lèi)中設(shè)置組件掃描路徑
package com.example.demo.configuration;

import com.example.demo.service.ExampleService;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackageClasses = ExampleService.class)
public class BasePackageClassConfiguration {
}
我們還會(huì)創(chuàng)建一個(gè)名為DemoApplication的類(lèi),這個(gè)類(lèi)的作用是啟動(dòng)Spring上下文并打印所有注冊(cè)的bean的名稱。
package com.example.demo;

import com.example.demo.configuration.BasePackageClassConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.Arrays;

public class DemoApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new
                AnnotationConfigApplicationContext(BasePackageClassConfiguration.class);
        String[] beanDefinitionNames = ctx.getBeanDefinitionNames();
        Arrays.stream(beanDefinitionNames).forEach(System.out::println);
    }
}
運(yùn)行上述DemoApplication類(lèi)的main方法,就會(huì)在控制臺(tái)上看到所有注冊(cè)的bean的名稱,包括我們剛剛創(chuàng)建的ExampleService。

fc7dfe12-3770-11ee-9e74-dac502259ad0.png

現(xiàn)在,如果我們?cè)贓xampleService類(lèi)所在的包或者其任意子包下創(chuàng)建一個(gè)新的類(lèi)(例如TestService),那么這個(gè)組件類(lèi)也會(huì)被自動(dòng)注冊(cè)為一個(gè)bean。這就是basePackageClasses屬性的作用:它告訴Spring要掃描哪些包以及其子包。
package com.example.demo.service;

import org.springframework.stereotype.Service;

@Service
public class TestService {
}
如果再次運(yùn)行DemoApplication類(lèi)的main方法,就會(huì)看到TestService也被打印出來(lái),說(shuō)明它也被成功注冊(cè)為了一個(gè)bean。

fcc2351e-3770-11ee-9e74-dac502259ad0.png

我們可以看到這個(gè)DemoDao始終沒(méi)有被掃描到,我們看一下@ComponentScan注解的源碼

fcea8212-3770-11ee-9e74-dac502259ad0.png

可以看到basePackageClasses屬性這是一個(gè)數(shù)組類(lèi)型的,有人會(huì)疑問(wèn)了,剛剛我們寫(xiě)的@ComponentScan(basePackageClasses = ExampleService.class),這沒(méi)有用到數(shù)組啊,為什么這里還能正常運(yùn)行呢? 如果數(shù)組只包含一個(gè)元素,可以在賦值時(shí)省略數(shù)組的大括號(hào){},這是Java的一種語(yǔ)法糖。在這種情況下,編譯器會(huì)自動(dòng)把該元素包裝成一個(gè)數(shù)組。 例如,以下兩種寫(xiě)法是等價(jià)的:
@ComponentScan(basePackageClasses = {ExampleService.class})

@ComponentScan(basePackageClasses = ExampleService.class)
在上面兩種情況下,ExampleService.class都會(huì)被包裝成一個(gè)只包含一個(gè)元素的數(shù)組。這是Java語(yǔ)法的一個(gè)便利特性,使得代碼在只有一個(gè)元素的情況下看起來(lái)更加簡(jiǎn)潔。 那么為了DemoDao組件被掃描到,我們可以在basePackageClasses屬性加上DemoDao類(lèi),這樣就可以掃描DemoDao組件所在的包以及它的子包。
package com.example.demo.configuration;

import com.example.demo.bean.DemoDao;
import com.example.demo.service.ExampleService;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackageClasses = {ExampleService.class, DemoDao.class})
public class BasePackageClassConfiguration {
}
運(yùn)行結(jié)果

fd037b64-3770-11ee-9e74-dac502259ad0.png

@ComponentScan注解的源碼還有不少,后面我們用到再說(shuō)

2. 按注解過(guò)濾組件(包含)

除了基本的包路徑掃描,Spring還提供了過(guò)濾功能,允許我們通過(guò)設(shè)定過(guò)濾規(guī)則,只包含或排除帶有特定注解的類(lèi)。

這個(gè)過(guò)濾功能對(duì)于大型項(xiàng)目中的模塊劃分非常有用,可以精細(xì)控制Spring的組件掃描范圍,優(yōu)化項(xiàng)目啟動(dòng)速度。 在Spring中可以通過(guò)@ComponentScan的includeFilters屬性來(lái)實(shí)現(xiàn)注解包含過(guò)濾,只包含帶有特定注解的類(lèi)。

在下面這個(gè)例子中,我們將創(chuàng)建一些帶有特定注解的組件,并設(shè)置一個(gè)配置類(lèi)來(lái)掃描它們。 全部代碼如下: 創(chuàng)建一個(gè)新的注解Species:

package com.example.demo.annotation;

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.TYPE)
public @interface Species {
}
接下來(lái),我們將創(chuàng)建三個(gè)不同的組件,其中兩個(gè)包含Species注解:
package com.example.demo.bean;

import com.example.demo.annotation.Species;
import org.springframework.stereotype.Component;

@Species
public class Elephant {
}
Elephant類(lèi)被@Species修飾,沒(méi)有@Component修飾。
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Monkey {
}
Monkey只被@Component修飾
package com.example.demo.bean;

import com.example.demo.annotation.Species;
import org.springframework.stereotype.Component;

@Component
@Species
public class Tiger {
}
如上所示,Tiger有@Component和@Species修飾。 接著,我們需要?jiǎng)?chuàng)建一個(gè)配置類(lèi),用于掃描和包含有Species注解的組件:
package com.example.demo.configuration;

import com.example.demo.annotation.Species;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(basePackages = "com.example.demo",
        includeFilters = @Filter(type = FilterType.ANNOTATION, classes = Species.class),
        useDefaultFilters = false)
public class FilterConfiguration {
}
在這個(gè)配置類(lèi)中,我們?cè)O(shè)置了@ComponentScan注解的includeFilters屬性,F(xiàn)ilterType.ANNOTATION代表按注解過(guò)濾,這里用于掃描包含所有帶有Species注解的組件。注意,useDefaultFilters = false表示禁用了默認(rèn)的過(guò)濾規(guī)則,只會(huì)包含標(biāo)記為Species的組件。

有人可能會(huì)疑問(wèn)了,useDefaultFilters = false表示禁用了默認(rèn)的過(guò)濾規(guī)則,什么是默認(rèn)的過(guò)濾規(guī)則? 在Spring中,當(dāng)使用@ComponentScan注解進(jìn)行組件掃描時(shí),Spring提供了默認(rèn)的過(guò)濾規(guī)則。這些默認(rèn)規(guī)則包括以下幾種類(lèi)型的注解:

@Component

@Repository

@Service

@Controller

@RestController

@Configuration

默認(rèn)不寫(xiě)useDefaultFilters屬性的情況下,useDefaultFilters屬性的值為true,Spring在進(jìn)行組件掃描時(shí)會(huì)默認(rèn)包含以上注解標(biāo)記的組件,如果將useDefaultFilters設(shè)置為false,Spring就只會(huì)掃描明確指定過(guò)濾規(guī)則的組件,不再包括以上默認(rèn)規(guī)則的組件。

所以,useDefaultFilters = false是在告訴Spring我們只想要自定義組件掃描規(guī)則。 最后,我們創(chuàng)建一個(gè)主程序,來(lái)實(shí)例化應(yīng)用上下文并列出所有的Bean名稱:

package com.example.demo;

import com.example.demo.configuration.FilterConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(FilterConfiguration.class);
        String[] beanNames = ctx.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}
當(dāng)我們運(yùn)行這個(gè)程序時(shí),會(huì)看到輸出的Bean名字只包括被Species注解標(biāo)記的類(lèi)。在這個(gè)例子中會(huì)看到Tiger和Elephant,但不會(huì)看到Monkey,因?yàn)槲覀兊呐渲弥话吮籗pecies注解的類(lèi)。 運(yùn)行結(jié)果:

fd2c2a5a-3770-11ee-9e74-dac502259ad0.png

如果useDefaultFilters屬性設(shè)置為true,那么運(yùn)行程序時(shí)輸出的Bean名字將會(huì)包括Monkey。 總結(jié):上面介紹了如何使用Spring的@ComponentScan注解中的includeFilters屬性和useDefaultFilters屬性來(lái)過(guò)濾并掃描帶有特定注解的類(lèi)。

通過(guò)自定義注解和在配置類(lèi)中設(shè)置相關(guān)屬性,可以精確地控制哪些類(lèi)被Spring容器掃描和管理。如果設(shè)置useDefaultFilters為false,則Spring只掃描被明確指定過(guò)濾規(guī)則的組件,不再包含默認(rèn)規(guī)則(如@Component、@Service等)的組件。

3. 按注解過(guò)濾組件(排除)

在Spring框架中,我們不僅可以通過(guò)@ComponentScan注解的includeFilters屬性設(shè)置包含特定注解的類(lèi),還可以通過(guò)excludeFilters屬性來(lái)排除帶有特定注解的類(lèi)。這個(gè)功能對(duì)于我們自定義模塊的加載非常有用,我們可以通過(guò)這種方式,精確控制哪些組件被加載到Spring的IOC容器中。

下面我們將通過(guò)一個(gè)具體的示例來(lái)說(shuō)明如何使用@ComponentScan的excludeFilters屬性來(lái)排除帶有特定注解的類(lèi)。 全部代碼如下: 首先,創(chuàng)建一個(gè)注解Exclude:

package com.example.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Exclude {
}
定義三個(gè)類(lèi)Elephant、Monkey、Tiger
package com.example.demo.bean;

import com.example.demo.annotation.Exclude;
import org.springframework.stereotype.Component;

@Component
@Exclude
public class Elephant {
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Monkey {
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Tiger {
}
注意,這幾個(gè)類(lèi)都標(biāo)記為@Component,Elephant類(lèi)上有@Exclude注解。 接下來(lái),我們創(chuàng)建配置類(lèi)FilterConfiguration,在其中使用@ComponentScan注解,并通過(guò)excludeFilters屬性排除所有標(biāo)記為@Exclude的類(lèi):
package com.example.demo.configuration;

import com.example.demo.annotation.Exclude;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(basePackages = "com.example.demo",
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Exclude.class))
public class FilterConfiguration {
}
這樣,在Spring IOC容器中,只有Tiger和Monkey類(lèi)會(huì)被掃描并實(shí)例化,因?yàn)镋lephant類(lèi)被@Exclude注解標(biāo)記,而我們?cè)贔ilterConfiguration類(lèi)中排除了所有被@Exclude注解標(biāo)記的類(lèi)。 主程序?yàn)椋?
package com.example.demo;

import com.example.demo.configuration.FilterConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(FilterConfiguration.class);
        String[] beanNames = ctx.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}
運(yùn)行結(jié)果:

fd681ee8-3770-11ee-9e74-dac502259ad0.png

總結(jié):這小節(jié)主要講解了如何在Spring框架中通過(guò)@ComponentScan注解的excludeFilters屬性進(jìn)行注解過(guò)濾,以精確控制加載到Spring IOC容器中的組件。在本小節(jié)的示例中,我們首先創(chuàng)建了一個(gè)名為Exclude的注解,然后定義了三個(gè)類(lèi)Elephant、Monkey、和Tiger,它們都被標(biāo)記為@Component,其中Elephant類(lèi)上還有一個(gè)@Exclude注解。

接下來(lái),我們創(chuàng)建了一個(gè)配置類(lèi)FilterConfiguration,其中使用了@ComponentScan注解,并通過(guò)excludeFilters屬性排除了所有標(biāo)記為@Exclude的類(lèi)。因此,當(dāng)程序運(yùn)行時(shí),Spring IOC容器中只有Tiger和Monkey類(lèi)會(huì)被掃描并實(shí)例化,因?yàn)镋lephant類(lèi)被@Exclude注解標(biāo)記,所以被排除了。

4. 通過(guò)正則表達(dá)式過(guò)濾組件

在Spring框架中,除了可以通過(guò)指定注解來(lái)進(jìn)行包含和排除類(lèi)的加載,我們還可以利用正則表達(dá)式來(lái)對(duì)組件進(jìn)行過(guò)濾。這種方式提供了一種更靈活的方式來(lái)選擇需要被Spring IOC容器管理的類(lèi)。具體來(lái)說(shuō),可以利用正則表達(dá)式來(lái)包含或者排除名稱符合某個(gè)特定模式的類(lèi)。

下面,我們將通過(guò)一個(gè)具體的例子來(lái)展示如何使用正則表達(dá)式過(guò)濾來(lái)只包含類(lèi)名以特定字符串結(jié)尾的類(lèi)。 下面的例子將演示如何只包含類(lèi)名以Tiger結(jié)尾的類(lèi)。 全部代碼如下: 定義三個(gè)類(lèi)Tiger、Elephant、Monkey

package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Elephant {
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Monkey {
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Tiger {
}
接著我們創(chuàng)建配置類(lèi)FilterConfiguration,使用@ComponentScan注解,并通過(guò)includeFilters屬性來(lái)包含類(lèi)名以Tiger結(jié)尾的類(lèi):
package com.example.demo.configuration;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(basePackages = "com.example.demo",
        useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Tiger"))
public class FilterConfiguration {
}
在上述示例中,我們使用FilterType.REGEX過(guò)濾類(lèi)型,并指定要包含的正則表達(dá)式模式".*Tiger"。結(jié)果只會(huì)有Tiger類(lèi)會(huì)被Spring的IOC容器掃描并實(shí)例化,因?yàn)橹挥蠺iger類(lèi)的類(lèi)名滿足正則表達(dá)式".*Tiger"。這里.代表任意單個(gè)字符 (除了換行符),*代表前一個(gè)字符重復(fù)任意次,.*組合起來(lái)表示匹配任意個(gè)任意字符。 主程序:
package com.example.demo;

import com.example.demo.configuration.FilterConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(FilterConfiguration.class);
        String[] beanNames = ctx.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}
運(yùn)行結(jié)果

fdb5eab0-3770-11ee-9e74-dac502259ad0.png

總結(jié):本小節(jié)介紹了如何在Spring框架中使用正則表達(dá)式對(duì)組件進(jìn)行過(guò)濾,以選擇哪些類(lèi)應(yīng)被Spring IOC容器管理。在所給示例中,首先定義了三個(gè)類(lèi)Elephant、Monkey和Tiger。然后創(chuàng)建了一個(gè)配置類(lèi)FilterConfiguration,使用了@ComponentScan注解,并通過(guò)includeFilters屬性設(shè)置了一個(gè)正則表達(dá)式" .*Tiger",用于選擇類(lèi)名以"Tiger"結(jié)尾的類(lèi)。所以在運(yùn)行主程序時(shí),Spring的IOC容器只會(huì)掃描并實(shí)例化Tiger類(lèi),因?yàn)橹挥蠺iger類(lèi)的類(lèi)名滿足正則表達(dá)式" .*Tiger"。

5. Assignable 類(lèi)型過(guò)濾組件

"Assignable類(lèi)型過(guò)濾 " 是Spring框架在進(jìn)行組件掃描時(shí)的一種過(guò)濾策略,該策略允許我們指定一個(gè)或多個(gè)類(lèi)或接口,然后Spring會(huì)包含或排除所有可以賦值給這些類(lèi)或接口的類(lèi)。如果我們指定了一個(gè)特定的接口,那么所有實(shí)現(xiàn)了這個(gè)接口的類(lèi)都會(huì)被包含(或者排除)。同樣,如果指定了一個(gè)具體的類(lèi),那么所有繼承自這個(gè)類(lèi)的類(lèi)也會(huì)被包含(或者排除)。 在下面的例子中,我們將使用 “Assignable類(lèi)型過(guò)濾” 來(lái)包含所有實(shí)現(xiàn)了Animal接口的類(lèi)。 全部代碼如下: 首先,我們定義一個(gè)Animal接口

package com.example.demo.bean;

public interface Animal {
}
接著定義三個(gè)類(lèi):Elephant、Monkey和Tiger,其中Tiger沒(méi)有實(shí)現(xiàn)Animal接口
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Elephant implements Animal {
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Monkey implements Animal {
}
package com.example.demo.bean

import org.springframework.stereotype.Component;

@Component
public class Tiger {
}
然后,我們創(chuàng)建一個(gè)FilterConfiguration類(lèi)并使用@ComponentScan來(lái)掃描所有實(shí)現(xiàn)了Animal接口的類(lèi)。
package com.example.demo.configuration;

import com.example.demo.bean.Animal;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(basePackages = "com.example.demo",
        useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Animal.class))
public class FilterConfiguration {
}
這種過(guò)濾方式在@ComponentScan注解中通過(guò)FilterType.ASSIGNABLE_TYPE來(lái)使用,這里Spring將只掃描并管理所有實(shí)現(xiàn)了Animal接口的類(lèi)。 最后,我們創(chuàng)建一個(gè)主程序來(lái)測(cè)試:
package com.example.demo;

import com.example.demo.configuration.FilterConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(FilterConfiguration.class);
        String[] beanNames = ctx.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}
運(yùn)行結(jié)果:

fde44d06-3770-11ee-9e74-dac502259ad0.png

這里也可以看到,只有實(shí)現(xiàn)了Animal接口的類(lèi)才會(huì)被Spring的IoC容器掃描并實(shí)例化,其他的@Component類(lèi)沒(méi)有實(shí)現(xiàn)Animal接口的bean將不會(huì)被掃描和實(shí)例化。 總結(jié):本小節(jié)介紹了Spring框架中的 "Assignable類(lèi)型過(guò)濾 ",這是一種可以指定一個(gè)或多個(gè)類(lèi)或接口進(jìn)行組件掃描的過(guò)濾策略。

Spring會(huì)包含或排除所有可以賦值給這些類(lèi)或接口的類(lèi)。在本小節(jié)的例子中,首先定義了一個(gè)Animal接口,然后定義了三個(gè)類(lèi)Elephant、Monkey和Tiger,其中Elephant和Monkey實(shí)現(xiàn)了Animal接口,而Tiger沒(méi)有。然后創(chuàng)建了一個(gè)FilterConfiguration類(lèi),使用了@ComponentScan注解,并通過(guò)includeFilters屬性和FilterType.ASSIGNABLE_TYPE類(lèi)型來(lái)指定掃描所有實(shí)現(xiàn)了Animal接口的類(lèi)。

因此,當(dāng)運(yùn)行主程序時(shí),Spring的IOC容器只會(huì)掃描并實(shí)例化實(shí)現(xiàn)了Animal接口的Elephant和Monkey類(lèi),未實(shí)現(xiàn)Animal接口的Tiger類(lèi)不會(huì)被掃描和實(shí)例化。

6. 自定義組件過(guò)濾器

Spring也允許我們定義自己的過(guò)濾器來(lái)決定哪些組件將被Spring IoC容器掃描。為此,我們需要實(shí)現(xiàn)TypeFilter接口,并重寫(xiě)match()方法。在match()方法中,我們可以自定義選擇哪些組件需要被包含或者排除。 全部代碼如下: 新增一個(gè)接口Animal

package com.example.demo.bean;

public interface Animal {
    String getType();
}
定義幾個(gè)類(lèi),實(shí)現(xiàn)Animal接口
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Elephant implements Animal {
    public String getType() {
        return "This is a Elephant.";
    }
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Monkey implements Animal {
    public String getType() {
        return "This is an Monkey.";
    }
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Tiger implements Animal {
    public String getType() {
        return "This is a Tiger.";
    }
}
package com.example.demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Tiger2 {
    public String getType() {
        return "This is a Tiger2.";
    }
}
Tiger2沒(méi)實(shí)現(xiàn)Animal接口,后面用來(lái)對(duì)比。 下面我們先個(gè)自定義一個(gè)過(guò)濾器CustomFilter,它實(shí)現(xiàn)了TypeFilter接口,這個(gè)過(guò)濾器會(huì)包含所有實(shí)現(xiàn)了Animal接口并且類(lèi)名以"T"開(kāi)頭的類(lèi):
package com.example.demo.filter;

import com.example.demo.bean.Animal;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.Arrays;

public class CustomFilter implements TypeFilter {

    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        ClassMetadata classMetadata = metadataReader.getClassMetadata();

        // 如果全限定類(lèi)名以 "T" 開(kāi)頭并且實(shí)現(xiàn)了 "Animal" 接口
        return classMetadata.getClassName().startsWith("com.example.demo.bean.T") &&
                Arrays.asList(classMetadata.getInterfaceNames()).contains(Animal.class.getName());
    }
}
如果match方法返回true,那么Spring將把這個(gè)類(lèi)視為候選組件,還需滿足其他條件才能創(chuàng)建bean,如果這個(gè)類(lèi)沒(méi)有使用@Component、@Service等注解,那么即使過(guò)濾器找到了這個(gè)類(lèi),Spring也不會(huì)將其注冊(cè)為bean。因?yàn)镾pring依然需要識(shí)別類(lèi)的元數(shù)據(jù)(如:@Component、@Service等注解)來(lái)確定如何創(chuàng)建和管理bean。反之,如果match方法返回false,那么Spring將忽略這個(gè)類(lèi)。 在match方法中

metadataReader.getClassMetadata()返回一個(gè)ClassMetadata對(duì)象,它包含了關(guān)于當(dāng)前類(lèi)的一些元數(shù)據(jù)信息,例如類(lèi)名、是否是一個(gè)接口、父類(lèi)名等。

classMetadata.getClassName()返回當(dāng)前類(lèi)的全限定類(lèi)名,也就是包括了包名的類(lèi)名。

在match方法中,我們檢查了當(dāng)前類(lèi)的全限定名是否以"com.example.demo.bean.T"開(kāi)頭,并且當(dāng)前類(lèi)是否實(shí)現(xiàn)了"Animal"接口。如果滿足這兩個(gè)條件,match方法就返回true,Spring會(huì)將這個(gè)類(lèi)視為候選組件。如果這兩個(gè)條件有任何一個(gè)不滿足,match方法就返回false,Spring就會(huì)忽略這個(gè)類(lèi),不會(huì)將其視為候選組件。 然后,在我們的FilterConfiguration中,使用FilterType.CUSTOM類(lèi)型,并且指定我們剛才創(chuàng)建的CustomFilter類(lèi):

package com.example.demo.configuration;

import com.example.demo.filter.CustomFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;

@Configuration
@ComponentScan(basePackages = "com.example.demo",
        useDefaultFilters = false,
        includeFilters = @ComponentScan.Filter(type = FilterType.CUSTOM, classes = CustomFilter.class))
public class FilterConfiguration {
}
這樣,當(dāng)Spring IoC容器進(jìn)行掃描的時(shí)候,只有類(lèi)名以"T"開(kāi)頭并且實(shí)現(xiàn)了Animal接口的組件才會(huì)被包含。在我們的例子中,只有Tiger類(lèi)會(huì)被包含,Tiger2、Elephant和Monkey類(lèi)將被排除。 主程序:
package com.example.demo;

import com.example.demo.configuration.FilterConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(FilterConfiguration.class);
        String[] beanNames = ctx.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            System.out.println(beanName);
        }
    }
}
運(yùn)行結(jié)果:

fe21dc20-3770-11ee-9e74-dac502259ad0.png

調(diào)試會(huì)發(fā)現(xiàn),match方法在不停的回調(diào)。其實(shí)match方法的調(diào)用次數(shù)和Spring應(yīng)用上下文中的Bean定義數(shù)量是相關(guān)的,當(dāng)我們使用@ComponentScan進(jìn)行包掃描時(shí),Spring會(huì)遍歷指定包(及其子包)下的所有類(lèi),對(duì)每個(gè)類(lèi)進(jìn)行分析以決定是否需要?jiǎng)?chuàng)建對(duì)應(yīng)的Bean。

當(dāng)我們使用@ComponentScan.Filter定義自定義的過(guò)濾器時(shí),Spring會(huì)為每個(gè)遍歷到的類(lèi)調(diào)用過(guò)濾器的match方法,以決定是否需要忽略這個(gè)類(lèi)。因此,match方法被調(diào)用的次數(shù)等于Spring掃描到的類(lèi)的數(shù)量,不僅包括最終被創(chuàng)建為Bean的類(lèi),也包括被過(guò)濾器忽略的類(lèi)。

這個(gè)行為可能受到一些其他配置的影響。例如,如果Spring配置中使用了懶加載 (@Lazy),那么match方法的調(diào)用可能會(huì)被延遲到Bean首次被請(qǐng)求時(shí)。 總結(jié):本小節(jié)介紹了如何在Spring框架中創(chuàng)建和使用自定義過(guò)濾器,以決定哪些組件將被Spring IoC容器視為候選組件。

通過(guò)實(shí)現(xiàn)TypeFilter接口并重寫(xiě)其match()方法,可以根據(jù)自定義的條件決定哪些類(lèi)會(huì)被包含在候選組件的列表中。在這個(gè)例子中,我們創(chuàng)建了一個(gè)自定義過(guò)濾器,只有以"T"開(kāi)頭且實(shí)現(xiàn)了Animal接口的類(lèi)才會(huì)被標(biāo)記為候選組件。當(dāng)Spring進(jìn)行包掃描時(shí),會(huì)遍歷所有的類(lèi),并對(duì)每個(gè)類(lèi)調(diào)用過(guò)濾器的match()方法,這個(gè)方法的調(diào)用次數(shù)等于Spring掃描到的類(lèi)的數(shù)量。

然后,只有那些同時(shí)滿足過(guò)濾器條件并且被Spring識(shí)別為組件的類(lèi)(例如,使用了@Component或@Service等注解),才會(huì)被實(shí)例化為Bean并被Spring IoC容器管理。如果配置了懶加載,那么Bean的實(shí)例化可能會(huì)被延遲到Bean首次被請(qǐng)求時(shí)。

7. 組件掃描的其他特性

Spring的組件掃描機(jī)制提供了一些強(qiáng)大的特性,我們來(lái)逐一講解。

7.1 組合使用組件掃描

Spring提供了@ComponentScans注解,讓我們能夠組合多個(gè)@ComponentScan使用,這樣可以讓我們?cè)谝淮尾僮髦型瓿啥啻伟鼟呙琛?@ComponentScans的主要使用場(chǎng)景是當(dāng)需要對(duì)Spring的組件掃描行為進(jìn)行更精細(xì)的控制時(shí),可以在同一個(gè)應(yīng)用程序中掃描兩個(gè)完全獨(dú)立的包,也可以在應(yīng)用多個(gè)獨(dú)立的過(guò)濾器來(lái)排除或包含特定的組件。

fe376e64-3770-11ee-9e74-dac502259ad0.png

可以看到@ComponentScans注解接收了一個(gè)ComponentScan數(shù)組,也就是一次性組合了一堆@ComponentScan注解。 讓我們通過(guò)一個(gè)例子來(lái)看看如何使用@ComponentScans來(lái)組合多個(gè)@ComponentScan。 全部代碼如下: 首先,我們定義兩個(gè)簡(jiǎn)單的類(lèi),分別在com.example.demo.bean1和com.example.demo.bean2包中:

package com.example.demo.bean1;

import org.springframework.stereotype.Component;

@Component
public class BeanA {
}
package com.example.demo.bean2;

import org.springframework.stereotype.Component;

@Component
public class BeanB {
}
然后,我們?cè)谂渲妙?lèi)中使用@ComponentScans來(lái)一次性掃描這兩個(gè)包:
package com.example.demo.configuration;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScans({
        @ComponentScan("com.example.demo.bean1"),
        @ComponentScan("com.example.demo.bean2")
})
public class AppConfig {
}

最后,我們可以測(cè)試一下是否成功地掃描到了這兩個(gè)類(lèi):

package com.example.demo;

import com.example.demo.bean1.BeanA;
import com.example.demo.bean2.BeanB;
import com.example.demo.configuration.AppConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        BeanA beanA = ctx.getBean(BeanA.class);
        BeanB beanB = ctx.getBean(BeanB.class);
        System.out.println("beanA = " + beanA);
        System.out.println("beanB = " + beanB);
    }
}
運(yùn)行上述main方法,BeanA和BeanB就成功地被掃描并注入到了Spring的ApplicationContext中。 運(yùn)行結(jié)果:

fe5317c2-3770-11ee-9e74-dac502259ad0.png

總結(jié):本小節(jié)介紹了Spring包掃描機(jī)制的一個(gè)重要特性,即能夠使用@ComponentScans注解進(jìn)行組合包掃描。這個(gè)特性允許在一次操作中完成多次包掃描,實(shí)現(xiàn)對(duì)Spring組件掃描行為的精細(xì)控制。例如,可以同時(shí)掃描兩個(gè)完全獨(dú)立的包,或者應(yīng)用多個(gè)獨(dú)立的過(guò)濾器來(lái)排除或包含特定的組件。在本小節(jié)的示例中,使用@ComponentScans一次性掃描了com.example.demo.bean1和com.example.demo.bean2兩個(gè)包,成功地將BeanA和BeanB掃描并注入到Spring的ApplicationContext中。

8. 組件掃描的組件名稱生成

當(dāng)我們?cè)赟pring中使用注解進(jìn)行bean的定義和管理時(shí),通常會(huì)用到@Component,@Service,@Repository,@Controller等注解。在使用這些注解進(jìn)行bean定義的時(shí)候,如果我們沒(méi)有明確指定bean的名字,那么Spring會(huì)根據(jù)一定的規(guī)則為我們的bean生成一個(gè)默認(rèn)的名字。 這個(gè)默認(rèn)的名字一般是類(lèi)名的首字母小寫(xiě)。例如,對(duì)于一個(gè)類(lèi)名為MyService的類(lèi),如果我們像這樣使用@Service注解:

@Service
public class MyService {
}
那么Spring會(huì)為我們的bean生成一個(gè)默認(rèn)的名字myService。我們可以在應(yīng)用的其他地方通過(guò)這個(gè)名字來(lái)引用這個(gè)bean。例如,我們可以在其他的bean中通過(guò)@Autowired注解和這個(gè)名字來(lái)注入這個(gè)bean:
@Autowired
private MyService myService;
這個(gè)默認(rèn)的名字是通過(guò)BeanNameGenerator接口的實(shí)現(xiàn)類(lèi)AnnotationBeanNameGenerator來(lái)生成的。AnnotationBeanNameGenerator會(huì)檢查我們的類(lèi)是否有明確的指定了bean的名字,如果沒(méi)有,那么它就會(huì)按照類(lèi)名首字母小寫(xiě)的規(guī)則來(lái)生成一個(gè)默認(rèn)的名字。

8.1 Spring 是如何生成默認(rèn) bean 名稱的(源碼分析)

為了解釋這個(gè)過(guò)程,讓我們看一下AnnotationBeanNameGenerator類(lèi)的源碼,以下源碼對(duì)應(yīng)的Spring版本是5.3.7。 先給出源碼圖片,后面給出源碼分析

fe8d7d22-3770-11ee-9e74-dac502259ad0.png

代碼塊提出來(lái)分析:

public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    if (definition instanceof AnnotatedBeanDefinition) { // 該行檢查BeanDefinition是否為AnnotatedBeanDefinition
        String beanName = this.determineBeanNameFromAnnotation((AnnotatedBeanDefinition)definition); // 該行調(diào)用方法來(lái)從注解獲取bean名稱
        if (StringUtils.hasText(beanName)) { // 檢查是否獲取到了有效的bean名稱
            return beanName; // 如果有,返回這個(gè)名稱
        }
    }

    return this.buildDefaultBeanName(definition, registry); // 如果沒(méi)有從注解中獲取到有效的名稱,調(diào)用方法生成默認(rèn)的bean名稱
}
再看看determineBeanNameFromAnnotation方法

fee58c38-3770-11ee-9e74-dac502259ad0.png

這段代碼很長(zhǎng),我們直接將代碼塊提出來(lái)分析:

@Nullable
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
    // 1. 獲取bean定義的元數(shù)據(jù),包括所有注解信息
    AnnotationMetadata amd = annotatedDef.getMetadata();
    
    // 2. 獲取所有注解類(lèi)型
    Set types = amd.getAnnotationTypes();
    
    // 3. 初始化bean名稱為null
    String beanName = null;
    
    // 4. 遍歷所有注解類(lèi)型
    Iterator var5 = types.iterator();
    while(var5.hasNext()) {
        // 4.1 獲取當(dāng)前注解類(lèi)型
        String type = (String)var5.next();
        
        // 4.2 獲取當(dāng)前注解的所有屬性
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
        
        // 4.3 只有當(dāng)前注解的屬性不為null時(shí),才會(huì)執(zhí)行以下代碼
        if (attributes != null) {
            Set metaTypes = (Set)this.metaAnnotationTypesCache.computeIfAbsent(type, (key) -> {
                Set result = amd.getMetaAnnotationTypes(key);
                return result.isEmpty() ? Collections.emptySet() : result;
            });
            
            // 4.4 檢查當(dāng)前注解是否為帶有名稱的元注解
            if (this.isStereotypeWithNameValue(type, metaTypes, attributes)) {
                // 4.5 嘗試從注解的"value"屬性中獲取bean名稱
                Object value = attributes.get("value");
                if (value instanceof String) {
                    String strVal = (String)value;
                    
                    // 4.6 檢查獲取到的名稱是否為有效字符串
                    if (StringUtils.hasLength(strVal)) {
                        // 4.7 如果已經(jīng)存在bean名稱并且與當(dāng)前獲取到的名稱不一致,則拋出異常
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent component names: '" + beanName + "' versus '" + strVal + "'");
                        }

                        // 4.8 設(shè)置bean名稱為獲取到的名稱
                        beanName = strVal;
                    }
                }
            }
        }
    }
    
    // 5. 返回獲取到的bean名稱,如果沒(méi)有找到有效名稱,則返回null
    return beanName;
}
最后看看buildDefaultBeanName方法,Spring是如何生成bean的默認(rèn)名稱的。

ff6294da-3770-11ee-9e74-dac502259ad0.png

拆成代碼塊分析:
protected String buildDefaultBeanName(BeanDefinition definition) {
    // 1. 從bean定義中獲取bean的類(lèi)名
    String beanClassName = definition.getBeanClassName();

    // 2. 確保bean類(lèi)名已設(shè)置,否則會(huì)拋出異常
    Assert.state(beanClassName != null, "No bean class name set");

    // 3. 使用Spring的ClassUtils獲取類(lèi)的簡(jiǎn)單名稱,即不帶包名的類(lèi)名
    String shortClassName = ClassUtils.getShortName(beanClassName);

    // 4. 使用Java內(nèi)省工具(Introspector)將類(lèi)名首字母轉(zhuǎn)換為小寫(xiě)
    // 這就是Spring的默認(rèn)bean命名策略,如果用戶沒(méi)有通過(guò)@Component等注解顯式指定bean名,
    // 則會(huì)使用該類(lèi)的非限定類(lèi)名(即不帶包名的類(lèi)名),并將首字母轉(zhuǎn)換為小寫(xiě)作為bean名。
    return Introspector.decapitalize(shortClassName);
}
8.2 生成默認(rèn) bean 名稱的特殊情況 大家肯定知道UserService默認(rèn)bean名稱為userService,但如果類(lèi)名為MService,bean名稱還是MService,不會(huì)首字母小寫(xiě)。具體原因,我們來(lái)分析一下。 我們上面分析buildDefaultBeanName方法生成默認(rèn)bean名稱的時(shí)候,發(fā)現(xiàn)里面有調(diào)用decapitalize方法后再返回,我們來(lái)看看decapitalize方法。

ff8c6bac-3770-11ee-9e74-dac502259ad0.png

提出代碼塊分析一下

/**
 * 將字符串轉(zhuǎn)換為正常的 Java 變量名規(guī)則的形式。
 * 這通常意味著將第一個(gè)字符從大寫(xiě)轉(zhuǎn)換為小寫(xiě),
 * 但在(不常見(jiàn)的)特殊情況下,當(dāng)有多個(gè)字符并且第一個(gè)和第二個(gè)字符都是大寫(xiě)時(shí),我們將保持原樣。
 * 因此,“FooBah”變?yōu)椤癴ooBah”,“X”變?yōu)椤皒”,但“URL”保持為“URL”。
 * 這是 Java 內(nèi)省機(jī)制的一部分,因?yàn)樗婕?Java 對(duì)類(lèi)名和變量名的默認(rèn)命名規(guī)則。
 * 根據(jù)這個(gè)規(guī)則,我們可以從類(lèi)名自動(dòng)生成默認(rèn)的變量名。
 *
 * @param name 要小寫(xiě)的字符串。
 * @return 小寫(xiě)版本的字符串。
 */
public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
    }
    // 如果字符串的前兩個(gè)字符都是大寫(xiě),那么保持原樣
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
        return name;
    }
    char chars[] = name.toCharArray();
    // 將第一個(gè)字符轉(zhuǎn)為小寫(xiě)
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
}
根據(jù)Java的命名規(guī)則,類(lèi)名的首字母應(yīng)該大寫(xiě),而變量名的首字母應(yīng)該小寫(xiě),它告訴內(nèi)省機(jī)制如何從類(lèi)名生成默認(rèn)的變量名(或者說(shuō)bean名)。 這里可以看到,decapitalize方法接收一個(gè)字符串參數(shù),然后將這個(gè)字符串的首字母轉(zhuǎn)為小寫(xiě),除非這個(gè)字符串的前兩個(gè)字符都是大寫(xiě),這種情況下,字符串保持不變。 所以,在Java內(nèi)省機(jī)制中,如果類(lèi)名的前兩個(gè)字母都是大寫(xiě),那么在進(jìn)行首字母小寫(xiě)的轉(zhuǎn)換時(shí),會(huì)保持原樣不變。也就是說(shuō),對(duì)于這種情況,bean的名稱和類(lèi)名是一樣的。 這種設(shè)計(jì)是為了遵守Java中的命名約定,即當(dāng)一個(gè)詞作為類(lèi)名的開(kāi)始并且全部大寫(xiě)時(shí)(如URL,HTTP),應(yīng)保持其全部大寫(xiě)的格式。

9. Java 的內(nèi)省機(jī)制在生成默認(rèn) bean 名稱中的應(yīng)用

Java內(nèi)省機(jī)制(Introspection)是Java語(yǔ)言對(duì)Bean類(lèi)的一種自我檢查的能力,它屬于Java反射的一個(gè)重要補(bǔ)充。它允許Java程序在運(yùn)行時(shí)獲取Bean類(lèi)的類(lèi)型信息以及Bean的屬性和方法的信息。注意:“內(nèi)省” 發(fā)音是"nèi xǐng"。 內(nèi)省機(jī)制的目的在于提供一套統(tǒng)一的API,可以在運(yùn)行時(shí)動(dòng)態(tài)獲取類(lèi)的各種信息,主要涵蓋以下幾個(gè)方面:

獲取類(lèi)的類(lèi)型信息:可以在運(yùn)行時(shí)獲取任意一個(gè)Bean對(duì)象所屬的類(lèi)、接口、父類(lèi)、修飾符等信息。

屬性信息:可以獲取Bean類(lèi)的屬性的各種信息,如類(lèi)型、修飾符等。

獲取方法信息:可以獲取Bean類(lèi)的方法信息,如返回值類(lèi)型、參數(shù)類(lèi)型、修飾符等。

調(diào)用方法:可以在運(yùn)行時(shí)調(diào)用任意一個(gè)Bean對(duì)象的方法。

修改屬性值:可以在運(yùn)行時(shí)修改Bean的屬性值。

通過(guò)這些反射API,我們可以以一種統(tǒng)一的方式來(lái)操作任意一個(gè)對(duì)象,無(wú)需對(duì)對(duì)象的具體類(lèi)進(jìn)行硬編碼。 在命名規(guī)則上,當(dāng)我們獲取一個(gè)Bean的屬性名時(shí),如果相應(yīng)的getter或setter方法的名稱除去"get"/"set"前綴后,剩余部分的第一個(gè)字母是大寫(xiě)的,那么在轉(zhuǎn)換成屬性名時(shí),會(huì)將這個(gè)字母變?yōu)樾?xiě)。如果剩余部分的前兩個(gè)字母都是大寫(xiě)的,屬性名會(huì)保持原樣不變,不會(huì)將它們轉(zhuǎn)換為小寫(xiě)。 這個(gè)規(guī)則主要是為了處理一些類(lèi)名或方法名使用大寫(xiě)字母縮寫(xiě)的情況。例如,對(duì)于一個(gè)名為 "getURL“的方法,我們會(huì)得到”URL“作為屬性名,而不是”uRL"。 雖然在日常開(kāi)發(fā)中我們可能不會(huì)直接頻繁使用到Java的內(nèi)省機(jī)制,但在一些特定的場(chǎng)景和工具中,內(nèi)省機(jī)制卻發(fā)揮著重要作用:

IDE 和調(diào)試工具:這些工具需要利用內(nèi)省機(jī)制來(lái)獲取類(lèi)的信息,如類(lèi)的層次結(jié)構(gòu)、方法和屬性信息等,以便提供代碼補(bǔ)全、代碼檢查等功能。

測(cè)試框架:例如JUnit這樣的測(cè)試框架需要通過(guò)內(nèi)省機(jī)制來(lái)實(shí)例化測(cè)試類(lèi),獲取測(cè)試方法等信息以進(jìn)行測(cè)試的運(yùn)行。

依賴注入框架:比如Spring等依賴注入框架需要利用內(nèi)省機(jī)制來(lái)掃描類(lèi),獲取類(lèi)中的依賴關(guān)系定義,并自動(dòng)裝配bean。

序列化 / 反序列化:序列化需要獲取對(duì)象的類(lèi)型信息和屬性信息來(lái)實(shí)現(xiàn)對(duì)象狀態(tài)的持久化;反序列化需要利用類(lèi)型信息來(lái)還原對(duì)象。

日志框架:很多日志框架可以通過(guò)內(nèi)省機(jī)制自動(dòng)獲取日志方法所在類(lèi)、方法名等上下文信息。

訪問(wèn)權(quán)限判斷:一些安全相關(guān)的框架需要通過(guò)內(nèi)省判斷一個(gè)成員的訪問(wèn)權(quán)限是否合法。

面向接口編程:內(nèi)省機(jī)制使得在面向接口編程的時(shí)候可以不需要hardcode接口的實(shí)現(xiàn)類(lèi)名,而是在運(yùn)行時(shí)定位。

簡(jiǎn)言之,內(nèi)省機(jī)制的目的是實(shí)現(xiàn)跨類(lèi)的動(dòng)態(tài)操作和信息訪問(wèn),提高運(yùn)行時(shí)的靈活性。這也使得框架在不知道具體類(lèi)的情況下,可以進(jìn)行一些有用的操作。







審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 控制器
    +關(guān)注

    關(guān)注

    114

    文章

    17626

    瀏覽量

    190090
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1669

    瀏覽量

    51060
  • JAVA語(yǔ)言
    +關(guān)注

    關(guān)注

    0

    文章

    138

    瀏覽量

    21340
  • 過(guò)濾器
    +關(guān)注

    關(guān)注

    1

    文章

    442

    瀏覽量

    20830
  • 調(diào)試器
    +關(guān)注

    關(guān)注

    1

    文章

    325

    瀏覽量

    24916

原文標(biāo)題:解鎖Spring組件掃描的新視角

文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    何在實(shí)際電路中使用帶通濾波器

    在本教程中,我們將了解此帶通濾波器、其背后的理論以及如何在實(shí)際電路中使用它。
    的頭像 發(fā)表于 09-08 15:52 ?9149次閱讀
    如<b class='flag-5'>何在</b><b class='flag-5'>實(shí)際</b>電路<b class='flag-5'>中使</b>用帶通濾波器

    何在Linux中使用htop命令

    本文介紹如何在 Linux 中使用 htop 命令。
    的頭像 發(fā)表于 12-04 14:45 ?4018次閱讀
    如<b class='flag-5'>何在</b>Linux<b class='flag-5'>中使</b>用htop命令

    什么是java spring

    。在SSH項(xiàng)目中管理事務(wù)以及對(duì)象的注入Spring是非侵入式的:基于Spring開(kāi)發(fā)的系統(tǒng)中的對(duì)象一般不依賴于Spring的類(lèi)。組成 Spring 框架的每個(gè)模塊(或
    發(fā)表于 09-11 11:16

    何在verilog組件中使用內(nèi)存?

    你好。我正在嘗試(現(xiàn)在)為我的碩士論文寫(xiě)簡(jiǎn)單的Verilog組件,而在做短的LIFO堆棧時(shí),我遇到了問(wèn)題,因?yàn)椋骸皹?gòu)建錯(cuò)誤:不支持內(nèi)存聲明”不管怎樣,為了避免這個(gè)問(wèn)題,例如,你可以在PSoC 5LP中使用不知何故的RAM構(gòu)建,以使這個(gè)
    發(fā)表于 08-22 12:51

    Spring筆記分享

    ; 可以管理所有的組件(類(lèi))Spring的優(yōu)良特性1) 非侵入式:基于Spring開(kāi)發(fā)的應(yīng)用中的對(duì)象可以不依賴于Spring的API2) 依
    發(fā)表于 11-04 07:51

    何在java代碼中使用HTTP代理IP

    何在java代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 15:38 ?2775次閱讀

    何在python代碼中使用HTTP代理IP

    何在python代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 15:46 ?1750次閱讀

    何在PHP代碼中使用HTTP代理IP

    何在PHP代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 16:08 ?2933次閱讀

    go語(yǔ)言代碼中使用HTTP代理IP的方法

    何在go語(yǔ)言代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 08-04 16:13 ?3715次閱讀

    何在易e語(yǔ)言代碼中使用HTTP代理IP

    何在易e語(yǔ)言代碼中使用HTTP代理IP,示例代碼demo直接可用(步驟注釋清晰)
    的頭像 發(fā)表于 08-05 16:29 ?7653次閱讀

    何在c語(yǔ)言代碼中使用HTTP代理IP

    何在c語(yǔ)言代碼中使用HTTP代理IP,示例代碼demo直接可用(步驟注釋清晰)
    的頭像 發(fā)表于 08-05 16:31 ?2831次閱讀

    何在c#語(yǔ)言代碼中使用HTTP代理IP

    何在c#語(yǔ)言代碼中使用HTTP代理IP,示例代碼demo直接可用(步驟注釋清晰)
    的頭像 發(fā)表于 08-05 16:33 ?3314次閱讀

    何在python代碼中使用HTTP代理IP

    如何再python代碼中使用HTTP代理IP。
    的頭像 發(fā)表于 09-13 09:25 ?1486次閱讀

    何在Arduino中使用LDR

    電子發(fā)燒友網(wǎng)站提供《如何在Arduino中使用LDR.zip》資料免費(fèi)下載
    發(fā)表于 10-31 09:50 ?0次下載
    如<b class='flag-5'>何在</b>Arduino<b class='flag-5'>中使</b>用LDR

    何在測(cè)試中使用ChatGPT

    Dimitar Panayotov 在 2023 年 QA Challenge Accepted 大會(huì) 上分享了他如何在測(cè)試中使用 ChatGPT。
    的頭像 發(fā)表于 02-20 13:57 ?1315次閱讀