Spring的雜談下

SpringBean的生命周期

??有關(guān)Bean的生命周要從他們的作用域來區(qū)分。所謂的生命周期就是從 創(chuàng)建->使用->銷毀

??singlton(單例):從Spring 容器的創(chuàng)建到Spring容器的銷毀。(如果是延時加載,在對象使用前創(chuàng)建對象。)

??prototype(原型):在調(diào)用前創(chuàng)建,使用后銷毀。

SpringBean是線程安全的嗎,其作用域是什么?

??一文搞定SpringBean線程安全,作用域

在 Spring 中注入一個 Java 集合

Spring 提供了以下四種集合類的配置元素:
1: 該標(biāo)簽用來裝配可重復(fù)的 list 值。
2: 該標(biāo)簽用來裝配沒有重復(fù)的 set 值。
3: 該標(biāo)簽可用來注入鍵和值可以為任何類型的鍵值對。
4: 該標(biāo)簽支持注入鍵和值都是字符串類型的鍵值對。

<beans>
    <!-- Definition for javaCollection -->
    <bean id="javaCollection" class="com.gupaoedu.JavaCollection">
    
        <!-- java.util.List -->
        <property name="customList">
        <list>
        <value>INDIA</value>
        <value>Pakistan</value>
        <value>USA</value>
        <value>UK</value>
        </list>
        </property>
        
        <!-- java.util.Set -->
        <property name="customSet">
        <set>
        <value>INDIA</value>
        <value>Pakistan</value>
        <value>USA</value>
        <value>UK</value>
        </set>
        </property>
        
        <!-- java.util.Map -->
        <property name="customMap">
        <map>
        <entry key="1" value="INDIA"/>
        <entry key="2" value="Pakistan"/>
        <entry key="3" value="USA"/>
        <entry key="4" value="UK"/>
        </map>
        </property>
        
        <!-- java.util.Properties -->
        <property name="customProperies">
        <props>
        <prop key="admin">admin@gupaoedu.com</prop>
        <prop key="support">support@gupaoedu.com</prop>
        </props>
        </property>
    
    </bean>
</beans>

Spring的五種裝配方式

??no:這是 Spring 框架的默認(rèn)設(shè)置,在該設(shè)置下自動裝配是關(guān)閉的,開發(fā)者需要自行在 bean 定義中用標(biāo)簽明確的設(shè)置依賴關(guān)系。

??byName:該選項(xiàng)可以根據(jù) bean名稱設(shè)置依賴關(guān)系。當(dāng)向一個 bean中自動裝配一個屬性時,IOC容器將根據(jù) bean 的名稱自動在對應(yīng)的容器中獲取,如果找到就返回,沒有找到就報(bào)錯。

??byType:該選項(xiàng)可以根據(jù) bean 類型設(shè)置依賴關(guān)系。當(dāng)向一個 bean中自動裝配一個屬性時,IOC容器將根據(jù) bean 的名稱自動在對應(yīng)的容器中獲取,如果找到就返回,沒有找到就報(bào)錯。

??constructor:構(gòu)造器的自動裝配和 byType 模式類似,但是僅僅適用于與有構(gòu)造器相同參數(shù)的 bean,如果在容器中沒有找到與構(gòu)造器參數(shù)類型一致的 bean,那么將會拋出異常。

??autodetect:該模式自動探測使用構(gòu)造器自動裝配或者 byType 自動裝配。首先,首先會嘗試找合適的帶參數(shù)的構(gòu)造器,如果找到的話就是用構(gòu)造器自動裝配,如果在 bean 內(nèi)部沒有找到相應(yīng)的構(gòu) 造器或者是無參構(gòu)造器,容器就會自動選擇 byTpe 的自動裝配方式。

如何開啟基于注解的自動裝配?

??1.引入配置文件中的下引入

<beans>
        <context:annotation-config />
</beans>

??在 bean 配置文件中直接引入 AutowiredAnnotationBeanPostProcessor

<beans>
<bean
class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProc
essor"/>
</beans>

自動裝配有哪些局限性?

??重寫:你仍然需要使用 和< property>設(shè)置指明依賴,這意味著總要重寫自動裝配。
??原生數(shù)據(jù)類型:你不能自動裝配簡單的屬性,如原生類型、字符串和類。
??模糊特性:自動裝配總是沒有自定義裝配精確,因此,如果可能盡量使用自定義裝配。

請舉例解釋@Required 注入

??主要用于校驗(yàn)?zāi)硞€bean的特定屬性是否被正確的設(shè)置。如下:

    public class EmployeeFactoryBean extends AbstractFactoryBean<Object>{
        private String designation;
        public String getDesignation() {
            return designation;
        }
        @Required
        public void setDesignation(String designation) {
            this.designation = designation;
        }
    }

??RequiredAnnotationBeanPostProcessor 是 Spring 中的后置處理用來驗(yàn)證被@Required 注解的 bean 屬性是否被正確的設(shè)置了。如果沒有找到對應(yīng)的配置則會拋出BeanInitializationException異常。

設(shè)置注入和構(gòu)造注入

區(qū)別:注入的順序、注入的時機(jī)、注入的有限性、循環(huán)依賴等問題。

??1.在設(shè)值注入方法支持大部分的依賴注入,如果我們僅需要注入int、string和long型的變量,我們不要用設(shè)值的方法注入。對于基本類型,如果我們沒有注入的話,可以為基本類型設(shè)置默認(rèn)值。
??在構(gòu)造方法注入不支持大部分的依賴注入,因?yàn)樵谡{(diào)用構(gòu)造方法中必須傳入正確的構(gòu)造參數(shù),否則的話為報(bào)錯。

??2.設(shè)值注入不會重寫構(gòu)造方法的值。如果我們對同一個變量同時使用了構(gòu)造方法注入又使用了設(shè)置方法注入的話,那么構(gòu)造方法將不能覆蓋由設(shè)值方法注入的值。很明顯,因?yàn)闃?gòu)造方法盡在對象被創(chuàng)建時調(diào)用。

??3.在使用設(shè)值注入時有可能還不能保證某種依賴是否已經(jīng)被注入,也就是說這時對象的依賴關(guān)系有可能是不完整的。而在另一種情況下,構(gòu)造器注入則不允許生成依賴關(guān)系不完整的對象。

??4.在設(shè)值注入時如果對象A和對象B互相依賴,在創(chuàng)建對象A時Spring會拋出ObjectCurrentlyInCreationException異常,因?yàn)樵贐對象被創(chuàng)建之前A對象是不能被創(chuàng)建的,反之亦然。所以Spring用設(shè)值注入的方法解決了循環(huán)依賴的問題,因?qū)ο蟮脑O(shè)值方法是在對象被創(chuàng)建之前被調(diào)用的。

Spring 框架中有哪些不同類型的事件?

??五種:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。

ContextRefreshedEvent

??該事件會在 ApplicationContext 被初始化或者更新時發(fā)布。也可以在調(diào)用 ConfigurableApplicationContext 接口中的 refresh()方法時被觸發(fā)。

ContextStartedEvent

??當(dāng)容器調(diào)用 ConfigurableApplicationContext的Start()方法開始/重新開始容器時觸發(fā)該事件。

ContextStoppedEvent

??當(dāng)容器調(diào)用 ConfigurableApplicationContext的Stop()方法停止容器時觸發(fā)該事件。

ContextClosedEvent

??當(dāng) ApplicationContext 被關(guān)閉時觸發(fā)該事件。容器被關(guān)閉時,其管理的所有單例 Bean 都被銷毀。

RequestHandledEvent

??在 Web 應(yīng)用中,當(dāng)一個 http 請求(request結(jié)束觸發(fā)該事件)

還可以通過擴(kuò)展 ApplicationEvent 類來開發(fā)自定義的事件。

定義
    public class CustomApplicationEvent extends ApplicationEvent{
        public CustomApplicationEvent ( Object source, final String msg ){
            super(source);
            System.out.println("Created a Custom event");
        }
    }
監(jiān)聽

    public class CustomEventListener implements ApplicationListener <
            CustomApplicationEvent >{
        @Override
        public void onApplicationEvent(CustomApplicationEvent applicationEvent) {
            //handle event
        }
    }
發(fā)布

CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext,
“Test message”);
applicationContext.publishEvent(customEvent);

FileSystemResource 、 ClassPathResource 、ServletContextResource有何區(qū)別?

FileSystemResource

??FileSystemResource 是 Spring 提供的資源訪問類。FileSystemResource 類相比其他兩個資源訪問類,沒有什么優(yōu)勢,它只是在 File 類的基礎(chǔ)上略作封裝。

/*默認(rèn)從文件系統(tǒng)的當(dāng)前路徑加載xttblog.xml資源*/
FileSystemResource fsr = new FileSystemResource("xttblog.xml");

類圖


FileSystemResource

部分源碼


public class FileSystemResource extends AbstractResource implements WritableResource {

    private final File file;

    private final String path;
    public FileSystemResource(File file) {
        Assert.notNull(file, "File must not be null");
        this.file = file;
        this.path = StringUtils.cleanPath(file.getPath());
    }
    public FileSystemResource(String path) {
        Assert.notNull(path, "Path must not be null");
        this.file = new File(path);
        this.path = StringUtils.cleanPath(path);
    }

    /**
     * Return the file path for this resource.
     */
    public final String getPath() {
        return this.path;
    }

    /**
     * This implementation returns whether the underlying file exists.
     * @see java.io.File#exists()
     */
    @Override
    public boolean exists() {
        return this.file.exists();
    }

        .....
}

??FileSystemResource 可以看出有兩種構(gòu)造,一種是文件,一種是字符串。

ClassPathResource

??利用ClassPathResource讀取xml配置的基本思路就是通過構(gòu)造函數(shù)傳入的文件路徑,接著交給class或者classLoader,調(diào)用getResourceAsStream獲取到InputStream。

FileSystemResource 和 ClassPathResource 的用法如下:



public class Test {


    String filePath = "D:/com/xttblog.txt";
    //使用系統(tǒng)文件路徑方式加載文件
    Resource res1 = new FileSystemResource(filePath);
    //使用類路徑方式加載文件
    Resource res2 = new ClassPathResource("conf/xttblog.txt");
    InputStream ins1 = res1.getInputStream();
    InputStream ins2 = res2.getInputStream();
    getFileName();//獲取文件名
    getFile();//獲取資源對應(yīng)的File對象
    getInputStream();//獲取文件的輸入流
    createRelative(String relativePath);//在相對地址創(chuàng)建新文件

    EncodedResource encRes = new EncodedResource(res, "UTF-8");
    String content = FileCopyUtils.copyToString(encRes.getReader());

    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource resources[] = resolver.getResources("classpath*://com/***/*.xml");
    For(Resource resource : resources){
        System.out.println(resource.getDescription);
    }


}


類圖
image.png
源碼
public class ClassPathResource extends AbstractFileResolvingResource {

    private final String path;

    @Nullable
    private ClassLoader classLoader;

    @Nullable
    private Class<?> clazz;

    public ClassPathResource(String path) {
        this(path, (ClassLoader) null);
    }

    
    public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
        Assert.notNull(path, "Path must not be null");
        String pathToUse = StringUtils.cleanPath(path);
        if (pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }
        this.path = pathToUse;
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
    }

    
    public ClassPathResource(String path, @Nullable Class<?> clazz) {
        Assert.notNull(path, "Path must not be null");
        this.path = StringUtils.cleanPath(path);
        this.clazz = clazz;
    }

    
    @Deprecated
    protected ClassPathResource(String path, @Nullable ClassLoader classLoader, @Nullable Class<?> clazz) {
        this.path = StringUtils.cleanPath(path);
        this.classLoader = classLoader;
        this.clazz = clazz;
    }


    /**
     * Return the path for this resource (as resource path within the class path).
     */
    public final String getPath() {
        return this.path;
    }

    /**
     * Return the ClassLoader that this resource will be obtained from.
     */
    @Nullable
    public final ClassLoader getClassLoader() {
        return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
    }


    @Override
    public boolean exists() {
        return (resolveURL() != null);
    }

    
    @Nullable
    protected URL resolveURL() {
        if (this.clazz != null) {
            return this.clazz.getResource(this.path);
        }
        else if (this.classLoader != null) {
            return this.classLoader.getResource(this.path);
        }
        else {
            return ClassLoader.getSystemResource(this.path);
        }
    }

    
    @Override
    public InputStream getInputStream() throws IOException {
        InputStream is;
        if (this.clazz != null) {
            is = this.clazz.getResourceAsStream(this.path);
        }
        else if (this.classLoader != null) {
            is = this.classLoader.getResourceAsStream(this.path);
        }
        else {
            is = ClassLoader.getSystemResourceAsStream(this.path);
        }
        if (is == null) {
            throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
        }
        return is;
    }

    
    @Override
    public URL getURL() throws IOException {
        URL url = resolveURL();
        if (url == null) {
            throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
        }
        return url;
    }

    
    @Override
    public Resource createRelative(String relativePath) {
        String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
        return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) :
                new ClassPathResource(pathToUse, this.classLoader));
    }

    @Override
    @Nullable
    public String getFilename() {
        return StringUtils.getFilename(this.path);
    }

    @Override
    public String getDescription() {
        StringBuilder builder = new StringBuilder("class path resource [");
        String pathToUse = path;
        if (this.clazz != null && !pathToUse.startsWith("/")) {
            builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
            builder.append('/');
        }
        if (pathToUse.startsWith("/")) {
            pathToUse = pathToUse.substring(1);
        }
        builder.append(pathToUse);
        builder.append(']');
        return builder.toString();
    }


    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof ClassPathResource) {
            ClassPathResource otherRes = (ClassPathResource) obj;
            return (this.path.equals(otherRes.path) &&
                    ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
                    ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
        }
        return false;
    }

    
    @Override
    public int hashCode() {
        return this.path.hashCode();
    }

}

區(qū)別如下:

ClassPathResource:從系統(tǒng)的類路徑中加載
FileSystemResource:從文件系統(tǒng)加載,比如說自己指定配置文件的全路徑
InputStreamResource:從輸入流中加載
ServletContextResource:從Servlet 上下文環(huán)境中加載
UrlResource:從指定的Url加載

Class.getResource("")獲取的是相對于當(dāng)前類的相對路徑。Class.getResource("/")獲取的是classpath的根路徑。ClassLoader.getResource("")獲取的是classpath的根路徑。

在創(chuàng)建ClassPathResource對象時,我們可以指定是按Class的相對路徑獲取文件還是按ClassLoader來獲取。

FileSystemResource 效果類似于Java中的File
ClassPathResource 效果類似于this.getClass().getResource("/").getPath();
ServletContextResource 效果類似于request.getServletContext().getRealPath("");

Spring 框架中都用到了哪些設(shè)計(jì)模式?

很多很多很多

1、代理模式:在 AOP 和 remoting 中被用的比較多。
2、單例模式:在 spring 配置文件中定義的 bean 默認(rèn)為單例模式。
3、模板模式:用來解決代碼重復(fù)的問題。比如. RestTemplate, JmsTemplate, JpaTemplate。
4、委派模式:Spring 提供了 DispatcherServlet 來對請求進(jìn)行分發(fā)。
5、工廠模式:BeanFactory 用來創(chuàng)建對象的實(shí)例,貫穿于 BeanFactory / ApplicationContext
接口的核心理念。
6、代理模式:AOP 思想的底層實(shí)現(xiàn)技術(shù),Spring 中采用 JDK Proxy 和 CgLib 類庫。

Spring5 新特性

1、依賴 JDK 8+和 Java EE7+以上版本
2、首次采用反應(yīng)式編程模型
3、支持使用注解進(jìn)行編程
4、新增函數(shù)式編程
5、支持使用 REST 斷點(diǎn)執(zhí)行反應(yīng)式編程
6、支持 HTTP 2.0
7、新增 Kotlin 和 Spring WebFlux
8、可使用 Lambda 表達(dá)式注冊 Bean
9、Spring WebMVC 支持最新的 API
10、使用 JUnit5 執(zhí)行條件和并發(fā)測試
11、使用 Spring WebFlux 執(zhí)行集成測試
12、核心容器優(yōu)化

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。