优化您的ApplicationContext

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/365237.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux多线程编程(中嵌教育-嵌入式linux开发课件),linux多线程编程(中嵌教育-嵌入式linux开发课件).ppt...

linux多线程编程(中嵌教育-嵌入式linux开发课件).pptlinux多线程编程 Linux下线程概述 linux线程实现 1、Linux下线程概述 进程是系统中程序执行和资源分配的基本单位。每个进程有自己的数据段、代码段和堆栈段。 线程通常叫做轻型的进程。线程是在共享内存空间中并发执行的多道…

oracle group by 多类别_python数据关系型图表散点图系列多数据系列

多数据系列多数据系列的散点图需要使用不同的填充颜色(fill)和数据点形状(shape)这两个视觉特征来表示数据系列&#xff1b;绘制多数据系列散点图多数据系列散点图就是在单数据系列上添加新的数据系列&#xff1b;使用不同的填充颜色或形状区分数据系列&#xff1b;plotnine绘制…

图片和视频压缩例子

/*** 视频压缩 需要jave jar 包。到网上下载即可 * * param source 需要压缩的视频* param targetPath 压缩的目标路径* return*/public static boolean compressVideo(File source, String targetPath) {System.out.println("source:" source);System.out.println…

Web 开发中应用 HTML5 技术的10个实例教程

HTML5 作为下一代网站开发技术&#xff0c;无论你是一个 Web 开发人员或者想探索新的平台的游戏开发者&#xff0c;都值得去研究。借助尖端功能&#xff0c;技术和 API&#xff0c;HTML5 允许你创建响应性、创新性、互动性以及令人惊叹的漂亮网站。更进一步&#xff0c;你也可以…

linux ptrace 内核源码分析,linux 3.5.4 ptrace源码分析分析(系列一)

ptrace是linux系统中为了调试专门设立的一种系统调用。要想调试调试一个进程&#xff0c;有两种方式&#xff1a;PTRACE_TRACEME和PTRACE_ATTACH。这两种方式的主要区别可以概括为&#xff1a;PTRACE_TRACEME是子进程主动申请被TRACE。而PTRACE_ATTACH是父进程自己要attach到子…

在单元测试中访问私有字段

首先&#xff0c;让我大声说一下&#xff0c;您需要将代码设计为可测试的&#xff0c;以便通过公共方法测试私有字段。 但是&#xff0c;&#xff08;“ buts”是人们仍在编程而不是计算机本身的原因&#xff0c;所以在这里很高兴&#xff09;有时您想要并且应该更改一些私有字…

【LeetCode题解】160_相交链表

目录 160_相交链表描述解法一&#xff1a;哈希表思路Java 实现Python 实现解法二&#xff1a;双指针&#xff08;推荐&#xff09;思路Java 实现Python 实现160_相交链表 描述 编写一个程序&#xff0c;找到两个单链表相交的起始节点。 例如&#xff0c;下面的两个链表&#xf…

maya崩溃自动保存路径_maya 使用swig将插件编译成pyd,无缝使用内置数据实现加速计算模块...

前言&#xff1a;原本目的是想寻求一种方式来对cpu计算密集型代码部分进行加速替代&#xff0c;但是maya中mll插件的插件套路在传递参数上会占用大量的io&#xff0c;对于数据比较大的部分也会有相当消耗。如果全部写在c部分又感觉缺乏灵活性&#xff0c;所以琢磨的一种可以在p…

VS2010中预处理器定义

vs2010下的预处理器定义就是使该预定义下的宏定义在每个文件中都包括&#xff0c;便于跨平台编码格式或者其他的一些设置&#xff0c;便于处理&#xff0c;值得注意的是工程移植的时候需要考虑预处理定义否则代码运行的环境可能不同&#xff0c;导致结果出错。 详解&#xff1a…

Slip.js – 在触摸屏上实现 Swipe 对列表重新排序

Slip.js 是一个很小的 JavaScript 库&#xff0c;用于实现对触摸屏的互动 Swipe 和对元素重新排序列表&#xff08;Reordering&#xff09;。Slip.js 没有任何的依赖&#xff0c;你可以通过自定义 DOM 事件实现重新排序交互。 您可能感兴趣的相关文章Pace.js – 页面加载进度自…

suse10 linux安装,SuSE10.2 安装手记

SuSE10.2 安装手记发布时间:2007-04-05 00:31:51来源:红联作者:Reference1. 添加安装源SuSE提供了多种安装源的管理&#xff0c;你可以通过Yast方便的添加和删除各种安装源。(1) 本地安装源&#xff1a;YaST -> Software -> Installation Source -> Add -> Local D…

构建和运行Java 8支持

尚未提供对Java 8的Eclipse支持。 如果要使用它&#xff0c;则必须构建它。 Eclipsepedia的JDT Core / Java8页面包含有关使用Eclipse Java开发工具 &#xff08;JDT&#xff09;中不断发展的Java 8支持源来设置开发环境的说明。 说明中缺少一些内容&#xff1b; 待会儿我会回圈…

django异常日志_【python小随笔】Django+错误日志(配置Django报错文件指定位置)...

1: 自定义日志文件.py----------几个文件需要创建日志&#xff0c;就需要重新定义几份#1定义一个日志文件 创建一个操作日志对象loggerfile_1 logging.FileHandler(text_1.log, a, encodingutf-8) # text_1.log 定义日志文件名fmt logging.Formatter(fmt"%(asctime)s - %…

Django之管理权限

什么是权限&#xff1a; 谁对什么资源能做什么操作。 管理权限的实现有很多&#xff0c;这里实现一个最简单的管理权限的实现方式&#xff1a;rbac &#xff08; role based access control &#xff09; 实现的一个基本思路&#xff1a; 一张user用户表&#xff0c;一张role…

狄克斯特拉 Dijkstra 算法 C#实现

今天在看《算法图解》&#xff0c;看了加权最小路径算法&#xff0c;决定用代码实现一下。 首先是画有向图&#xff0c;在网上找了一下&#xff0c;有不错的开源软件graphviz,该源代码托管在GitLab上。该软件是一个图形可视化软件。 画了一个有向图如下&#xff1a; 画图用的代…

So Easy! 让开发人员更轻松的工具和资源

这篇文章给大家分享让开发人员生活更轻松的免费工具和资源。所以&#xff0c;如果你正在寻找一些为迅速解决每天碰到的设计和开发问题的工具和资源&#xff0c;不要再观望&#xff0c;试试这些工具吧。这些奇妙的工具不仅会加快您的生产&#xff0c;也让你的工作质量提升。 您可…

Java 8:按集合分组

在继续阅读Venkat Subramaniam的“ Java函数式编程 ”时&#xff0c;我到达了介绍Stream&#xff03;collect函数的那部分。 我们想收集一个人&#xff0c;按年龄分组&#xff0c;并返回一张方便的&#xff08;年龄->人名&#xff09;地图。 要刷新&#xff0c;这是Person…

android linux截图库,Android中截图(surfaceView)源码

总结了一个方法&#xff0c;实现了在Android当前Activity的截图&#xff0c;本人测试确实通过了&#xff0c;不过有朋友说截出来的图是黑色的&#xff0c;不能看。我心想&#xff0c;这没有问题啊&#xff0c;相同的代码我就可以执行通过&#xff0c;并没有没有在意这个问题。可…

xmta温度控制仪说明书_XMT系列数显温度控制器使用说明书

测温元件名称分度号测温范围℃分辩率热电偶镍铬—铜镍E(EA—2)0 - 300、0 - 400、0 - 6001℃镍铬—镍硅K(EU—2)0-400、0-600、0-800、0-1300铂铑—铂S(LB—3)0 - 1600、700 - 1600热电阻铜电阻Cu50(G)-50-150、0-50、0-100、0-1500.1℃铂电阻Pt100(BA1、BA2)-199 - 199、0 - 1…

WARNING: 'aclocal-1.14' is missing on your system.

源码安装zabbix agent时进行到make install时报如下错误&#xff1a; WARNING: aclocal-1.14 is missing on your system.You should only need it if you modified acinclude.m4 orconfigure.ac or m4 files included by configure.ac.The aclocal program is part of the GNU…