@bean注解和@component注解的区别_通过源码查看 @Component,@Service 等注解是如何被解析的...

点击上方“匠心零度”,选择“设为星标”

做积极的人,而不是积极废人

a67656f51c5bc192df827898b31570b3.png

来源:my.oschina.net/floor/blog/4325651

  • 前言
  • 1.@Component解析流程
    • 找入口
    • 找核心方法
    • 概要分析
    • 2.查文档找思路
  • 3. 探寻@Component派生性流程
    • 1. 确定metadataReader
    • 2.查看match方法找重点方法
    • 逐步分析
  • 总结

前言

@Component和@Service都是工作中常用的注解,Spring如何解析?

1.@Component解析流程

找入口

Spring Framework2.0开始,引入可扩展的XML编程机制,该机制要求XML Schema命名空间需要与Handler建立映射关系。

该关系配置在相对于classpath下的/META-INF/spring.handlers中。

5876f3701e4796eb0937b9c1caadad3b.png

如上图所示 ContextNamespaceHandler对应context:... 分析的入口。

找核心方法

浏览ContextNamespaceHandler

c5b8307438763e035dfa5983daebaa1d.gif

在parse中有一个很重要的注释

// Actually scan for bean definitions and register them.

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);

大意是:ClassPathBeanDefinitionScanner#doScan是扫描BeanDefinition并注册的实现

ClassPathBeanDefinitionScanner 的源码如下:

protected Set doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {//findCandidateComponents 读资源装换为BeanDefinition
      Set 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;
}

上边的代码,从方法名,猜测:

findCandidateComponents:从classPath扫描组件,并转换为备选BeanDefinition,也就是要做的解析@Component的核心方法。

概要分析

findCandidateComponents在其父类ClassPathScanningCandidateComponentProvider 中。

public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
//省略其他代码
public Set findCandidateComponents(String basePackage) {
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      return scanCandidateComponents(basePackage);
   }
}
private Set scanCandidateComponents(String basePackage) {
   Set candidates = new LinkedHashSet<>();try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);//省略部分代码for (Resource resource : resources) {//省略部分代码if (resource.isReadable()) {try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setSource(resource);if (isCandidateComponent(sbd)) {
                     candidates.add(sbd);//省略部分代码
      }
   }catch (IOException ex) {//省略部分代码 }return candidates;
}
}

findCandidateComponents大体思路如下:

  1. String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX resolveBasePackage(basePackage) + '/' + this.resourcePattern;                            将package转化为ClassLoader类资源搜索路径packageSearchPath,例如:com.wl.spring.boot转化为classpath*:com/wl/spring/boot/**/*.class
  2. Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 加载搜素路径下的资源。
  3. isCandidateComponent 判断是否是备选组件
  4. candidates.add(sbd); 添加到返回结果的list

ClassPathScanningCandidateComponentProvider#isCandidateComponent其源码如下:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    //省略部分代码
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

includeFilters由registerDefaultFilters()设置初始值,有@Component,没有@Service啊?

protected void registerDefaultFilters() {
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
      logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
   }
   catch (ClassNotFoundException ex) {
      // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
   }
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
      logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
   }
   catch (ClassNotFoundException ex) {
      // JSR-330 API not available - simply skip.
   }
}

Spring如何处理@Service的注解的呢????

2.查文档找思路

查阅官方文档,下面这话:

https://docs.spring.io/spring/docs/5.0.17.RELEASE/spring-framework-reference/core.html#beans-meta-annotations

@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, and @Controller are specializations of @Component

大意如下:

@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是派生自@Component。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// @Service 派生自@Component
@Component
public @interface Service {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any (or empty String otherwise)
    */
   @AliasFor(annotation = Component.class)
   String value() default "";

}

@Component是@Service的元注解,Spring 大概率,在读取@Service,也读取了它的元注解,并将@Service作为@Component处理。

3. 探寻@Component派生性流程

回顾ClassPathScanningCandidateComponentProvider 中的关键的代码片段如下:

private Set scanCandidateComponents(String basePackage) {
 //省略其他代码
 MetadataReader metadataReader
             =getMetadataReaderFactory().getMetadataReader(resource);
   if(isCandidateComponent(metadataReader)){
       //....
   }
}
public final MetadataReaderFactory getMetadataReaderFactory() {
   if (this.metadataReaderFactory == null) {
      this.metadataReaderFactory = new CachingMetadataReaderFactory();
   }
   return this.metadataReaderFactory;
}

1. 确定metadataReader

CachingMetadataReaderFactory继承自 SimpleMetadataReaderFactory,就是对SimpleMetadataReaderFactory加了一层缓存。

其内部的SimpleMetadataReaderFactory#getMetadataReader 为:

public class SimpleMetadataReaderFactory implements MetadataReaderFactory {
    @Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
    }

这里可以看出

MetadataReader metadataReader =new SimpleMetadataReader(...);

2.查看match方法找重点方法

535177a142a3d2a2280baad0d8f1a06a.gif

AnnotationTypeFilter#matchself方法如下:

@Override
protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

是metadata.hasMetaAnnotation法,从名称看是处理元注解,我们重点关注

逐步分析

找metadata.hasMetaAnnotation

metadata=metadataReader.getAnnotationMetadata();

metadataReader =new SimpleMetadataReader(...)

metadata= new SimpleMetadataReader#getAnnotationMetadata()

//SimpleMetadataReader 的构造方法
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
   InputStream is = new BufferedInputStream(resource.getInputStream());
   ClassReader classReader;
   try {
      classReader = new ClassReader(is);
   }
   catch (IllegalArgumentException ex) {
      throw new NestedIOException("ASM ClassReader failed to parse class file - " +
            "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
   }
   finally {
      is.close();
   }

   AnnotationMetadataReadingVisitor visitor =
            new AnnotationMetadataReadingVisitor(classLoader);
   classReader.accept(visitor, ClassReader.SKIP_DEBUG);

   this.annotationMetadata = visitor;
   // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
   this.classMetadata = visitor;
   this.resource = resource;
}

metadata=new SimpleMetadataReader(...)**.**getAnnotationMetadata()= new AnnotationMetadataReadingVisitor(。。)

也就是说

metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation

其方法如下:

public class AnnotationMetadataReadingVisitor{
    // 省略部分代码
@Override
public boolean hasMetaAnnotation(String metaAnnotationType) {
   Collection> allMetaTypes = this.metaAnnotationMap.values();for (Set metaTypes : allMetaTypes) {if (metaTypes.contains(metaAnnotationType)) {return true;
      }
   }return false;
}
}

逻辑很简单,就是判断该注解的元注解在,在不在metaAnnotationMap中,如果在就返回true。

这里面核心就是metaAnnotationMap,搜索AnnotationMetadataReadingVisitor类,没有发现赋值的地方??!。

查找metaAnnotationMap赋值

回到SimpleMetadataReader 的方法,

//这个accept方法,很可疑,在赋值之前执行
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
//省略其他代码
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
 this.annotationMetadata = visitor;
 }

发现一个可疑的语句:classReader.accept。

查看accept方法

public class ClassReader {
        //省略其他代码
public void accept(..省略代码){
    //省略其他代码
    readElementValues(
    classVisitor.visitAnnotation(annotationDescriptor, /* visible = */ true),
    currentAnnotationOffset,
     true,
    charBuffer);
}
}

查看readElementValues方法

public class ClassReader{
    //省略其他代码
private int readElementValues(final AnnotationVisitor annotationVisitor,final int annotationOffset,final boolean named,final char[] charBuffer) {
  int currentOffset = annotationOffset;
  // Read the num_element_value_pairs field (or num_values field for an array_value).
  int numElementValuePairs = readUnsignedShort(currentOffset);
  currentOffset += 2;
  if (named) {
    // Parse the element_value_pairs array.
    while (numElementValuePairs-- > 0) {
      String elementName = readUTF8(currentOffset, charBuffer);
      currentOffset =
          readElementValue(annotationVisitor, currentOffset + 2, elementName, charBuffer);
    }
  } else {
    // Parse the array_value array.
    while (numElementValuePairs-- > 0) {
      currentOffset =
          readElementValue(annotationVisitor, currentOffset, /* named = */ null, charBuffer);
    }
  }
  if (annotationVisitor != null) {
    annotationVisitor.visitEnd();
  }
  return currentOffset;
}
}

这里面的核心就是 annotationVisitor.visitEnd();

确定annotationVisitor

这里的annotationVisitor=AnnotationMetadataReadingVisitor#visitAnnotation

源码如下,注意这里传递了metaAnnotationMap!!

public class AnnotationMetadataReadingVisitor{
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
   String className = Type.getType(desc).getClassName();
   this.annotationSet.add(className);
   return new AnnotationAttributesReadingVisitor(
         className, this.attributesMap,
              this.metaAnnotationMap, this.classLoader);
}
}

annotationVisitor=AnnotationAttributesReadingVisitor

查阅annotationVisitor.visitEnd()

annotationVisitor=AnnotationAttributesReadingVisitor#visitEnd()

public class AnnotationAttributesReadingVisitor{
@Override
public void visitEnd() {
   super.visitEnd();

   Class extends Annotation> annotationClass = this.attributes.annotationType();
   if (annotationClass != null) {
      List attributeList = this.attributesMap.get(this.annotationType);if (attributeList == null) {this.attributesMap.add(this.annotationType, this.attributes);
      }else {
         attributeList.add(0, this.attributes);
      }if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {try {
            Annotation[] metaAnnotations = annotationClass.getAnnotations();if (!ObjectUtils.isEmpty(metaAnnotations)) {
               Set visited = new LinkedHashSet<>();for (Annotation metaAnnotation : metaAnnotations) {
                  recursivelyCollectMetaAnnotations(visited, metaAnnotation);
               }if (!visited.isEmpty()) {
                  Set metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());for (Annotation ann : visited) {
                     metaAnnotationTypeNames.add(ann.annotationType().getName());
                  }this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
               }
            }
         }catch (Throwable ex) {if (logger.isDebugEnabled()) {
               logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex);
            }
         }
      }
   }
}
}

内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap,也就是AnnotationMetadataReadingVisitor 中的metaAnnotationMap中。

总结

大致如下:

ClassPathScanningCandidateComponentProvider#findCandidateComponents

1、将package转化为ClassLoader类资源搜索路径packageSearchPath

2、加载搜素路径下的资源。

3、isCandidateComponent 判断是否是备选组件。

内部调用的TypeFilter的match方法:

AnnotationTypeFilter#matchself中metadata.hasMetaAnnotation处理元注解

metadata.hasMetaAnnotation=AnnotationMetadataReadingVisitor#hasMetaAnnotation

就是判断当前注解的元注解在不在metaAnnotationMap中。

AnnotationAttributesReadingVisitor#visitEnd()内部方法recursivelyCollectMetaAnnotations 递归的读取注解,与注解的元注解(读@Service,再读元注解@Component),并设置到metaAnnotationMap

4、添加到返回结果的list

END

如果读完觉得有收获的话,欢迎点【好看】,关注【匠心零度】,查阅更多精彩历史!!!

6c1532010ed8c3e15d2322394b472c94.gif

让我“好看” 9bf230cf99ee1bb68f9a9256220b5361.gif

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

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

相关文章

centos7已有数据硬盘挂载_干货!如何给虚拟机增加虚拟硬盘,Linux再分区挂载

很多朋友一开始给虚拟机的硬盘都很小&#xff0c;用着用着虚拟硬盘就不够了&#xff0c;今天小编就给大伙说说如何给虚拟机添加虚拟硬盘在到Linux系统下分区格式化再到挂载。点击创建新的虚拟盘我分配一个8g的硬盘吧&#xff01;现在启动centos 7查看一下你刚刚添加的8G的硬盘&…

diy机器人图片 手绘纸箱_废物利用,她用几个纸箱让家里变成动物园,孩子回家乐坏了!收藏...

纸箱是我们日常生活中最常见的一种废品&#xff0c;现在随着网购的崛起&#xff0c;家里的纸箱越来越多&#xff0c;这时候你是直接丢弃&#xff0c;还是会想想是否可以再利用的价值&#xff1f;这些看似没用的纸箱&#xff0c;在创意妈的眼中可是个宝贝呢~它可以变废为宝成为孩…

计算机研究生考426分单科多少,考研初试426分,依然没被录取,他犯的错误,值得大家参考!...

原标题&#xff1a;考研初试426分&#xff0c;依然没被录取&#xff0c;他犯的错误&#xff0c;值得大家参考&#xff01;最近几天&#xff0c;考研初试成绩陆续发布。考试分数比较高的考生&#xff0c;有机会参加复试&#xff0c;就要尽早准备复试复习了。复试成绩也很重要&am…

70进货卖100利润是多少_一只周黑鸭随便就卖100多,那成本有多少?说出来你可能不信...

一只周黑鸭动不动就卖100多&#xff0c;那成本有多少&#xff1f;说出来你可能不信。如果你吃过周黑鸭&#xff0c;不知道每次想起的时候&#xff0c;尤其是午夜时分会不会有流口水的感觉&#xff0c;那种麻辣的口感&#xff0c;再搭配上冰凉爽口的啤酒&#xff0c;发自心底的那…

powerquery加载pdf_pdf转换为excel,你不会,同事点点鼠标2分钟就搞定了

Hello.大家好&#xff0c;最近office365进行了一次小的更新&#xff0c;这次更新在excel中添加了获取pdf文件中的表格的功能&#xff0c;操作起来十分的简单&#xff0c;下面就跟大家分享下它是如何操作的首先我们新建一个excel文件&#xff0c;然后点击数据功能组&#xff0c;…

防火墙设置导致服务器站点打开,服务器、网站、环境配置全正常网站打不开原来是系统防火墙造成的...

大家都知道网站是架在服务器上的&#xff0c;通过域名解析指向网站并在服务器上绑定域名&#xff0c;上传网站程序到指定的目录&#xff0c;并只要有适合网站运行的环境&#xff0c;网站目录权限正常网站就肯定可以正常运行了&#xff0c;但今天我们技术遇到了一个问题以上所说…

web前端开发技术期末考试_智慧树来我校开展WEB前端开发微专业导学

11月17日&#xff0c;在6号教学楼6102阶梯教室&#xff0c;智慧树工作人员带来了WEB前端开发微专业导学。根据前期长青联盟微专业的报名情况及学生学习情况&#xff0c;为进一步提高学生学习微专业的质量&#xff0c;学院联系了微专业教学平台的专家对教学平台的使用进行技术指…

计算机辅助设计还需要手绘吗,西安电脑如此发达为什么还要学习手绘

在处理器以八核成为主流、硬盘以T而论的今天&#xff0c;计算机辅助设计软件是设计师的得力工具。用铅笔、尺子要时间才能画好的一个室内空间平面布置图&#xff0c;如果用CAD可能一个小时都不用就完成了&#xff0c;而且很方便修改&#xff0c;可见在效率方面&#xff0c;电脑…

一键生成通讯录的软件_橙瓜码字自动写作软件,外貌描写对话描写一键生成

很多写手对于以古代为背景的小说&#xff0c;偶尔都会对某些地方感到棘手&#xff0c;有时候是历史的背景&#xff0c;有时候是当时的官制、称呼&#xff0c;或是一些特有的服饰&#xff0c;外貌的描写&#xff0c;而面对难题&#xff0c;很多时候我们也是选择上网查询&#xf…

gin 静态文件服务器拒绝,nginx实现简单的图片服务器(windows)+静态文件服务器-Go语言中文社区...

需求&#xff1a;能够使用 http://localhost/目录/图片名 访问本地(服务器)已经存在的图片首先需要在本地将nginx跑起来&#xff0c;这里使用默认端口80&#xff1b;在浏览器使用http://localhost看到下面的界面证明nginx启动正常。下面需要修改nginx.conf来实现直接使用http:/…

wegame每次登陆都要滑动验证_Vue项目中实现用户登录及token验证

在前后端完全分离的情况下&#xff0c;Vue项目中实现token验证大致思路如下&#xff1a;1、第一次登录的时候&#xff0c;前端调后端的登陆接口&#xff0c;发送用户名和密码2、后端收到请求&#xff0c;验证用户名和密码&#xff0c;验证成功&#xff0c;就给前端返回一个toke…

云服务器 性能监控软件,云监控 - 云应用监控 - ManageEngine Applications Manager

云监控什么是云监控?尽管许多组织仍然依赖于在自托管的数据中心中存储数据的本地方法&#xff0c;但在业务组织中采用云服务的情况已经逐渐增多。自然&#xff0c;这导致了一些云监控工具的出现。无论您使用的是公共、私有还是混合环境&#xff0c;对应用程序性能具有端到端可…

mysql导入dat文件_MySql导入和抽取大数量级文件数据

一、情况介绍需要处理的文件是一个3.41G的csv格式文件&#xff0c;现在需要把它导入数据库&#xff0c;进行后续处理和分析。二、导入数据该文件数据量庞大&#xff0c;无法用excel或者editplus之类普通软件打开&#xff0c;于是借用了pandas的方法查看了表的结构&#xff1a;方…

服务器操作系统与安装步骤,服务器操作系统与安装步骤

服务器操作系统与安装步骤 内容精选换一换如果在创建弹性云服务器时未设置密码&#xff0c;或密码丢失、过期&#xff0c;可以参见本节操作重置密码。密码丢失或过期前&#xff0c;已安装密码重置插件。公共镜像创建的弹性云服务器默认已安装一键重置密码插件。私有镜像创建的云…

文件服务器mfs,分布式文件系统MFS(moosefs)实现存储共享

这种架构除了性能问题而外&#xff0c;还存在单点故障&#xff0c;一旦这个NFS服务器发生故障&#xff0c;所有靠共享提供数据的应用就不再可用&#xff0c;尽管用rsync方式同步数据到另外一个服务器上做nfs服务的备份&#xff0c;但这对提高整个系统的性能毫无帮助。基于这样一…

mysql 1215_mysql执行带外键的sql文件时出现mysql ERROR 1215 (HY000): Cannot add foreign key constraint的解决...

ERROR 1215 (HY000): Cannot add foreign key constraint最近在建表时遇到了这个错误&#xff0c;然后找了下找到了解决办法&#xff0c;记录下&#xff1a;本来是要建两张表&#xff1a;出现下面的提示&#xff1a;然后开始上网查&#xff0c;有的说是用的引擎不同的原因&…

mysql里面可以用正则吗_Mysql中使用正则表达式

最近在《MYSQL必知必会》这本书中读到在SQL中使用正则表达式&#xff0c;以前是学过数据库也学过正则表达式&#xff0c;但是不知道原来他们可以一起使用&#xff0c;这里做下记录。首先是Mysql使用正则表达式的语法&#xff1a;SELECT ... FROM ... WHERE ... REGEXP ...(这…

mysql数据库的备份和恢复的常用方法_MySQL数据库备份与恢复方法(转)

常有新手问我该怎么备份数据库&#xff0c;下面介绍3种备份数据库的方法&#xff1a;MySQL中的每一个数据库和数据表分别对应文件系统中的目录和其下的文件。在Linux下数据库文件的存放目录一般为/var/lib/mysql。在Windows下这个目录视MySQL的安装路径而定&#xff0c;DiaHost…

ruby mysql dbi_Ruby MySQL DBI实例

本次章节将介绍如何用Ruby来访问数据库。类似的Perl DBI模块为Ruby脚本的Ruby DBI模块提供了一种数据库(这里用MySQL讲解)独立的接口。DBI代表数据库无关的接口为RubyDBI提供的Ruby代码和底层数据库之间的抽象层&#xff0c;允许你真的很容易切换数据库实现。它定义了一套方法&…

mysql与tomcat_mysql数据库与tomcat服务器的一些细节问题

java程序使用数据库的方法1.使用jndi数据源核心代码&#xff1a;initCtx new InitialContext()DataSource ds;ds (DataSource) initCtx.lookup("java:comp/env/jdbc/mysql1");conn ds.getConnection();----------配置服务器信息web项目web.xml中配置MySQL DB Conn…