关于spring自动加载的那点事儿
背景
慣例要講一下背景,畢竟問題來源于生活,困難滋生于工作,要是每天吃吃喝喝、無憂無慮,我相信我也沒什么問題好寫了^_^公司架構組在推新的基礎框架,主要是嫌以前的框架用起來太啰嗦了,做了很多感覺多余的工作(思想也是在進步滴)。正好我這邊也在做中臺服務的下沉和重構,用到了索性就直接接新的框架了,老是用陳年的東西不肯升級,都快覺得自己已經七老八十了,咱要有互聯網人的與時俱進的思維,出了問題大不了刪庫跑路~~~開玩笑哈。
問題
進入正題,問題就來源于這里,引了新的jar包后,大致瀏覽了一遍對接文檔,發現確實要做的工作很少,把核心bean用spring的注入方式引進去就可以拿來用了。類似于下面這樣:
@Autowired private Template template;然后就可以拿著template做一些不可描述的事情
三下五除二我寫完了代碼,想著總不能干坐著吧,顯得工作不飽滿呀,研究一下架構組的框架吧。看著看著還真看出了問題(我以為的問題),我的工程是springboot工程,這個Template為啥可以自動注入進來,所在的包路徑明明沒有被我配置啊。我們知道,springboot是有自動加載bean到context的配置的,前提是標注@SpringBootApplication的啟動類所在的包是bean所在包的父包。有點繞口,總結來說,一般我們把啟動類放在根包下,其他的類都在子包下,那其他所有自己寫的類就都能自動加載了。但是三方jar的包路徑明顯跟自己定義的不一樣,為啥也能自動加載呢?翻了一遍jar包中的所有類也沒找到什么特殊的配置,百思不得其解。說來也巧,問題都是扎推來的,老項目在啥都沒改的情況突然不能啟動了,報spring的bean命名沖突,也就是容器內存在同名類,一查兩個不同三方包內的bean重名了,納尼?這種問題為啥現在突然爆出來,而且三方包如果所有的bean都自動加載的話,重名根本不是自己能控制的呀。感覺跟前一個是同一問題,就是為啥spring能自動加載三方jar中的bean,各種查資料,終于被我找到了答案,一看也沒多高深,就是自己以前沒接觸過。。。
原因
我們知道,java有一個spi機制,在jar包的META-INF/services/目錄里創建一個以服務接口命名的文件。該文件里就是實現該服務接口的具體實現類。而當外部程序裝配這個模塊的時候,就能通過該jar包META-INF/services/里的配置文件找到具體的實現類名,并裝載實例化,完成模塊的注入。基于這樣一個約定就能很好的找到服務接口的實現類,而不需要再代碼里制定。jdk提供服務實現查找的一個工具類:java.util.ServiceLoader。
這里spring的實現其實與java的spi類似(不知道是不是參考了),在META-INF下創建一個spring.factories文件,像下面這樣,然后spring-core包里定義了SpringFactoriesLoader類,這個類實現了檢索META-INF/spring.factories文件,并獲取指定接口的配置的功能。
// spring實現的檢索META-INF/spring.factories文件的方法 public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {Assert.notNull(factoryClass, "'factoryClass' must not be null");ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);if (logger.isTraceEnabled()) {logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);}List<T> result = new ArrayList<T>(factoryNames.size());for (String factoryName : factoryNames) {result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));}AnnotationAwareOrderComparator.sort(result);return result; }public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {String factoryClassName = factoryClass.getName();try {Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));List<String> result = new ArrayList<String>();while (urls.hasMoreElements()) {URL url = urls.nextElement();Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));String factoryClassNames = properties.getProperty(factoryClassName);result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));}return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);} }注意,這里是會查找所有的classpath下的jar包中的spring.factories文件的,spring自己本身的starter機制就是以此為基礎實現的。至于這個文件內容到底是怎樣的,我們需要怎么配置呢?以spring-boot-autoconfigure為例,下面就是其中的spring.factories文件,這個文件還有其他的作用,我們主要關注Auto Configure模塊。
# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer# Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\ org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\ org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\ org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\ org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.hornetq.HornetQAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceDelegatingViewResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.SitePreferenceAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration,\ org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\ org.springframework.boot.autoconfigure.social.SocialWebAutoConfiguration,\ org.springframework.boot.autoconfigure.social.FacebookAutoConfiguration,\ org.springframework.boot.autoconfigure.social.LinkedInAutoConfiguration,\ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\ org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.WebSocketMessagingAutoConfiguration# Template availability providers org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider=\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.velocity.VelocityTemplateAvailabilityProvider,\ org.springframework.boot.autoconfigure.web.JspTemplateAvailabilityProvider如果想要自己實現的jar包被別人依賴的時候能夠自動配置,可以仿照該文件寫一個,第一行是自動配置的一個注解,然后每一行都是自己定義的一個配置類,比如開頭我提到的包自動掃描的功能,只要我在新建一個配置類
@ComponentScan("cn.jjs.demo") public class DemoAutoConfiguration { }然后把這個類加到META/INF下的spring.factories文件中去,像下面這樣,這樣就可以自動掃描cn.jjs.demo包了,不需要引用方額外配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ cn.jjs.demo.DemoAutoConfiguration結語
不探索還真發現不了編程之美,有時候刨根問題能夠帶來不一樣的收貨,說不定愛上了代碼,然后就是步步高升,迎娶白富美,走上人生巔峰。
針對這種三方jar能夠自動配置bean的情況,如果重名了咋辦呢?因為這也不是自己能控制的,所以需要我們手動排除,如果確實兩個bean都需要用到,那就另外取一個名字,類似下面這樣:
@SpringBootApplication // 這里通過類型在掃描中排除了A這個類 @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = A.class)}) public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);} } // 這里通過配置A的別名解決重名沖突,spring默認會拿方法名作為beanName @Configuration public class AutoConfig {@Beanpublic A anotherA() {return new A();} }本文由博客一文多發平臺 OpenWrite 發布!
總結
以上是生活随笔為你收集整理的关于spring自动加载的那点事儿的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新概念英语的MP3下载(英音和美音两种版
- 下一篇: AdobePhotoshopCS快捷键