前几天由于公司项目架构调整,想将以前代码开发为主改成配置文件配置为主,即所有的外部服务调用由配置文件组织,因为必须高效,所以涉及包括调用顺序,并发调用等,但配置文件的缺陷是只能实现简单的业务逻辑,所以我们还用了jeval表达式Jar包。
废话不多说,由于服务配置文件是放在Maven项目下的一个子模块的classpath下,该子模块在eclipse下运行是以用文件系统路径来扫描到并解析的,但在线上环境,该子模块是会被打成Jar包,就是说线上环境是需要解析该子模块的Jar包才能取到配置文件的。
Jar包本质上是压缩文件,以前也做个在压缩文件中解析配置文件,但感觉不太专业,由于时间赶,不想在网上捞资料,而且靠不靠谱也不一定,于是想到了借鉴Spring中的扫描和解析配置文件的功能代码。
(转载请注明出处:http://manzhizhen.iteye.com/blog/2244806)
我们经常用如下Spring配置来解析资源文件和扫描class:
<context:component-scan base-package="com.manzhizhen.server.service,com.manzhizhen.server.aop" />
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:conf/resource1.properties</value>
</list>
</property>
</bean>
我本地已经有Spring4的源码,于是我直接在源码中搜索base-package关键字,于是定位到ComponentScanBeanDefinitionParser类:
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
然后我搜索哪些类用到了BASE_PACKAGE_ATTRIBUTE,于是找到了
ComponentScanBeanDefinitionParser类:
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
... ...
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
ComponentScanBeanDefinitionParser类的作用就是将解析来的xml元素转换成Bean定义,并将他们注册到上下文中,所以我可以从这里开始追踪Spring是如何根据我们定义的class路径去扫描class文件的。
其中,element.getAttribute(BASE_PACKAGE_ATTRIBUTE)的值就是我们配置的"com.manzhizhen.server.service,com.manzhizhen.server.aop",而ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS只是在Spring中预定义的配置路径分隔符而已,比如“.;\t\n”,最后经过分隔,得到的String[] basePackages就是com.manzhizhen.server.service和com.manzhizhen.server.aop组成的字符串列表了。
我们发现,代码Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);就已经把我们配置的两个package下的所有class解析出来了,所以我决定看看scanner.doScan(basePackages)里面到底做了什么,于是我们来到了ClassPathBeanDefinitionScanner#doScan:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
由于上面加黑的代码就已经将class扫描出来了,于是去看看findCandidateComponents方法是怎么实现的:
/**
* Scan the class path for candidate components.
* @param basePackage the package to check for annotated classes
* @return a corresponding Set of autodetected bean definitions
*/
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
代码String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;将我们的包路径组装成Spring中能识别的格式,如把 “com.manzhizhen.server.service” 变成 "classpath*:com.manzhizhen.server.service/**/*.class",对,就是对前后做了补充,给后面的统一解析操作提供必要的指引。我们发现代码 Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); 就已经将所有的class扫描出来了,于是我们看看里面做了些什么,于是追踪到了GenericApplicationContext#getResources:
/**
* This implementation delegates to this context's ResourceLoader if it
* implements the ResourcePatternResolver interface, falling back to the
* default superclass behavior else.
* @see #setResourceLoader
*/
@Override
public Resource[] getResources(String locationPattern) throws IOException {
if (this.resourceLoader instanceof ResourcePatternResolver) {
return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern);
}
return super.getResources(locationPattern);
}
加黑部分,发现它是调了父类的方法
AbstractApplicationContext#
getResources:
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
this.resourcePatternResolver 是
PathMatchingResourcePatternResolver类的对象,我们看看它的
getResources 方法:
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
CLASSPATH_ALL_URL_PREFIX 是 PathMatchingResourcePatternResolver 的实现接口
ResourcePatternResolver 中定义的常量:
/**
* Pseudo URL prefix for all matching resources from the class path: "classpath*:"
* This differs from ResourceLoader's classpath URL prefix in that it
* retrieves all matching resources for a given name (e.g. "/beans.xml"),
* for example in the root of all deployed JAR files.
* @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
其值就是前面Spring给包路径加的前缀。
我们回到 PathMatchingResourcePatternResolver#getResources 的那段代码,继续往下看,getPathMatcher() 返回的是 AntPathMatcher 类的对象,咱们看看它的 isPattern 方法:
public boolean isPattern(String path) {
return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
}
由于前面Spring对包路径的加工,我们很幸运的就匹配上了,于是我们进入了下面的
findPathMatchingResources(locationPattern); 方法,我们看看实现:
/**
* Find all resources that match the given location pattern via the
* Ant-style PathMatcher. Supports resources in jar files and zip files
* and in the file system.
* @param locationPattern the location pattern to match
* @return the result as Resource array
* @throws IOException in case of I/O errors
* @see #doFindPathMatchingJarResources
* @see #doFindPathMatchingFileResources
* @see org.springframework.util.PathMatcher
*/
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<Resource>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else if (rootDirResource.getURL().getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirResource, subPattern, getPathMatcher()));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[result.size()]);
}
第一行
String rootDirPath = determineRootDir(locationPattern); 得到的
rootDirPath 值为“classpath*:com/kuaidadi/liangjian/allconfig/server/service/”, 即资源文件根目录。第二行
String subPattern = locationPattern.substring(rootDirPath.length()); 得到的
subPattern 是
“**/*.class”,即需要扫描的资源文件类型。接下来的
Resource[] rootDirResources = getResources(rootDirPath); 将该资源根路径解析成Spring中的资源对象。 其实
getResources 和
findPathMatchingResources 之间会相互调用。请看上面代码我对两个方法进行了加黑:
doFindPathMatchingJarResources 和
doFindPathMatchingFileResources,这两个方法分别完成Jar包和文件系统资源的扫描工作,doFindPathMatchingFileResources方法实现比较简单,文件系统的读取大家都会,咱们看看Spring是如何解析Jar包中的资源的,
doFindPathMatchingJarResources 方法源码如下:
/**
* Find all resources in jar files that match the given location pattern
* via the Ant-style PathMatcher.
* @param rootDirResource the root directory as Resource
* @param subPattern the sub pattern to match (below the root directory)
* @return the Set of matching Resource instances
* @throws IOException in case of I/O errors
* @see java.net.JarURLConnection
* @see org.springframework.util.PathMatcher
*/
protected Set<Resource> doFindPathMatchingJarResources(Resource rootDirResource, String subPattern)
throws IOException {
URLConnection con = rootDirResource.getURL().openConnection();
JarFile jarFile;
String jarFileUrl;
String rootEntryPath;
boolean newJarFile = false;
if (con instanceof JarURLConnection) {
// Should usually be the case for traditional JAR files.
JarURLConnection jarCon = (JarURLConnection) con;
jarCon.setUseCaches(false);
jarFile = jarCon.getJarFile();
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
JarEntry jarEntry = jarCon.getJarEntry();
rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
}
else {
// No JarURLConnection -> need to resort to URL file parsing.
// We'll assume URLs of the format "jar:path!/entry", with the protocol
// being arbitrary as long as following the entry format.
// We'll also handle paths with and without leading "file:" prefix.
String urlFile = rootDirResource.getURL().getFile();
int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
jarFileUrl = urlFile.substring(0, separatorIndex);
rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
jarFile = getJarFile(jarFileUrl);
}
else {
jarFile = new JarFile(urlFile);
jarFileUrl = urlFile;
rootEntryPath = "";
}
newJarFile = true;
}
try {
if (logger.isDebugEnabled()) {
logger.debug("Looking for matching resources in jar file [" + jarFileUrl + "]");
}
if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
// Root entry path must end with slash to allow for proper matching.
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set<Resource> result = new LinkedHashSet<Resource>(8);
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
if (entryPath.startsWith(rootEntryPath)) {
String relativePath = entryPath.substring(rootEntryPath.length());
if (getPathMatcher().match(subPattern, relativePath)) {
result.add(rootDirResource.createRelative(relativePath));
}
}
}
return result;
}
finally {
// Close jar file, but only if freshly obtained -
// not from JarURLConnection, which might cache the file reference.
if (newJarFile) {
jarFile.close();
}
}
}
这就拿到了我想要的代码的,我 定义了一个
ResourceTool 类,其中做了简化处理:
public class ResourceTool {
/**
* 获取默认的类加载器
*
* @return
*/
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable ex) {
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
}
return cl;
}
/**
* 获取配置文件资源对象
*
* @param location
* @return
* @throws IOException
*/
public static List<URL> findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
Enumeration<URL> resourceUrls = getDefaultClassLoader().getResources(location);
List<URL> result = Lists.newArrayList();
while (resourceUrls.hasMoreElements()) {
result.add(resourceUrls.nextElement());
}
return result;
}
/**
* 获取指定路径下的指定文件列表
*
* @param rootFile 文件路径
* @param extensionName 文件扩展名
* @return
*/
public static List<File> getFiles(File rootFile, String extensionName) {
List<File> fileList = Lists.newArrayList();
String tail = null;
if (extensionName == null) {
tail = "";
} else {
tail = "." + extensionName;
}
if (rootFile == null) {
return fileList;
} else if (rootFile.isFile() && rootFile.getName().endsWith(tail)) {
fileList.add(rootFile);
return fileList;
} else if (rootFile.isDirectory()) {
File[] files = rootFile.listFiles();
for (File file : files) {
if (file.isFile() && file.getName().endsWith(tail)) {
fileList.add(file);
} else if (file.isDirectory()) {
fileList.addAll(getFiles(file, extensionName));
}
}
}
return fileList;
}
public static List<URL> getJarUrl(URL rootUrl, String extensionName) throws IOException {
List<URL> result = Lists.newArrayList();
if (rootUrl == null || !"jar".equals(rootUrl.getProtocol())) {
return result;
}
if (StringUtils.isNotBlank(extensionName)) {
extensionName = "." + extensionName;
}
if (extensionName == null) {
extensionName = "";
}
URLConnection con = rootUrl.openConnection();
JarURLConnection jarCon = (JarURLConnection) con;
jarCon.setUseCaches(false);
JarFile jarFile = jarCon.getJarFile();
JarEntry jarEntry = jarCon.getJarEntry();
String rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
rootEntryPath = rootEntryPath + "/";
}
for (Enumeration<JarEntry> entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
String entryPath = entry.getName();
if (entryPath.startsWith(rootEntryPath)) {
String relativePath = entryPath.substring(rootEntryPath.length());
if (relativePath.endsWith(".service")) {
result.add(createRelative(rootUrl, relativePath));
}
}
}
return result;
}
private static URL createRelative(URL url, String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
return new URL(url, relativePath);
}
}
使用举例:
/**
* 将配置内容转换成内置对象
*
* @return
*/
private Map<String, ServiceSetting> getServiceSettingList(String path) {
Map<String, ServiceSetting> map = Maps.newHashMap();
try {
List<URL> urlList = ResourceTool.findAllClassPathResources(path);
for (URL url : urlList) {
String protocol = url.getProtocol();
// org.springframework.util.ResourceUtils
if (ResourceUtils.URL_PROTOCOL_JAR.equals(protocol)) {
// 资源文件扩展名为"service"
List<URL> result = ResourceTool.getJarUrl(url, "service");
for (URL jarUrl : result) {
URLConnection connection = jarUrl.openConnection();
try {
/**
* 得到InputStream,即可解析配置文件
*/
ServiceSetting serviceSetting = reloadServiceSetting(connection.getInputStream());
/**
* 检查服务配置正确性
*/
boolean check = checkServiceSetting(serviceSetting);
if (check) {
map.put(serviceSetting.getName(), serviceSetting);
logger.info("成功加载文件:" + jarUrl.getFile() + ", serviceSetting:"
+ JsonUtil.toJson(serviceSetting));
}
} catch (Exception e) {
// TODO:
}
}
} else if (ResourceUtils.URL_PROTOCOL_FILE.endsWith(protocol)) {
// ...StringUtils
File file = new File(
new URI(StringUtils.replace(url.toString(), " ", "%20")).getSchemeSpecificPart());
//// 资源文件扩展名为"service"
List<File> fileList = ResourceTool.getFiles(file, "service");
for (File serviceFile : fileList) {
ServiceSetting serviceSetting = reloadServiceSetting(new FileInputStream(serviceFile));
/**
* 检查服务配置正确性
*/
boolean check = checkServiceSetting(serviceSetting);
if (check) {
map.put(serviceSetting.getName(), serviceSetting);
logger.info("成功加载文件:" + serviceFile.getPath() + ", serviceSetting:"
+ JsonUtil.toJson(serviceSetting));
}
}
}
}
return map;
} catch (Exception e) {
// TODO:
}
}
是不是相当简单?
分享到:
相关推荐
在"spring 所有包和配置文件"这个主题中,我们将深入探讨Spring框架的主要组件、配置文件以及它们如何协同工作。 首先,Spring框架的核心在于它的容器,主要有两种:BeanFactory和ApplicationContext。BeanFactory...
通过Spring的XML配置文件,我们可以灵活地定义、组合和管理切面,实现代码的解耦和模块化。这种方式虽然相比注解方式略显繁琐,但对于大型项目或者需要精细控制AOP配置的情况,仍然是一个很好的选择。通过深入理解和...
在Spring框架中,加载多个配置文件是常见的需求,特别是在大型项目中,为了保持代码的整洁和模块化,通常会将不同的配置分散到多个文件中。本文将深入探讨如何在Spring中实现这一功能。 首先,Spring提供了多种方式...
- **约束条件**:Spring框架通过在配置文件中使用特定的元素和属性来定义约束条件。例如,可以使用`<context:component-scan>`来指定扫描特定包下的组件;或者使用`<aop:aspectj-autoproxy>`来启用基于AspectJ的自动...
为了使Spring Boot能够找到JAR内的配置文件,可以在`src/main/resources`目录下创建`META-INF/spring.factories`文件,并添加以下内容: ``` org.springframework.boot.autoconfigure.EnableAutoConfiguration=...
在本案例中,我们将探讨如何在已有的MySQL数据库环境下,配置Spring和Hibernate来实现数据访问层。 首先,我们需要在项目中引入Spring和Hibernate的相关依赖。在Maven或Gradle的构建文件中,添加对应的依赖库,如...
3. Bean 生产和实例分发:在 Spring 配置文件中,可以定义 Bean 的生产和实例分发策略,例如 singleton、prototype 等。Spring 容器将根据配置文件中的定义,生产和分发 Bean 实例。 下面是一个比较完整的 Spring ...
Spring框架是Java开发中最常用的轻量级框架之一,它的核心在于IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented ...深入学习Spring4的jar包和配置文件,将使你更好地驾驭Java开发环境,提高软件工程能力。
解决异常: org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class ...applicationContext.xml:不同版本的jar对应不同的配置文件描述,重要参考!
此外,配置文件的位置和名称对于Spring来说是敏感的。如果存在同名的配置文件,但位于不同的位置,根据Spring加载类路径资源的策略,可能会加载到一个错误的文件。为了避免这种情况,开发者需要仔细设计自己的目录...
在标题"spring定时器的包和配置文件"中,我们讨论的核心是Spring如何配置和使用定时器来自动化执行特定的任务。 首先,让我们了解Spring定时任务的基本概念。Spring定时器基于Java的`java.util.Timer`和`java.util....
### Spring框架中加载多个配置文件的方法 在Spring框架中,加载多个配置文件是常见的需求之一。这不仅可以帮助我们更好地组织...通过上述介绍和示例代码,相信您已经掌握了Spring框架中加载多个配置文件的相关知识。
总之,模拟Spring的XML配置文件注入是一个深入了解Spring框架和XML解析的好方法。通过SAXBuilder解析XML,结合反射和Java Bean机制,我们可以手动创建并管理对象的生命周期,实现依赖注入,从而更好地理解Spring的...
IDE如IntelliJ IDEA和Eclipse都有对Spring配置文件的优秀支持,可以进行自动提示、验证和重构,极大地提高了开发效率。 在实际项目中,我们往往将配置文件划分为多个部分,比如数据库连接池配置、数据访问对象配置...
在配置文件中,`<context:component-scan>`元素用于扫描指定包下的所有类,自动发现和注册带有特定注解的bean: ```xml ``` Spring的AOP功能可以通过`<aop:config>`和`<aop:aspect>`元素进行配置,定义切面、通知...
### Spring之AOP配置文件详解 #### 一、前言 在Java开发中,Spring框架因其强大的功能和灵活的配置而被广泛应用于企业级应用的开发。其中,面向切面编程(Aspect Oriented Programming,简称AOP)是Spring框架的...
本篇文章将深入探讨Spring配置文件中的归类,主要包括IOC(Inversion of Control,控制反转)和AOP(Aspect-Oriented Programming,面向切面编程)两个重要概念。 ### 1. IOC (控制反转) IOC是Spring的核心特性,...
Spring MVC 的配置文件主要用于管理整个应用的结构和行为。通过 DispatcherServlet,我们可以控制请求的处理流程,并通过不同的 HandlerMapping 实现灵活的 URL 映射机制。这些配置有助于开发者构建高效、可扩展的 ...
接下来,我们来配置Spring Boot的`application.yml`或`application.properties`文件以连接到Kafka集群。这里是一个示例配置: ```yaml spring: kafka: bootstrap-servers: localhost:9092 # Kafka服务器地址 ...
"Spring配置文件spring-context.zip"包含了Spring框架中的上下文配置,这是Spring管理对象及服务的核心。 `applicationContext.xml`是Spring应用上下文的主配置文件,它定义了bean的声明、bean之间的依赖关系以及...