Yaratıcı JingQ Java Geek Technology
Görüntülenen kod bazı temel yöntemleri çıkarır, bazı varsayılan ayarları ve günlük çıktısını kaldırır ve ayrıca hata istisnalarının çoğunu kaldırır.Ayrıntılı kodu, yorumları ve demoları görmek istiyorsanız, yüklediğim not projesini indirebilirsiniz.
Code Cloud Gitee adresi https://gitee.com/vip-augus/spring-analysis-note.git
Github adresi https://github.com/Vip-Augus/spring-analysis-note
Kaynak kodunu okuma sürecinde, tasarımcının tasarım fikirlerini anlayın ve ondan öğrenin ve temel bir bahar anlayışına sahip olun.
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext'in miras mimarisi diyagramı:
Bu tür bir yapı diyagramı, IDEA düzenleyicisinin Diyagramlar işlevi aracılığıyla görüntülenir. Geçerli sınıfa sağ tıklayın ve kalıtım sistemini, hangi sınıfların miras alındığını ve hangi arabirimlere başvurulduğunu görebilirsiniz, böylece anlayabiliriz ~
ClassPathXmlApplicationContext, AbstractApplicationContext'ten miras alır ve AbstractRefreshableApplicationContext, AbstractApplicationContext'in soyut bir alt sınıfıdır. Kullanılan sınıf kayıt fabrikası DefaultListableBeanFactory'dir. Bu kayıt fabrikası da çok önemlidir ve daha sonra tanıtılacaktır.
Basitçe söylemek gerekirse, DefaultListableBeanFactory, Spring'in fasulye kaydı ve yüklemesinin varsayılan uygulamasıdır.Kayıtlı çekirdekleri anahtar-değer depolaması için beanDefinitionMap'e koyar.
Resmin sağ üst köşesinde görebileceğiniz gibi, ResourceLoader, bu sınıfın kaynak yükleme işlevini uyguladığını gösteren üst düzey arayüzüdür.
Yapıcının kodu:
genel ClassPathXmlApplicationContext (
String configLocations, boolean yenileme, @Nullable ApplicationContext parent)
BeansException {atar
süper (ebeveyn);
// Not 1.1 Kaynak dosyalarını alın
setConfigLocations (configLocations);
eğer (yenile) {
yenileme ();
}
}
Yapılandırma dosyası yolunu ayarlayın
org.springframework.context.support.AbstractRefreshableConfigApplicationContextpublic void setConfigLocations (@Nullable String ... konumlar) {
eğer (konumlar! = boş) {
Assert.noNullElements (konumlar, "Yapılandırma konumları boş olmamalıdır");
// Not 1.2 Yapılandırma kaynak yolunu configLocations dizisine yerleştirin
this.configLocations = new String;
for (int i = 0; i < konumlar.length; i ++) {
this.configLocations = resolPath (konumlar ) .trim ();
}
}
Başka {
this.configLocations = null;
}
}
SolutionPath'in amacı şudur: Verilen yolu ayrıştırın ve yer tutucuyu karşılık gelen yer tutucuyla değiştirin
Örneğin, yeni ClassPathXmlApplicationContext ("classpath: config.xml");, sınıf yolunu ayrıştırmanız ve doğru yola çevirmeniz gerekir.
korumalı Dize resolverPath (Dize yolu) {
getEnvironment (). returnRequiredPlaceholders (yol);
}
Geliştirme, test veya üretim gibi farklı işletim ortamlarımız var. Şu anda yüklenen yapılandırma dosyaları ve özellikler farklı olmalıdır. Şu anda, ayırt etmek için Ortamı kullanmamız gerekiyor.
Yay ortamı ve özellikleri dört bölümden oluşur:
Profil
Bu öznitelik aracılığıyla, yapılandırma dosyasında aynı anda iki yapılandırma kümesi dağıtılabilir; bu, üretim ortamı ve geliştirme ortamı için uygundur, böylece geliştirme ve dağıtım ortamını değiştirmek uygun olur ve genellikle farklı veritabanları veya yapılandırma dosyalarını değiştirmek için kullanılır.
demo: (referans 4'ten alıntılanmıştır)
< ! - Test ortamı yapılandırma dosyası - >
< fasulye profili = "test" >
< bağlam: özellik-yer tutucu location = "sınıf yolu: test / *. özellikler, sınıf yolu: ortak / *. özellikler" / >
< /Fasulyeler >
< ! - Üretim ortamı yapılandırma dosyası - >
< fasulye profili = "üretim" >
< bağlam: özellik-yer tutucu location = "sınıf yolu: üretim / *. özellikler, sınıf yolu: ortak / *. özellikler" / >
< /Fasulyeler >
< ! - Geliştirme ortamı yapılandırma dosyası - >
< fasulye profili = "geliştirme" >
< bağlam: özellik-yer tutucu location = "sınıf yolu: dev / *. özellikler, sınıf yolu: ortak / *. özellikler" / >
< /Fasulyeler >
Hangi yapılandırmanın kullanılacağını seçmenin iki yolu vardır:
web.xml'de ayarlayın
< bağlam parametresi >
< param-adı > spring.profiles.active < / param-adı >
< param-değeri > Ölçek < / param-değeri >
< / context-param >
Kodun ne zaman başlayacağını ayarlayın
context.getEnvironment (). setActiveProfiles ("test");
PropertySource arayüzü
Miras sistemi şekilde gösterilmiştir:
PropertySource miras sistemi perspektifinden, customPropertySources yönteminin çağrı bağlantısı, alt sınıftan sonuna kadar çağırmaktır:
SoyutÇevre > StandardServletEnvironment- > StandardEnvironment
Son olarak, öznitelik depolaması için StandardEnvironment'daki CopyOnWriteArrayList dizisini kullanın
korumalı void customPropertySources (MutablePropertySources propertySources) {
propertySources.addLast (yeni MapPropertySource (SYSTEM_PROPERTYES_PROPERTY_SOURCE_NAME, getSystemProperties ()));
propertySources.addLast (yeni SystemEnvironmentPropertySource (SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment ()));
}
Örneğin, yukarıdan da görülebileceği gibi, propertySourceList sistem parametrelerini saklayacaktır:
Daha sonra bu parametreler, bağlam bağlamı aracılığıyla başlatılan uygulamada elde edilebilir
((MutablePropertySources) ((StandardEnvironment) context.environment) .propertySources) .propertySourceList
Fasulye analizi ve kaydı
Bahar fasulyesi ayrıştırma ve kaydı önemli bir yöntem yenileme ()
AbstractApplicationContext.refresh ()
public void refresh (), BeansException, IllegalStateException {atar
senkronize (this.startupShutdownMonitor) {
// Bu bağlamı yenileme için hazırlayın. (Güncellemeye bağlam hazırlayın, bazı işaretler ayarlayın)
hazırlamakRefresh ();
// Alt sınıfa dahili fasulye fabrikasını yenilemesini söyleyin. (Alt sınıfa dahili fasulye fabrikasını yenilemesini söyleyin)
// Fasulye fabrikasına sınıfın kaydı da bu aşamada
YapılandırılabilirListableBeanFactory beanFactory = eldeFreshBeanFactory ();
// Fasulye fabrikasını bu bağlamda kullanmak için hazırlayın.
hazırlamakBeanFactory (beanFactory);
Deneyin {
// Fasulye fabrikasının bağlam alt sınıflarında sonradan işlenmesine izin verir.
postProcessBeanFactory (beanFactory);
// Bağlamda fasulye olarak kaydedilmiş fabrika işlemcilerini çağırın.
invokeBeanFactoryPostProcessors (beanFactory);
// Fasulye oluşumunu engelleyen fasulye işlemcilerini kaydedin.
registerBeanPostProcessors (beanFactory);
// Bu bağlam için mesaj kaynağını başlatın.
initMessageSource ();
// Bu bağlam için olay çok yöneticisini başlatın.
initApplicationEventMulticaster ();
// Belirli bağlam alt sınıflarında diğer özel fasulyeleri başlatın.
onRefresh ();
// Dinleyici çekirdeklerini kontrol edin ve kaydedin.
registerListeners ();
// Kalan tüm (tembel olmayan) tekilleri örnekleyin.
finishBeanFactoryInitialization (beanFactory);
// Son adım: karşılık gelen etkinliği yayınlayın.
finishRefresh ();
}
catch (BeansException ex) {
if (logger.isWarnEnabled ()) {
logger.warn ("Bağlam başlatma sırasında karşılaşılan istisna -" +
"yenileme girişiminin iptal edilmesi:" + örn);
}
// Kaynakların sarkmasını önlemek için önceden oluşturulmuş tekilleri yok edin.
destroyBeans ();
// 'Etkin' işaretini sıfırlayın.
cancelRefresh (eski);
// İstisnayı çağırana ilet.
eski atmak;
}
en sonunda {
// Spring'in çekirdeğindeki yaygın iç gözlem önbelleklerini sıfırlayın, çünkü
// artık tekli çekirdekler için meta veriye ihtiyaç duymayabilir ...
resetCommonCaches ();
}
}
}
Aşağıda, izleme ve analiz için bu yönteme odaklanılacaktır.
Belirli doğrulama yöntemi
org.springframework.core.env.AbstractPropertyResolver # validateRequiredProperties
public void validateRequiredProperties () {
MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException ();
for (String key: this.requiredProperties) {
eğer (this.getProperty (key) == null) {
ex.addMissingRequiredProperty (anahtar);
}
}
eğer (! ex.getMissingRequiredProperties (). isEmpty ()) {
eski atmak;
}
}
Gördüğünüz gibi, doğrulama mantığı, varsayılan olarak boş olan bir karakter kümesi olan requiredProperties'i geçmek içindir, bu, hiçbir elemanın doğrulanması gerekmediği anlamına gelir.Listede bir değer varsa, boş olacak anahtara göre karşılık gelen ortam değişkenini alın. Bahar kapsayıcısının başlatılamamasına neden olan bir istisna atıldı.
Fasulye kabını alın
Bu kod satırında ConfigurableListableBeanFactory beanFactory = findFreshBeanFactory ();
Spesifik çağrı şudur:
org.springframework.context.support.AbstractRefreshableApplicationContext # renewBeanFactorykorumalı son void refreshBeanFactory () BeansException {
// Güncelleme sırasında zaten var olduğu tespit edilirse önceki çekirdek temizlenecek ve eski çekirdek kabı kapatılacaktır.
eğer (hasBeanFactory ()) {
destroyBeans ();
closeBeanFactory ();
}
Deneyin {
DefaultListableBeanFactory beanFactory = createBeanFactory ();
beanFactory.setSerializationId (getId ());
customBeanFactory (beanFactory);
// Yorum 1.3 yüklemeye başlar (fasulye kaydı)
loadBeanDefinitions (beanFactory);
senkronize (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
yeni ApplicationContextException ("I / O hatası için fasulye tanımı kaynağını ayrıştırırken" + getDisplayName (), örn.);
}
}
Bu giriş yöntemi çok önemlidir Bu adımda, yeni bir fasulye kabı ve ayrıştırılmış bir çekirdek oluşturulur ve çekirdek kabın içine kaydedilir.
BanFactory özel
Spesifik yöntem aşağıdaki gibidir: Bu yöntemle fabrika özelleştirilebilir ve alt sınıflar serbestçe yapılandırılabilir:
org.springframework.context.support.AbstractRefreshableApplicationContext # customBeanFactorykorumalı void customBeanFactory (DefaultListableBeanFactory beanFactory) {
eğer (this.allowBeanDefinitionOverriding! = null) {
// Varsayılan yanlıştır, üzerine yazmaya izin verilmez
beanFactory.setAllowBeanDefinitionOverriding (this.allowBeanDefinitionOverriding);
}
eğer (this.allowCircularReferences! = null) {
// Varsayılan yanlıştır, döngüsel referanslara izin verilmez
beanFactory.setAllowCircularReferences (this.allowCircularReferences);
}
}
EntityResolver
Arayüzün tam yolu şudur: org.xml.sax.EntityResolver, analiz için kullanılan özel yöntem şudur:
org.springframework.beans.factory.xml.ResourceEntityResolver # resolEntityBu yöntem, şema ve dtd'yi ayrıştırmak için kullanılır. Daha derine inerseniz çok karmaşıktır, ancak xml ayrıştırmak benim anlamak istediğim şey değildir, bu yüzden önce atlayın ~
Yapılandırma dosyası yükleniyor
Giriş yöntemi: (Aynı ada sahip birden fazla yöntem olduğundan, yolu kopyalarken parametre türü de kopyalanır)
org.springframework.beans.factory.support.AbstractBeanDefinitionReader # loadBeanDefinitions (java.lang.String, java.util.Set < org.springframework.core.io.Resource > )Temel yöntem bu iki satırdır
public int loadBeanDefinitions (Dize konumu, @Nullable Set < Kaynak > realResources), BeanDefinitionStoreException {
// Kaynak dosyasını alın (kaynak yükleyici, kaynak dosyasını yoldan tanımlar)
Kaynak kaynakları = ((ResourcePatternResolver) resourceLoader) .getResources (konum)
// Not 1.6 Fasulyeyi kaynak dosyasına göre yükleyin
int count = loadBeanDefinitions (kaynaklar);
···
}
Kaynak dosyasını aldıktan sonra, kaynak dosyasını (yani başlangıçta iletilen config.xml) ayrıştırmaya başlayın ve bunu Belgeye dönüştürün
İzleme kodundan, ayrıştırılacak kaynak dosyasının Resource'tan EncodeResouce'a paketlendiğini ve karakter kodlamasının, tasarım deseni dekoratör modunu yansıtan giriş akışına eklendiğini (varsayılan değer boştur) görebilirsiniz.
Kaynak dosyasında gezinmek ve dönüştürme gerçekleştirmek için temel yöntem aşağıdaki iki satırdır:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader # loadBeanDefinitions (org.springframework.core.io.support.EncodedResource)public int loadBeanDefinitions (EncodedResource encodedResource) BeanDefinitionStoreException {
// Not 1.7 Kaynak dosyadan giriş akışını alın
InputStream inputStream = encodedResource.getResource (). GetInputStream ();
InputSource inputSource = yeni InputSource (inputStream);
dönüş doLoadBeanDefinitions (inputSource, encodedResource.getResource ());
}
Varsayılan etiket çözünürlüğü
Bu bölüm ayrıntılara girmeyecek ve daha sonra onu tamamlamak için bir makale yazacağım, bu yüzden koddaki varsayılan etiketin nasıl ayrıştırılacağına bakacağım.
private void parseDefaultElement (Element ele, BeanDefinitionParserDelegate delegate) {
eğer (delegate.nodeNameEquals (ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource (ele);
}
else if (delegate.nodeNameEquals (ele, ALIAS_ELEMENT)) {
processAliasRegistration (ele);
}
else if (delegate.nodeNameEquals (ele, BEAN_ELEMENT)) {
processBeanDefinition (ele, delege);
}
else if (delegate.nodeNameEquals (ele, NESTED_BEANS_ELEMENT)) {
// yineleme
doRegisterBeanDefinitions (ele);
}
}
Bakalım fasulye etiketlerinin nasıl ayrıştırılacağı
Kimliği ve adı alın
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate # parseBeanDefinitionElement (org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)public BeanDefinitionHolder parseBeanDefinitionElement (Element ele, @Nullable BeanDefinition containsBean) {
// ID özniteliğini al
Dize kimliği = ele.getAttribute (ID_ATTRIBUTE);
// NAME özelliğini al
String nameAttr = ele.getAttribute (NAME_ATTRIBUTE);
Liste < Dize > aliases = new ArrayList < > ();
if (StringUtils.hasLength (nameAttr)) {
// İsim,;
String nameArr = StringUtils.tokenizeToStringArray (nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll (Arrays.asList (nameArr));
}
String beanName = id;
eğer (! StringUtils.hasText (beanName)! aliases.isEmpty ()) {
// id belirtilmezse, id olarak ismin ilk değerini kullanın
beanName = takma adlar.remove (0);
}
// varsayılan boş
if (containsBean == null) {
// Adın benzersiz olup olmadığını kontrol edin, kimlik tekrarlanırsa bir hata atılır
// Internal usedNames, yüklenen adı ve takma adları depolayan bir HashSet'tir
checkNameUniqueness (beanName, takma adlar, ele);
}
// public özelliğini, GenericBeanDefinition alt sınıfında uygulanan AbstractBeanDefinition içine koyun
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement (ele, beanName, includingBean);
eğer (beanDefinition! = null) {
eğer (! StringUtils.hasText (beanName)) {
eğer (containsBean! = null) {
// id ve adın her ikisi de boşsa, bu yay onun için varsayılan bir ad oluşturacaktır
beanName = BeanDefinitionReaderUtils.generateBeanName (
beanDefinition, this.readerContext.getRegistry (), true);
}
Başka {
beanName = this.readerContext.generateBeanName (beanDefinition);
String beanClassName = beanDefinition.getBeanClassName ();
eğer (beanClassName! = null
beanName.startsWith (beanClassName) beanName.length () > beanClassName.length ()
! this.readerContext.getRegistry (). isBeanNameInUse (beanClassName)) {
aliases.add (beanClassName);
}
}
}
}
String aliasesArray = StringUtils.toStringArray (takma adlar);
return new BeanDefinitionHolder (beanDefinition, beanName, aliasesArray);
}
boş döndür;
}
Kimlik ve ad özniteliklerini elde etme süreci, kod yorumlarını adım adım takip ederek anlaşılır.
Bu yöntemin ana iş akışı aşağıdaki gibidir:
Etiketteki diğer özelliklerin analizi
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate # parseBeanDefinitionElement (org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)public AbstractBeanDefinition parseBeanDefinitionElement (
Eleman ele, String beanName, @Nullable BeanDefinition containsBean) {
AbstractBeanDefinition bd = createBeanDefinition (sınıfAdı, üst);
parseBeanDefinitionAttributes (ele, beanName, includeBean, bd);
bd.setDescription (DomUtils.getChildElementValueByTagName (ele, DESCRIPTION_ELEMENT));
parseMetaElements (ele, bd);
parseLookupOverrideSubElements (ele, bd.getMethodOverrides ());
parseReplacedMethodSubElements (ele, bd.getMethodOverrides ());
parseConstructorArgElements (ele, bd);
parsePropertyElements (ele, bd);
parseQualifierElements (ele, bd);
bd.setResource (this.readerContext.getResource ());
bd.setSource (extractSource (ele));
dönüş bd;
}
Bu yöntemde BeanDefiniton'ı başlatın: (Spesifik uygulama, alt sınıfı GenericBeanDefinition ~)
BeanDefinitionReaderUtils.createBeanDefinition (parentName, className, this.readerContext.getBeanClassLoader ())public static AbstractBeanDefinition createBeanDefinition (
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition ();
bd.setParentName (parentName);
eğer (className! = null) {
eğer (classLoader! = null) {
bd.setBeanClass (ClassUtils.forName (className, classLoader));
}
Başka {
bd.setBeanClassName (sınıfAdı);
}
}
dönüş bd;
}
Bir sonraki adım, diğer etiketlerin içeriğini ayrıştırmak ve ardından çukuru oluşturmaktır ~
BeanDefinitionHolder değişikliği
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate # decorateBeanDefinitionIfRequired (org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinitionHolder, org.springframework.beans.factory.config)public BeanDefinitionHolder decorateBeanDefinitionIfRequired (
Eleman ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containsBd) {
// Yöntemdeki üçüncü parametre, ana fasulyedir
// İç içe yerleştirilmiş bir yapılandırmayı analiz ederken, üst sınıfın kapsam özniteliğini kullanmak için buraya geçirilmesi gerekir, alt sınıfın kapsamı ayarlamaması durumunda, üst sınıfın kapsam özniteliğini kullanabilirsiniz
BeanDefinitionHolder finalDefinition = definitionHolder;
// Önce özel özniteliklere göre dekorasyon yapın.
NamedNodeMap nitelikleri = ele.getAttributes ();
// Tüm nitelikleri çaprazlayın ve nitelikleri değiştirin
for (int i = 0; i < attributes.getLength (); i ++) {
Düğüm düğümü = öznitelikler.item (i);
finalDefinition = decorateIfRequired (node, finalDefinition, containsBd);
}
// Özel iç içe yerleştirilmiş öğelere göre dekorasyon yapın.
NodeList çocukları = ele.getChildNodes ();
// Tüm alt düğümleri geçin ve alt öğeleri değiştirin
for (int i = 0; i < children.getLength (); i ++) {
Düğüm düğümü = children.item (i);
eğer (node.getNodeType () == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired (node, finalDefinition, containsBd);
}
}
return finalDefinition;
}
Önceki normal özellik analizinden sonra, bu işlem adımında, esas olarak özel etiket öğelerinin analizini tamamlamak için kullanılır. Burada bir boşluk bırakalım ~
hazırlamakBeanFactory
Sınıf yükleyicinin ortamını hazırlayın ve ClassLoader, post-processors vb. Dahil olmak üzere daha önce elde edilen beanFactory'yi (ConfigurationListableBeanFactory) ayarlayın.
invokeBeanFactoryPostProcessors
Tüm kayıtlı BeanFactoryPostProcessorBeans örneğini oluşturun ve çağırın. Bunlar işlem sonrası işlemcilerdir ve işleme türü BeanFactory'dir. Yay kapsayıcı, çekirdeği başlatmadan önce çekirdek bilgilerini okumanıza ve özelliklerini değiştirmenize olanak tanır.
Kullanıcıya, somutlaştırmadan önce çekirdek bilgisini değiştirmek için son şansı vermeye eşdeğerdir.
Diğer bir nokta da, bu işlemcilerin PriorityOrdered, Order arabirimini uygulayıp uygulamadığına ve sipariş değerine göre sıralamanın sıralı bir sıraya sahip olabilmesidir.
initMessageSource
Bu bağlamın mesaj kaynağını başlatın
onRefresh
Bağlama özgü yenileme çalışması eklemek için şablon yöntemi geçersiz kılınabilir.
Tekliyi somutlaştırmadan önce özel fasulyenin başlatılmasını çağırın. (Sis, özel fasulyenin ne olduğunu bilmiyorum, bir delik bırak = - =)
Bu uygulama boş.
finishBeanFactoryInitialization
Çekirdek kabının başlatılmasını tamamlayın ve kalan tüm (tembel olmayan başlatma) tekilleri somutlaştırın
resetCommonCaches
Önbelleği temizlemek için kullanılan gerçek kaydın son adımıdır
Spring çekirdeğindeki genel iç gözlem önbelleğini sıfırlayın, çünkü artık tekil çekirdeğin meta verilerine ihtiyacımız olmayabilir.Javadoc derleme hatası
Javadoc oluşturma başarısız. Oluşturulan Javadoc seçenekler dosyası (sorun giderme için kullanışlıdır)Derleme sırasında, başarılı olamadığı ve Javadoc'ta bir hataya yol açtığı bulundu. Çözüm, aşağıdaki yapılandırmayı gradle dosyasına eklemektir:
task.withType (Javadoc) {
options.addStringOption ('Xdoclint: yok', '- sessiz')
options.addStringOption ('kodlama', 'UTF-8')
}