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

手机网站建设比较好的公司高端网站设计建设

手机网站建设比较好的公司,高端网站设计建设,u盘搭建网站开发环境方法,东莞凤岗企业网站建设推广背景#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.zqtcl.cn/news/63610/

相关文章:

  • 品牌营销型网站网站开发用工工程师
  • 多语言网站实现完全菜鸟七天学会建网站
  • 以3d全景做的网站网站改版的宣传词
  • pc网站模板wordpress代码片段
  • 沈阳鹊起网站建设默认网站预览能能显示建设中
  • 什么学习网站建设企业如何进行网络营销
  • 网站设计佛山顺德公司营销型网站
  • 商丘网站开发怎么注册网站免费的吗
  • 最专业的佛山网站建设价格织梦cms网站搬家
  • 微网站建设报价新兴街做网站公司
  • joomla网站模板青岛高端网站设计公司
  • 网站顶部布局微网站促销版
  • 网站制作公司多少钱微信怎么建公众号
  • 北京网站建设制作开发企业网络组网
  • 男女做羞羞漫画网站wordpress下载教程
  • 廊坊微信网站建设做网站要学的代码
  • 怎么做区块链网站湖南建设人力资源官方网站
  • 网站建设及运维合同电子商务公司网站建立
  • 承接网站开发 app开发wordpress网站管理
  • 建网站要多少钱软件开发者能看到手机信息吗
  • 在华图做网站编辑安全的营销型网站制作
  • 网站建设公司违法网页制作工具可分为
  • 免费asp网站模板带后台电商运营主要做什么
  • 安仁网站制作包头市做网站公司
  • 河南省网站建设哪家好网站策划书格式
  • 网站建设公司山而asp网站图片轮播代码
  • 佛山网站建站建设电商网站的付款功能
  • 营销网站的策划方案怎么做免费网上商城模板
  • 上海中小企业网站百度广告搜索推广
  • 网站建设的一般流程是什么意思保定网页模板建站