《精通Spring 4.X企業應用開發實戰》讀書筆記1-1(IoC容器和Bean)

   很長一段時間關注在Java Web開發的方向上,說起到Jave Web開發就繞不開Spring全家桶系列,使用面向百度,谷歌的編程方法可以完成大部分的工做。可是這種不繫統的瞭解總以爲本身的知識有所欠缺。因此有了系統瞭解Spring的想法,瞭解了Spring,纔可以更好的學習Spring全家桶系列,Spring的書籍也是琳琅滿目,固然也能夠閱讀Spring官方的reference,相信那個纔是最好的材料,可是鑑於英語的閱讀速度有限。因此就挑選了這本《精通Spring 4.X企業應用開發實戰》。這裏記錄一下讀書筆記,主要是方便之後快速的查閱~html

  以前零碎的信息瞭解到Spring的兩大基礎其實就是 IoC和AOP了,最近花了好久的時間閱讀了《精通Spring 4.X企業應用開發實戰》關於 DI 的這部份內容。仍是瞭解了很多內容,固然也產生了很多的疑問。java

 

IoC容器web

  IoC(Inversion of Control,控制反轉),另一個詞DI(Dependency Injection,依賴注入),在Spring中能夠把這兩個詞等價起來,糾結這些概念我以爲沒有必要,個人理解,它就是一種成熟的軟件設計模式,可以實現軟件開發的高內聚,低耦合。系統在改動,擴展起來可以輕鬆應對。網絡上面不少關於這個概念的解釋,他們也都舉例說明了它的好處,可是都是很小的實例,不是一個複雜的系統對於減輕應對改動、擴展的功效印象並不深入。因此仍是應該潛心碼代碼,當本身碼的代碼到達必定的規模,天然而然應該就會了解到IoC的概念,設計模式們的優雅之處了。 面試

  用書中的一句話來描述,某一接口具體實現類的選擇控制權從調用類中移除,轉交給第三方決定(一頭霧水)。簡單點說由Spring容器集中管理實現類,須要該實現類的時候由Spring容器根據名稱或類型動態的注入該實現類。spring

  從注入方法上區分,IoC主要包含構造函數注入、屬性注入和接口注入;Spring支持構造函數注入和屬性注入。Spring容器經過xml配置文件、註解描述、JavaConfig、Groovy DSL四種方式對實現類的信息及其它們之間的依賴關係進行描述,目前比較經常使用的應該是基於註解,而後配合JavaConfig的方式(Spring boot在我看來是一個anti-xml的產品,最近它比較流行,xml配置文件的方式在spring boot中有點顯得格格不入),不過《精通Spring 4.X企業應用開發實戰》這本書不太好的地方就在於,書中仍是保留了大量的基於xml配置文件的介紹,不過也是瞭解一下歷史吧(Spring由於一直向下保持兼容,致使即便Spring boot如今流行起來,網上仍是大量的關於Spring xml配置的介紹,在推廣註解、Java Config的方向上仍是阻力重重。這也是以前看到相對於Guice這種後起的IoC框架,Spring的不足之處)。編程

  說到這裏其實我以前面試的時候被問到一個問題:Spring有什麼優缺點,咱們看Spring的書,滿滿的都是Spring的優勢,輕量級的框架,當初就是由於EJB太笨重纔有的Spring;依賴注入幫助咱們設計出低耦合的程序;AOP特性使得咱們寫出異常簡潔的代碼,java web程序幾乎離不開這個框架的身影,如今Spring已然是全家桶的解決方案,原有的SSH框架到如今的SSM輕量級框架,其中Spring,SpringMVC都屬於Spring社區的,Spring的優勢太多了,我也只是依照我我的理解簡述了一下。缺點呢?前段時間看到一篇對比Spring和Guice的,角度挺好的,Spring雖然隨着發展,擁抱了註解、java配置的形式對bean進行描述,可是依然保留了xml形式的bean定義,致使如今網上搜索資料不少都是xml形式的解答,這樣使得社區變化的比較慢。可是依然推薦使用Spring,由於相對於Guice,Spring有一個更加優秀的社區,你遇到問題可以搜索到不少的Spring解答,社區也活躍。若是你使用Guice,遇到了問題社區相對於Spring活躍度就大打折扣了。設計模式

  

Java反射(這個知識自己很基礎,聽說效率不高,可是幫助你理解Spring框架,xml的bean聲明都是反射生成的吧?)數組

  Java中有一個特殊的類,Class,每一個Class內部都有一個Class類,是否是很拗口。可是確實是這樣,這是一個特殊的類,它包含類的全部信息;全部的構造函數,全部的Field,全部的Method...能夠動態的建立對象;用代碼展現比較合適(很容易理解,就是異常聲明的有點多)。tomcat

package com.test.spider;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class JavaRefection {
	public static class MockClass {
		private String firstName;
		private String secondName;	
		public String getFirstName() {
			return this.firstName;
		}
		
		public void setFirstName(String firstName) {
			this.firstName = firstName;
		}

		@Override
		public String toString() {
			return "MockClass [firstName=" + firstName + ", secondName=" + secondName + "]";
		}
	}
	
	public static void main(String[] args) throws 
								ClassNotFoundException, 
								InstantiationException, 
								IllegalAccessException, 
								NoSuchMethodException, 
								SecurityException, 
								IllegalArgumentException, 
								InvocationTargetException 
	{
		Class c1 = MockClass.class;
		Class c2 = Class.forName("com.test.spider.JavaRefection$MockClass");
		Class c3 = new MockClass().getClass();
		//check that 3 methods obtain same class instance
		System.out.println("Whether T.class is same with Class.forName():" 
							+ String.valueOf(c1 == c2) + "\n"
							+ "Whether Class.forName is same with object.getClass():" 
							+ String.valueOf(c2 == c3));
		//using nullary constructor
		MockClass mockClass1 = (MockClass)c1.newInstance();
		//another method, first get Constructor
		Constructor<MockClass> constructor = c1.getConstructor(new Class[]{});
		MockClass mockClass2 = (MockClass)constructor.newInstance(new Object[]{});
		//get Method
		Method[] methods = c1.getDeclaredMethods();
		for(Method method : methods) {
			System.out.println(method.getName());
		}
		//get Field
		Field[] fields = c1.getDeclaredFields();
		for(Field field : fields) {
			System.out.println(field.getName());
		}
		fields[0].setAccessible(true);
		fields[0].set(mockClass2, "Hello");
		fields[1].setAccessible(true);
		fields[1].set(mockClass2, "World!");
		System.out.println(mockClass2.toString());
		//invoke method
		System.out.println(methods[1].invoke(mockClass2, new Object[]{}));
		System.out.println(methods[1].invoke(mockClass1, new Object[]{}));
	}
}

  用書中正式的話描述:每一個類在JVM中都擁有一個對應的java.lang.Class對象,提供類結構信息的描述。數組、枚舉、註解及基本的Java類型(int,double等),甚至void都擁有對應的Class對象。Class沒有public的構造方法。Class對象是在裝載類時由JVM經過調用類裝載器中的defineClass()方法自動構造的。網絡

		//for classLoader
		//全盤負責委託機制(委託機制防止自定義的ClassLoader惡意加載基礎類)
		ClassLoader loader = c1.getClassLoader();
		System.out.println("currentLoad:"+ loader);
		System.out.println("parent:" + loader.getParent());
		System.out.println("grandParent:" + loader.getParent().getParent());
		System.out.println(loader.getResource("java/net/URL.class"));
		System.out.println(loader.getResource("java/lang/String.class"));

  輸出:

currentLoad:sun.misc.Launcher$AppClassLoader@73d16e93
parent:sun.misc.Launcher$ExtClassLoader@6d06d69c
grandParent:null
jar:file:/G:/Develop/JDK1.8/jre/lib/rt.jar!/java/net/URL.class
jar:file:/G:/Develop/JDK1.8/jre/lib/rt.jar!/java/lang/String.class
jar:file:/G:/Develop/JDK1.8/jre/lib/rt.jar!/java/net/URL.class

  ClassLoader,(1)裝載:查找和導入class文件(2)連接:執行校驗(檢查class文件正確性)、準備(靜態變量分配存儲空間)和解析步驟(符號引用轉換成直接飲用),其中解析步驟是可選的;(3)初始化:對類的靜態變量,靜態代碼塊執行初始化工做;

  幾個ClassLoader的關係,以及Class、Object、ClassLoader的關係:

  

 

  介紹反射的概念仍是由於Spring中很多地方應該都用到了反射的知識,我我的理解,xml中聲明bean的時候須要指明class屬性,其中須要使用全類名,此時我相信Spring建立這個bean對象的時候使用的就是反射的技術。首先使用Class.forName("")獲取到其類型,而後newInstance()或者獲取到構造函數,使用有參數的構造函數;還有一個能想到的使用反射的地方。目前注入bean的時候能夠在類的private屬性上面添加@Autowired註釋注入bean,即便沒有相關的set方法,這裏必定使用了反射技術,否則private類型是沒法被外部的對象訪問的,反射能夠動態調整屬性的訪問類別,而後直接設置屬性;應該還有不少地方。

 

IoC容器

  話題繼續切回IoC容器,Spring中的IoC容器,BeanFactory、ApplicationContext,ApplicationContext相比BeanFactory有更強大的功能,通常直接使用ApplicationContext;

BeanFactory

  最初接觸Spring的時候,不太能理解Bean是什麼概念,有時候又看到JavaBean,最初JavaBean只是一個可複用的Java類,有必定的條件,好比無參構造函數、setter,getter獲取屬性以及可序列化,而後發展爲後來的EJB(企業級JavaBean),爲了簡化企業級開發,就有了Spring框架,Spring裏面的Bean就比較寬泛了,能被Spring容器實例化的Java類均可以成爲Bean,因此幾乎全部的類均可以是Bean。固然也能夠不糾結這些概念。

  XmlBeanDefinitionReader和DefaultListableBeanFactory,繼承圖如上圖。(Idea真的挺好用的,Ctrl+N能夠搜索到想要的類,而後生成類圖,固然也能夠看代碼),經過Idea查看接口、類擁有什麼方法最方便了,反正能夠看出從BeanFactory,ListableBeanFactory,HierarchicalBeanFactory,ConfigurableBeanFactory(類裝載器,屬性編輯器,容器初始化後置處理器等),AutowireCapableBeanFactory功能逐漸增長的;

<?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="car" class="com.smart.beanfactory.Car">
        <property name="brand" value="redCA72"></property>
        <property name="color" value="black"></property>
        <property name="maxSpeed" value="200"></property>
    </bean>
</beans>

  這裏不得不說Idea的xml代碼提示也挺好用的。

public class BeanFactoryTest {
    @Test
    public void getBean() throws Exception {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("classpath:beans.xml");
        System.out.println(resource.getURL());
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);
        xmlBeanDefinitionReader.loadBeanDefinitions(resource);

        Car car = (Car)factory.getBean("car");
        System.out.println(car.toString());
    }
}

  

ApplicationContext

  以上是最基本的ApplicationContext接口的類圖

 

  Lifecycle這個接口好像tomcat的設計中也有相似的設計,tomcat的Lifecyle負責容器的生命週期?

 

  主要使用的4個類

ClassPathXmlApplicationContext,構造參數中的路徑classpath:...
FileSystemXmlApplicationContext,構造參數中的路徑file:...
AnnotationConfigApplicationContext,基於@Configuration註解bean以及@Component方式聲明Bean支持
GenericGroovyApplicationContext,基於Groovy聲明Bean的方式 

  第三種目前使用的最多,SpringBoot中推崇的方式,Xml的方式遺留系統中使用較多;

WebApplicationContext

   WebApplicationContext的初始化方式不一樣於BeanFactory、ApplicationContext,由於其須要ServletContext實例。web.xml中配置自啓動的Servlet或自定義Web容器監聽器(ServletContextListener),藉助而這種任何一個,完成啓動Spring Web應用上下文的工做。

   ContextLoaderServlet和ContextLoaderListener分別是Spring提供的用於啓動WebApplicationContext的Servlet和Web容器的監聽器。

  二者的內部都實現了啓動WebApplicationContext實例的邏輯,根據Web容器具體狀況進行選擇,web.xml中進行配置便可。

  經過Web容器監聽器引導

...  
    <!-- 指定配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/smart-dao.xml, /WEB-INF/smart-service.xml
        </param-value>
    </context-param>

    <!-- 聲明web容器監聽器 -->
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
...

  不支持監聽器的低版本web容器,採用自啓動的Servlet;

...
    <!-- 指定配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/smart-dao.xml, /WEB-INF/smart-service.xml
        </param-value>
    </context-param>

    <!-- 聲明自啓動的servlet -->
    <servlet>
    	<servlet-name>springContextLoaderServlet</servlet-name>
    	<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
    	<load-on-startup>1</load-on-startup>
    </servlet>
...

  

Bean的生命週期

  Bean的完整生命週期從Spring容器着手實例化Bean開始,知道最終銷燬Bean,其中通過了許多關鍵點,每一個關鍵點都設計特定的方法調用,方法主要分爲4個種類:

  1. Bean自身的方法:如調用Bean構造函數實例化Bean、調用Setter設置Bean的屬性以及經過Bean的init-method和destroy-method所指定的方法

  2.Bean級生命週期接口方法:如BeanAware、BeanFactoryAware、InitializingBean和DisposableBean,這些接口方法由Bean類實現

  3.容器級別生命週期接口方法:InstantiationAwareBeanPostProcessor和BeanPostProcessor這兩個接口實現,通常稱它們的實現類爲「後處理器」。後處理器接口通常不禁Bean自己實現,它們獨立於Bean,實現類以容器附加裝置的形式註冊到Spring容器中,並經過接口反射未Spring容器掃描識別。當Spring容器建立任何Bean的時候,這些後處理器就會發生做用,因此這些後處理器的影響是全局性的。

  4.工廠後處理器接口方法:包括AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor等方法。工廠後處理器也是容器級的,在應用上下文裝配配置文件後當即調用。

  Bean級生命週期和容器級生命週期接口是個性和共性的結合,Bean級別解決了個性化處理問題,容器級解決了某些Bean共性化處理的問題,Spring容器中能夠註冊多個後處理器,他們同時實現org.springframework.core.Ordered接口便可。

BeanFactory

  

public class BeanFactoryTest {
    @Test
    public void getBean() throws Exception {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource resource = resolver.getResource("classpath:beans.xml");
        System.out.println(resource.getURL());
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);
        xmlBeanDefinitionReader.loadBeanDefinitions(resource);

        factory.addBeanPostProcessor(new BeanPostProcessor1());
        factory.addBeanPostProcessor(new BeanPostProcessor2());
        factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor1());
        factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor2());

        Car car = (Car)factory.getBean("car");
        System.out.println(car.toString());
factory.destorySingletons(); } } public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean{ private String brand; private String color; private String maxSpeed; public String getBrand() { return brand; } public String getColor() { return color; } public String getMaxSpeed() { return maxSpeed; } public void setBrand(String brand) { this.brand = brand; } public void setColor(String color) { this.color = color; } public void setMaxSpeed(String maxSpeed) { this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car{" + "brand='" + brand + '\'' + ", color='" + color + '\'' + ", maxSpeed='" + maxSpeed + '\'' + '}'; } private BeanFactory beanFactory; private String beanName; public void selfInit() { System.out.println("init-method指定的方法的調用。"); } public void selfDestory() { System.out.println("destory-method指定的方法的調用。"); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("調用BeanFactoryAware接口的setBeanFactory方法。"); this.beanFactory = beanFactory; } @Override public void setBeanName(String s) { System.out.println("調用BeanNameAware接口的setBeanName方法。參數:" + s); this.beanName = s; } @Override public void destroy() throws Exception { System.out.println("調用DisposableBean接口的destory方法。"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("調用InitializingBean接口的afterPropertiesSet方法。"); } } public class BeanPostProcessor1 implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("調用BeanPostProcessor0接口的postProcessBeforeInitialization方法:" + s); return o; } @Override public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("調用BeanPostProcessor0接口的postProcessAfterInitialization方法:" + s); return o; } @Override public int getOrder() { return 0; } } public class BeanPostProcessor2 implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("調用BeanPostProcessor1接口的postProcessBeforeInitialization方法:" + s); return o; } @Override public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("調用BeanPostProcessor1接口的postProcessAfterInitialization方法:" + s); return o; } @Override public int getOrder() { return 1; } } public class MyInstantiationAwareBeanPostProcessor1 extends InstantiationAwareBeanPostProcessorAdapter implements Ordered { @Override public int getOrder() { return 0; } public Object postProcessBeforeInstantiation(Class<?> var1, String var2) throws BeansException { System.out.println("調用InstantiationAwareBeanPostProcessor0接口的postProcessBeforeInstantiation方法:" + var2); return null; } public boolean postProcessAfterInstantiation(Object var1, String var2) throws BeansException { System.out.println("調用InstantiationAwareBeanPostProcessor0接口的postProcessAfterInstantiation方法:" + var2); return true; } public PropertyValues postProcessPropertyValues(PropertyValues var1, PropertyDescriptor[] var2, Object var3, String var4) throws BeansException { System.out.println("調用InstantiationAwareBeanPostProcessor0接口的postProcessPropertyValues方法:" + var4); return var1; } } public class MyInstantiationAwareBeanPostProcessor2 extends InstantiationAwareBeanPostProcessorAdapter implements Ordered { @Override public int getOrder() { return 1; } public Object postProcessBeforeInstantiation(Class<?> var1, String var2) throws BeansException { System.out.println("調用InstantiationAwareBeanPostProcessor1接口的postProcessBeforeInstantiation方法:" + var2); return null; } public boolean postProcessAfterInstantiation(Object var1, String var2) throws BeansException { System.out.println("調用InstantiationAwareBeanPostProcessor1接口的postProcessAfterInstantiation方法:" + var2); return true; } public PropertyValues postProcessPropertyValues(PropertyValues var1, PropertyDescriptor[] var2, Object var3, String var4) throws BeansException { System.out.println("調用InstantiationAwareBeanPostProcessor1接口的postProcessPropertyValues方法:" + var4); return var1; } }

  

 

  Spring提倡的是非侵入式編程,以上其實框架代碼和業務代碼融合了;因此在應用編程的時候,儘可能少用4個Bean生命週期接口類,使用init-method,destory-method,或者支持@PostConstruct,@PreDestory的InitDestoryAnnotationBeanPostProcessor(ApplicationContext默認裝載)這些不入侵代碼的編程方式。

ApplicationContext

  其與BeanFactory中的bean的生命週期一點不一樣在於生命週期中多了一些接口調用;

  另一點最大的不一樣時ApplicationContext利用Java反射機制自動識別出配置文件中定義的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,而且自動將他們註冊到ApplicationContext中;BeanFactory的代碼中展現須要手動調用addBeanPostProcessor()等方法註冊;

  因此應用中使用ApplicationContext更加方便;

  

相關文章
相關標籤/搜索