Spring有一个问题,已经存在了一段时间,我在许多项目中都遇到过。 与Spring或Spring的Guys无关,这取决于像您和我这样的Spring用户。 让我解释一下……在Spring 2的过去,您必须手动配置Application Context,手动创建一个包含所有bean定义的XML配置文件。 这种技术的缺点是创建这些XML文件很费时,然后您就很难维护这个越来越复杂的文件。 我似乎记得当时它被称为“ Spring Config Hell”。 从好的方面来说,至少您对加载到上下文中的所有内容都有一个中央记录。 顺应需求和流行的注释方式,Spring 3引入了大量的原型设计类,例如@Service @Controller@Repository @Component@Controller@Repository ,以及<context:component-scan/>的XML配置文件<context:component-scan/>元素。 从编程的角度来看,这使事情变得简单得多,并且是构造Spring上下文的一种非常流行的方式。

但是,使用Spring注释时要放任不管,并使用@Service @Component@Controller @Component@Controller@Repository来添加所有内容,这在大型代码库中尤其麻烦。 问题是您的上下文被不需要的东西污染了,这是一个问题,因为:

  • 您不必要地用完了烫发空间,从而导致更多“烫发空间错误”的风险。
  • 您不必要地耗尽了堆空间。
  • 您的应用程序可能需要更长的时间才能加载。
  • 不需要的对象可以“随便做”,特别是如果它们是多线程的,则具有start()方法或实现InitializingBean
  • 不需要的对象只会阻止您的应用程序正常工作……

在小型应用程序中,我猜想在Spring上下文中是否有几个额外的对象并不重要,但是,正如我上面说的,如果您的应用程序很大,处理器密集型或占用内存,则这尤其麻烦。 在这一点上,有必要对这种情况进行分类,并且要做到这一点,您必须首先弄清楚要加载到Spring上下文中的是什么。

一种方法是通过在log4j属性中添加以下内容来启用com.springsource包上的调试功能:

log4j.logger.com.springsource=DEBUG

将以上内容添加到您的log4j属性(在本例中为log4j 1.x)中,您将获得有关Spring上下文的大量信息–我的意思是很多。 如果您是Spring的专家之一,并且正在研究Spring源代码,那么实际上这只是您需要做的事情。

另一种更简洁的方法是在您的应用程序中添加一个类,该类将准确报告正在Spring上下文中加载的内容。 然后,您可以检查报告并进行任何适当的更改。

该博客的示例代码包含一个类,这是我之前写过两三遍的文章,分别为不同的公司从事不同的项目。 它依赖于Spring的几个功能。 也就是说,在Context加载后,Spring可以在您的类中调用一个方法,并且Spring的ApplicationContext接口包含一些方法,这些方法可以告诉您有关其内部的所有信息。

@Service
public class ApplicationContextReport implements ApplicationContextAware, InitializingBean { private static final String LINE = "====================================================================================================\n"; private static final Logger logger = LoggerFactory.getLogger("ContextReport"); private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void afterPropertiesSet() throws Exception { report(); } public void report() { StringBuilder sb = new StringBuilder("\n" + LINE); sb.append("Application Context Report\n"); sb.append(LINE); createHeader(sb); createBody(sb); sb.append(LINE); logger.info(sb.toString()); } private void createHeader(StringBuilder sb) { addField(sb, "Application Name: ", applicationContext.getApplicationName()); addField(sb, "Display Name: ", applicationContext.getDisplayName()); String startupDate = getStartupDate(applicationContext.getStartupDate()); addField(sb, "Start Date: ", startupDate); Environment env = applicationContext.getEnvironment(); String[] activeProfiles = env.getActiveProfiles(); if (activeProfiles.length > 0) { addField(sb, "Active Profiles: ", activeProfiles); } } private void addField(StringBuilder sb, String name, String... values) { sb.append(name); for (String val : values) { sb.append(val); sb.append(", "); } sb.setLength(sb.length() - 2); sb.append("\n"); } private String getStartupDate(long startupDate) { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); return df.format(new Date(startupDate)); } private void createBody(StringBuilder sb) { addColumnHeaders(sb); addColumnValues(sb); } private void addColumnHeaders(StringBuilder sb) { sb.append("\nBean Name\tSimple Name\tSingleton\tFull Class Name\n"); sb.append(LINE); } private void addColumnValues(StringBuilder sb) { String[] beanNames = applicationContext.getBeanDefinitionNames(); for (String name : beanNames) { addRow(name, sb); } } private void addRow(String name, StringBuilder sb) { Object obj = applicationContext.getBean(name); String fullClassName = obj.getClass().getName(); if (!fullClassName.contains("org.springframework")) { sb.append(name); sb.append("\t"); String simpleName = obj.getClass().getSimpleName(); sb.append(simpleName); sb.append("\t"); boolean singleton = applicationContext.isSingleton(name); sb.append(singleton ? "YES" : "NO"); sb.append("\t"); sb.append(fullClassName); sb.append("\n"); } }
}

首先要注意的是,此版本的代码实现了Spring的InitializingBean接口。 当Spring将一个类加载到上下文中时,Spring将检查此接口。 如果找到它,它将调用AfterPropertiesSet()方法。

这不是让Spring在启动时调用您的类的唯一方法,请参阅: 三种Spring Bean生命周期技术以及使用JSR-250的@PostConstruct注释替换Spring的InitializingBean

接下来要注意的是,该报告类实现了Spring的ApplicationContextAware接口。 这是另一个有用的Spring主力接口,通常永远不需要每天使用。 该接口背后的理由是使您的类可以访问应用程序的ApplicationContext 。 它包含一个方法: setApplicationContext(...) ,由Spring调用以将ApplicationContext注入您的类中。 在这种情况下,我只是将ApplicationContext参数保存为实例变量。

主报告的生成是通过report()方法完成的(由afterPropertiesSet()调用)。 report()方法所做的全部工作就是创建一个StringBuilder()类,然后附加大量信息。 我不会逐一介绍每一行,因为这种代码是线性的,而且很无聊。 突出显示形式为createBody(...)调用的addColumnValues(...)方法。

private void addColumnValues(StringBuilder sb) { String[] beanNames = applicationContext.getBeanDefinitionNames(); for (String name : beanNames) { addRow(name, sb); } }

此方法调用applicationContext.getBeanDefinitionNames()以获取包含此上下文加载的所有bean的名称的数组。 获得这些信息后,我将遍历数组,在每个bean名称上调用applicationContext.getBean(...) 。 一旦拥有bean本身,就可以在报告中将其类详细信息添加到StringBuilder中。

创建报告后,编写自己的文件处理代码(将StringBuilder的内容保存到磁盘)并没有多大意义。 这种代码已经被写过很多次了。 在这种情况下,我选择通过在上面的Java代码中添加以下记录器行来利用Log4j(通过slf4j):

private static final Logger logger = LoggerFactory.getLogger("ContextReport");

…并通过将以下内容添加到我的log4j XML配置文件中:

<appender name="fileAppender" class="org.apache.log4j.RollingFileAppender"><param name="Threshold" value="INFO" /><param name="File" value="/tmp/report.log"/><layout class="org.apache.log4j.PatternLayout"><param name="ConversionPattern" value="%d %-5p  [%c{1}] %m %n" /></layout></appender><logger name="ContextReport" additivity="false"><level value="info"/>    <appender-ref ref="fileAppender"/></logger>

请注意,如果您使用的是log4j 2.x,则XML可能会有所不同,但这超出了本博客的范围。

这里要注意的是,我使用RollingFileAppender ,它将一个名为report.log的文件写入/tmp -尽管该文件显然可以位于任何地方。

注意的另一个配置点是ContextReport Logger。 这会将其所有日志输出定向到fileAppender并且由于具有additivity="false"属性,因此仅将fileAppender到其他地方。

配置唯一需要记住的其他部分是将report包添加到Spring的component-scan元素中,以便Spring将检测@Service批注并加载该类。

<context:component-scan base-package="com.captaindebug.report" />

为了证明它有效,我还创建了一个JUnit测试用例,如下所示:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({ "file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml" })
public class ApplicationContextReportTest { @Autowired private ApplicationContextReport instance; @Test public void testReport() { System.out.println("The report should now be in /tmp"); } }

这使用SpringJUnit4ClassRunner@ContextConfiguration批注来加载应用程序的实时 servlet-context.xml文件。 我还包括了@WebAppConfiguration批注,以告诉Spring这是一个Web应用程序。

如果运行JUnit测试,您将获得一个report.log ,其中包含以下内容:

2014-01-26 18:30:25,920 INFO   [ContextReport]
====================================================================================================
Application Context Report
====================================================================================================
Application Name:
Display Name: org.springframework.web.context.support.GenericWebApplicationContext@74607cd0
Start Date: 2014-01-26T18:30:23.552+0000Bean Name       Simple Name     Singleton       Full Class Name
====================================================================================================
deferredMatchUpdateController   DeferredMatchUpdateController   YES     com.captaindebug.longpoll.DeferredMatchUpdateController
homeController  HomeController  YES     com.captaindebug.longpoll.HomeController
DeferredService DeferredResultService   YES     com.captaindebug.longpoll.service.DeferredResultService
SimpleService   SimpleMatchUpdateService        YES     com.captaindebug.longpoll.service.SimpleMatchUpdateService
shutdownService ShutdownService YES     com.captaindebug.longpoll.shutdown.ShutdownService
simpleMatchUpdateController     SimpleMatchUpdateController     YES     com.captaindebug.longpoll.SimpleMatchUpdateController
applicationContextReport        ApplicationContextReport        YES     com.captaindebug.report.ApplicationContextReport
the-match       Match   YES     com.captaindebug.longpoll.source.Match
theQueue        LinkedBlockingQueue     YES     java.util.concurrent.LinkedBlockingQueue
BillSykes       MatchReporter   YES     com.captaindebug.longpoll.source.MatchReporter
====================================================================================================

该报告包含一个标题,该标题包含诸如Display NameStart Date后跟主体。 主体是一个制表符分隔的表,其中包含以下几列:Bean名称,简单类名称,该Bean是否为单例或原型以及完整的类名称。

现在,您可以使用此报告来发现不需要加载到Spring Context中的类。 例如,如果你决定,你不希望加载BillSykes实例com.captaindebug.longpoll.source.MatchReporter ,那么你有以下几种选择。

首先,可能是BillSykes bean已装入的情况,因为它装在错误的程序包中。 当您尝试沿着类类型线组织项目结构时,通常会发生这种情况,例如,将所有服务放在一个service包中,而所有控制器放在一个controller包中; 因此,将服务模块包含到应用程序中将加载所有服务类,即使您不需要的类也可能会导致问题。 通常最好按照“ 如何组织Maven子模块”中所述的功能来组织。 。

不幸的是,重组整个项目的成本特别高,并且不会产生很多收入。 解决该问题的另一种较便宜的方法是对Spring context:component-scan Element进行调整,并排除那些引起问题的类。

<context:component-scan base-package="com.captaindebug.longpoll" /><context:exclude-filter type="regex" expression="com\.captaindebug\.longpoll\.source\.MatchReporter"/></context:component-scan>

…或任何给定包中的所有类:

<context:component-scan base-package="com.captaindebug.longpoll" /><context:exclude-filter type="regex" expression="com\.captaindebug\.longpoll\.source\..*"/></context:component-scan>

使用exclude-filter是一种有用的技术,但是与之相对应的还有很多文章:include-filter,因此对此XML配置的完整解释超出了本博客的范围,但是也许我会在下面进行介绍。以后再约会。

  • 该博客的代码可在GitHub上作为长期民意测验项目的一部分获得,网址为:https://github.com/roghughe/captaindebug/tree/master/long-poll
参考: Captain Debug的Blog博客中的JCG合作伙伴 Roger Hughes 优化了ApplicationContext 。

翻译自: https://www.javacodegeeks.com/2014/02/optimising-your-applicationcontext.html

优化您的ApplicationContext相关推荐

  1. SSH框架整合——基于XML配置文件

    SSH框架整合--基于XML配置文件 @(Spring)[spring, struts2, hibernate, 框架整合, ssh, Spring] SSH框架整合基于XML配置文件 SSH框架整合 ...

  2. 为什么大多数IOC容器使用ApplicationContext,而不用BeanFactory

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 1. 引言 Spring框架附带了两个IOC容器– Bea ...

  3. openOffice word转pdf,pdf转图片优化版

    之前写了一个版本的,不过代码繁琐而且不好用,效率有些问题.尤其pdf转图片速度太慢.下面是优化版本的代码. spriing_boot 版本信息:2.0.1.RELEASE 1.配置信息: packag ...

  4. Android性能优化

    Android性能优化 一.布局优化: 1.优化方式: 减少不必要嵌套,尽可能使用LinearLayout.ConstraintLayout等控件 减少颜色覆盖,子控件背景足够,不要在其父控件加颜色 ...

  5. Android性能优化 - 内存优化

    性能优化系列阅读 Android性能优化 性能优化 - 消除卡顿 性能优化- 内存优化 性能分析工具 - TraceView Android性能分析工具 为什么内存优化? 在一个商业项目中,很有可能因 ...

  6. android 关于内存优化的一些总结

    内存泄漏 如果app存在内存泄漏没有解决,就先不要想其它的了,先把内存泄漏解决掉 图片展示 以glide为例: 1.ImageView一定不要宽高都使用wrap_content 2.尽量要求后台根据具 ...

  7. API性能优化之异步

    很多时候为了达到SLA承诺(Service-Level Agreement)即为"服务水平承诺,我们需要拼命的优化我们的api的性能,其中最有效的便是异步了 异步和同步: 定义:同步和异步关 ...

  8. Android Note - 内存优化

    内存优化是Android性能优化的重点内容,一般来说,谈及性能优化,肯定避不开内存优化.虽然现在手机内存都很大,但并不意味着我们的App在使用内存时能"为所欲为".这篇就简单地总结 ...

  9. java异步处理同步化_java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring EventListener,超时处理和空循环性能优化...

    异步转同步 业务需求 有些接口查询反馈结果是异步返回的,无法立刻获取查询结果. 正常处理逻辑 触发异步操作,然后传递一个唯一标识. 等到异步结果返回,根据传入的唯一标识,匹配此次结果. 如何转换为同步 ...

最新文章

  1. 【全网之最】最短代码清除数组中的假、空值(0、空、null、undefined、false)
  2. MII 功能简介(论坛整理)
  3. jQuery中DOM操作
  4. Load 和 DOMContentLoaded 区别
  5. Oracle SQL语句执行过程
  6. ADB server didn't ACK
  7. SSM(Spring+Spring MVC+Mybatis)整合 1:整体概述、目录内容及实验环境介绍
  8. Mybatis源码之(TypeAliasRegistry)TypeAlias别名实现机制
  9. Vx2Text-多模态任务新进展!哥大Facebook提出VX2TEXT模型,实现了“视频+X”到“文本”的任务...
  10. java 我爱你_Java初级教程-课程笔记
  11. 运气真不错:3月取到的TeaVM恰好能够运行,之前之后都有问题
  12. 基于Matlab系统的HVDC系统
  13. linux 查看gc情况
  14. python爬取豆瓣读书界面的书名、作者、价格、导入数据库_python爬虫:利用正则表达式爬取豆瓣读书首页的book...
  15. 读书笔记—CLR via C#章节4-7
  16. 街道大动土,断网一周,学习计划照旧
  17. python怎么输出坐标_使用Python实现图像标记点的坐标输出功能
  18. 如何实现WiFi与5G无缝切换?如何进行无线通信切换测试?(一)
  19. iOS开发UI篇章之应用管理的九宫格坐标计算
  20. 文本分类:垃圾邮件分类

热门文章

  1. chengxuyuan
  2. c++ 凸包 分治算法_三维凸包
  3. aws生态系统集成商_通过通用数据访问扩展AWS生态系统
  4. aws lambda_AWS Lambda事件源映射:使您的触发器混乱无序
  5. java world_Java World中的GraphQL简介
  6. spring async_Spring Async和Java的8 CompletableFuture
  7. 启动jboss_3种启动JBoss BPM流程的基本方法
  8. java identity_仔细研究Java Identity API
  9. Corda服务的异步流调用
  10. Lambda运行时内部:窥视无服务器巢穴