当前位置: 首页 > news >正文

贵阳网站制作专业网站开发费的税率是多少

贵阳网站制作专业,网站开发费的税率是多少,2023年适合小学生的新闻,上海建章汽车服务有限公司背景#xff1a; Spring提供了IOC机制#xff0c;基于此我们可以通过XML或者注解配置#xff0c;将三方件注册到IOC中。问题是每个三方件都需要经过手动导入依赖、配置属性、注册IOC#xff0c;比较繁琐。 基于约定优于配置原则的自动装配机制为该问题提供了一…背景 Spring提供了IOC机制基于此我们可以通过XML或者注解配置将三方件注册到IOC中。问题是每个三方件都需要经过手动导入依赖、配置属性、注册IOC比较繁琐。 基于约定优于配置原则的自动装配机制为该问题提供了一个解决方案。 不同SpringBoot版本细节部分存在差异本文基于SpringBoot的2.3.2.RELEASE版本进行说明 1.自动装配机制 SpringBoot在启动时通过SPI机制扫描所有JAR包下的spring.factories文件将文件中EnableAutoConfiguration包含的配置类全部加载到容器中。 根据各个配置类的条件确定是否进行装载条件包括:容器中有无指定Bean类路径中有无指定Class对象等。配置类内部Bean的定义也可通过条件确定是否进行装载。 Spring在spring-boot-autoconfigure包中为三方件定义了很多配置类并提供了对应的starter依赖用户只需通过引入对应的starter依赖即可完成对应三方件的组装。 以redis为例 [1] 在pom.xml中添加redis对应的starter: dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId /dependency[2] 在spring配置文件中添加对redis的配置 spring:redis:host: localhostport: 6379timeout: 3000database: 0[3] 测试用例: RunWith(SpringRunner.class) SpringBootTest(classes {DemoApplication.class}) public class RedisComponentTest {Autowiredprivate RedisTemplateString, String redisTemplate;Testpublic void testRedis() {Assert.assertEquals(testValue, redisTemplate.opsForValue().get(testKey));} }Note在redis中添加testKey - testValue后该测试用例就可以运行成功。 原因分析 在spring-boot-autoconfigure的spring.factories文件中有如下定义: org.springframework.boot.autoconfigure.EnableAutoConfiguration\ ... org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ ...进入RedisAutoConfiguration配置类 Configuration(proxyBeanMethods false) ConditionalOnClass(RedisOperations.class) EnableConfigurationProperties(RedisProperties.class) Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration {BeanConditionalOnMissingBean(name redisTemplate)public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {//...}BeanConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {//...} }RedisAutoConfiguration自动配置类的装配条件是ConditionalOnClass(RedisOperations.class), 即类路径中包含RedisOperations.class. RedisOperations定义在spring-data-redis包中而依赖的spring-boot-starter-data-redis包含了对spring-data-redis的依赖。 另外在[SpringBoot系列-1 启动流程]中的也提到过使用jetty代替tomcat的方式 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactIdexclusionsexclusiongroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-tomcat/artifactId/exclusion/exclusions /dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jetty/artifactIdversion2.6.4/version /dependency即类路径中删除了对Tomcat的默认依赖添加了对Jetty的依赖在自动配置类 ServletWebServerFactoryConfiguration中因找不到Tomcat.class对象而不会装配Tomcat相关组件因引入了jetty的starter而装配Jetty容器。 2.自定义starter 除了SpringBoot自定义的starter外也有第三方自定义的starter, 如常见的mybatis: dependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion2.2.2/version /dependency用户也可基于SpringBoot提供的自动装配机制自定义starter从而可以从多个项目中抽出重复的逻辑以减少不必要的重复操作。本章通过一个完整的案例进行说明。 2.1 准备pom文件: groupIdcom.demo/groupId // [标注1] artifactIddemo-spring-boot-starter/artifactId version1.0.0/versiondependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-autoconfigure/artifactIdversion2.7.5/version/dependency /dependenciesNote 1: springboot官方的starter依赖基本是pom, 用于关联需要的依赖项。而用户或者第三方自定义时starter需要包含配置类、依赖项、spring.factories文件。另外命名时需要遵循命名规范springboot定义的形式如spring-boot-starter-xxx, 用户自定义的形式如xxx-spring-boot-starter. 只需引入spring-boot-autoconfigure依赖即可因为spring-boot-autoconfigure依赖了spring-boot而spring-boot依赖了spring. 2.2 定义属性配置类 属性配置类用于提供用户自定义能力 ConfigurationProperties(demo.configure) public class DemoProperties {private String userName;private String password;// getter和setter方法 }用户可以在spring.yml等配置文件中通过demo.configure对DemoProperties的userName和password属性进行配置。 2.3 定义服务类 服务类包含了该组件的核心逻辑: public class DemoService {private final DemoProperties demoProperties;public DemoService(DemoProperties demoProperties) {this.demoProperties demoProperties;}public Boolean check(String name, String password) {if (name null || password null) {return false;}return name.equals(demoProperties.getUserName()) password.equals(demoProperties.getPassword());} }此时提供了一个服务方法校验用户名和密码。 2.4 自动装配类 Configuration //导入属性配置类 EnableConfigurationProperties(DemoProperties.class) ConditionalOnClass(DemoService.class) public class DemoAutoConfiguration {Beanpublic DemoService demoService(DemoProperties demoProperties) {return new DemoService(demoProperties);} }添加了ConditionalOnClass(DemoService.class)表示当DemoService.class在类路径中时该自动装配类才会生效。 2.5 spring.factories文件 在resources目录下新增META-INF/spring.factories文件指定自动配置类: # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration\ com.caltta.demo.DemoAutoConfiguration2.6 使用方式 将上述的starter项目install到仓库后在其他项目中可通过如下方式引入: dependencygroupIdcom.caltta/groupIdartifactIddemo-spring-boot-starter/artifactIdversion1.0.0/version /dependency在application.yml文件中配置: demo:configure:userName: rootpassword: Root.123测试用例: RunWith(SpringRunner.class) SpringBootTest(classes {DemoApplication.class}) public class DemoComponentTest {Autowiredprivate DemoService demoService;Testpublic void testDemoService() {Assert.assertTrue(demoService.check(root, Root.123));} }测试用例可正常运行。 3.原理 3.1 SpringBootApplication注解 SpringBootApplication注解是由SpringBootConfiguration、EnableAutoConfiguration、ComponentScan注解组成的复合注解: (1) SpringBootConfiguration本质上是一个Configuration注解; (2) ComponentScan定义了包扫描路径; (3) EnableAutoConfiguration开启自动装配。 SpringBootApplication注解中定义了几个属性: (1) scanBasePackages/scanBasePackageClasses桥接给ComponentScan用于确定扫描包路径默认我注解类所在路径 (2) exclude/excludeName桥接给EnableAutoConfiguration用于排除自动装配的类 (3) proxyBeanMethods桥接给Configuration注解用于确定代理类型。 3.2 EnableAutoConfiguration注解 EnableAutoConfiguration由AutoConfigurationPackage和Import(AutoConfigurationImportSelector.class)组成 AutoConfigurationPackage Import(AutoConfigurationImportSelector.class) public interface EnableAutoConfiguration {Class?[] exclude() default {};String[] excludeName() default {}; }其中AutoConfigurationPackage注解用于向IOC添加一个BasePackages类型的Bean对象属性默认为注解所在类的包名。 Import(AutoConfigurationImportSelector.class)用于向容器导入AutoConfigurationImportSelector对象该部分是整个装配机制的关键。 3.3 AutoConfigurationImportSelector AutoConfigurationImportSelector是DeferredImportSelector接口的实现类更是ImportSelector接口的实现类。 selectImports方法如下 public String[] selectImports(AnnotationMetadata annotationMetadata) {// 判断是否开启自动装配if (!isEnabled(annotationMetadata)) {return {};}// 获取返回需要装配的类型列表AutoConfigurationEntry autoConfigurationEntry getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }Note: 在ConfigurationClassPostProcessor处理Import注解时对于DeferredImportSelector类型调用的是getAutoConfigurationEntry方法。 上述逻辑住就要包含两个方法: isEnabled方法表示是否开启自动装配逻辑如下: protected boolean isEnabled(AnnotationMetadata metadata) {if (getClass() AutoConfigurationImportSelector.class) {return getEnvironment().getProperty(spring.boot.enableautoconfiguration, Boolean.class, true);}return true; }Note以spring.boot.enableautoconfiguration为key从环境变量中取值如果为false则关闭自动装配其他情况(空或者false)开启。如在application.yml中配置“spring.boot.enableautoconfiguration”值为false即可关闭。 getAutoConfigurationEntry方法用于获取待装配的类 protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 获取注解属性即EnableAutoConfiguration的exclude和excludeNameAnnotationAttributes attributes getAttributes(annotationMetadata);// 根据SPI机制从spring.factories中加载EnableAutoConfiguration的值ListString configurations getCandidateConfigurations(annotationMetadata, attributes);// 去重因为spring.factories文件加载自多个jar包-可能有重复configurations removeDuplicates(configurations);// 根据EnableAutoConfiguration的exclude和excludeName进行排除SetString exclusions getExclusions(annotationMetadata, attributes);checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);// 对初步排除的结果进行再次过滤configurations getConfigurationClassFilter().filter(configurations);// 发送事件返回结果fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions); }getExclusions方法获取需要排除的装配类: protected SetString getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {SetString excluded new LinkedHashSet();excluded.addAll(asList(attributes, exclude));excluded.addAll(Arrays.asList(attributes.getStringArray(excludeName)));// 从环境变量中spring.autoconfigure.exclude指定的类型数组excluded.addAll(getExcludeAutoConfigurationsProperty());return excluded; }Note: 排除自动装配可通过EnableAutoConfiguration的exclude和excludeName属性也可通过在application.yml中设置spring.autoconfigure.exclude值来进行排除。 getConfigurationClassFilter().filter(configurations)方法对候选的自动装配类进行再一次过滤。 getConfigurationClassFilter()获取配置自动配置过滤器的主要逻辑如下: private ConfigurationClassFilter getConfigurationClassFilter() {//...ListAutoConfigurationImportFilter filters getAutoConfigurationImportFilters();this.configurationClassFilter new ConfigurationClassFilter(this.beanClassLoader, filters);//... }protected ListAutoConfigurationImportFilter getAutoConfigurationImportFilters() {return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader); }Note-1: 获取过滤器: 从spring.factories文件中获取AutoConfigurationImportFilter对应的值。spring-boot-autoconfigure包中的spring.factories文件中有如下定义: # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition默认情况下(无用户自定义三方件引入)只有OnBeanCondition、OnClassCondition、OnWebApplicationCondition三个过滤器。该过滤器与自动装配的元数据配合实现快速排除不必要的自动配置类加快容器启动速度。 Note-2: 构造ConfigurationClassFilter new ConfigurationClassFilter(this.beanClassLoader, filters)方法构造时传入了过滤器同时从类路径加载了元数据: ConfigurationClassFilter(ClassLoader classLoader, ListAutoConfigurationImportFilter filters) {// 加载META-INF/spring-autoconfigure-metadata.properties文件内容this.autoConfigurationMetadata AutoConfigurationMetadataLoader.loadMetadata(classLoader);this.filters filters; }Note-3: 执行过滤 ListString filter(ListString configurations) {String[] candidates StringUtils.toStringArray(configurations);boolean skipped false;for (AutoConfigurationImportFilter filter : this.filters) {boolean[] match filter.match(candidates, this.autoConfigurationMetadata);for (int i 0; i match.length; i) {if (!match[i]) {candidates[i] null;skipped true;}}}if (!skipped) {return configurations;}ListString result new ArrayList(candidates.length);for (String candidate : candidates) {if (candidate ! null) {result.add(candidate);}}return result; }逻辑较为清晰对每个候选的自动配置类都进行三个过滤器的过滤操作(调用过滤器的match方法)只有三个过滤器都返回true才会保留否则会被标记为false然后排除。skipped用于优化流程没有匹配失败情况可快速退出。 遍历过滤器调用filter.match(candidates, this.autoConfigurationMetadata)方法以OnClassCondition为例进行说明。 public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {// 省略日志...ConditionOutcome[] outcomes getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);boolean[] match new boolean[outcomes.length];for (int i 0; i outcomes.length; i) {match[i] (outcomes[i] null || outcomes[i].isMatch());}return match; }入参中: autoConfigurationClasses表示候选的自动装配类列表autoConfigurationMetadata表示加载的自动配置元数据。 getOutcomes方法根据autoConfigurationMetadata对每个候选的自动装配类生成一个匹配结果结果为空或者true表示匹配继续看getOutcomes方法实现细节 protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {if (autoConfigurationClasses.length 1 Runtime.getRuntime().availableProcessors() 1) {return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);} else {OutcomesResolver outcomesResolver new StandardOutcomesResolver(autoConfigurationClasses, 0, autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader());return outcomesResolver.resolveOutcomes();} }根据处理器个数进行优化确定是否折成两半分别进行本质还是调用了StandardOutcomesResolver的resolveOutcomes方法: public ConditionOutcome[] resolveOutcomes() {return getOutcomes(this.autoConfigurationClasses, this.start, this.end, this.autoConfigurationMetadata); }private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {ConditionOutcome[] outcomes new ConditionOutcome[end - start];for (int i start; i end; i) {String autoConfigurationClass autoConfigurationClasses[i];if (autoConfigurationClass ! null) {// 从元数据中获取ConditionalOnClass为key尾缀的值String candidates autoConfigurationMetadata.get(autoConfigurationClass, ConditionalOnClass);if (candidates ! null) {outcomes[i - start] getOutcome(candidates);}}}return outcomes; }Note: 在spring-boot-autoconfigure包中定义的spring-autoconfigure-metadata.properties文件有如下定义: org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.ConditionalOnClass\ org.springframework.data.redis.core.RedisOperations表示此阶段会根据类路径中是否存在RedisOperations类确定是否排除自动配置类RedisAutoConfiguration。 继续跟踪getOutcome(candidates)方法进入: private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {// 会根据类加载机制是否排除异常确定类是否存在if (ClassNameFilter.MISSING.matches(className, classLoader)) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class).didNotFind(required class).items(Style.QUOTE, className));}return null; }ClassNameFilter.MISSING的matches方法实现如下 public boolean matches(String className, ClassLoader classLoader) {return !isPresent(className, classLoader); } static boolean isPresent(String className, ClassLoader classLoader) {if (classLoader null) {classLoader ClassUtils.getDefaultClassLoader();}try {// 使用类加载器加载classNameresolve(className, classLoader);return true;} catch (Throwable ex) {return false;} }isPresent方法通过类加载器去类路径中加载加载成功则返回true,否则返回false. 上述为OnClassCondition过滤机制。 4.整体流程 对于一个SpringBoot项目已经知道了自动装配机制的实现原理现在再结合Configuration注解分章节梳理一下Bean的注入IOC的流程。 这部分需要读者对Spring启动流程和ConfigurationClassPostProcessor和SpringBoot启动流程有比较清晰的理解可参考Spring系列-11 Configuration注解原理 和 SpringBoot系列-1启动流程和 Spring系列-1 启动流程. 4.1 主配置类注入阶段 为表述方便使用SpringBoot系列-1启动流程中案例进行介绍如下所示 SpringBootApplication // 标注[1] public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);} }SpringBoot系列-1启动流程的章节-2.2中介绍在SpringApplication对象的run方法中刷新Spring容器前的准备阶段中通过BeanDefinitionLoader将主配置类导入IOC中即此时主配置类DemoApplication已被导入IOC容器。 4.2 获取配置类 配置类值被Configuration注解的Bean。ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor接口的实现类更是BeanFactoryPostProcessor接口实现类因此在容器刷新阶段会通过invokeBeanFactoryPostProcessors方法调用其勾子方法(时机比注入非懒加载靠前)。 调用勾子逻辑进入ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法: public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {// ⚠️第一阶段ListBeanDefinitionHolder configCandidates new ArrayList();String[] candidateNames registry.getBeanDefinitionNames();for (String beanName : candidateNames) {BeanDefinition beanDef registry.getBeanDefinition(beanName);if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) ! null) {} else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// Return immediately if no Configuration classes were foundif (configCandidates.isEmpty()) {return;}// Sort by previously determined Order value, if applicableconfigCandidates.sort((bd1, bd2) - {int i1 ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// ...// ⚠️第二阶段... }processConfigBeanDefinitions方法可以分为两个阶段 (1) 第一阶段从IOC容器中获取配置类; (2) 第二阶段解析配置类获取Bean对象并讲所有的Bean对象注入到IOC中. 实际上此时获取的configCandidates获取的就是 章节-4.1 主配置类注入阶段 中注入IOC的DemoApplication. 4.3 解析配置类启动自动装配 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {// ⚠️第一阶段...// ⚠️第二阶段ConfigurationClassParser parser new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);SetBeanDefinitionHolder candidates new LinkedHashSet(configCandidates);SetConfigurationClass alreadyParsed new HashSet(configCandidates.size());do {parser.parse(candidates);parser.validate();// ...SetConfigurationClass configClasses new LinkedHashSet(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);this.reader.loadBeanDefinitions(configClasses);// ...}while (!candidates.isEmpty());// ... }该阶段可以分为两个步骤解析出所有的Bean、注册解析得到的Bean。核心逻辑在于前者配置类解析依赖于解析器ConfigurationClassParser存在递归逻辑用图解表示如下: DemoApplication类上注解了SpringBootApplication继而间接注解了Import(AutoConfigurationImportSelector.class)在4.3 解析配置类阶段会通过ImportSelect逻辑导入AutoConfigurationImportSelector类从而启动自动装配过程。 Note: 上图中的条件过滤用于处理注解在自动配置类中添加的Conditional注解。 4.4 条件注解 条件注解的解析和判断在ConditionEvaluator类的shouldSkip中方法进行读者可自行阅读。
http://www.ihoyoo.com/news/74861.html

相关文章:

  • 网站建设实训 考核要求电商具体是做什么的上班
  • 杭州企业网站设计制作网站建设页面带声音
  • 网站建设及推广的书做网站的费用如何写分录
  • 九狐建设网站开发网站 数据库
  • 网站备案如何查询个人如何在百度上做广告
  • 农业信息网站建设网站项目建设与管理论文
  • 北海做网站有哪家个人网站可以做淘宝店铺名
  • 网站发号源码2016延庆上海网站建设
  • 极简风网站Wordpress优化图片插件
  • 海珠企业网站建设文山北京网站建设
  • 中专网站建设与数据管理是什么宁乡电商网站建设价格
  • 自建淘宝客APP网站模板商城系统网站建设开发
  • 做汽车新闻哪个网站好优秀的包装设计案例
  • 网站用什么字体专业的公司网站制作服务
  • 自己做影视网站网站 稳定性
  • wordpress福利网站源码卖米网站源码
  • html企业网站怎么做怎么自己制作个网站
  • seo资源网站 排名诸暨做网站
  • 手机网站底部悬浮菜单seo营销外包公司
  • 哪里有响应式网站企业长春站建筑
  • 重庆网捷网站建设技术有限公司做预算查价格的网站是哪个好
  • 企业门户网站功能列表垂直门户网站的盈利模式探讨
  • 播视频网站开发免费网页模版下载
  • 建设部网站官网查询网站建设需要哪些资料
  • 响应式网站建设公司‘永州静默管理
  • vs2015 网站开发教程网站建设头部代码
  • 蘑菇丁毕业设计网站浙江建设工程合同备案网站
  • 网站建设套用模板类的要多少钱网络平台制作方法
  • 如何做网站制作北京住房投资建设中心网站首页
  • 做网站如何做视频佛山市seo广告优化工具