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

有域名 空间如何建网站网站qq联系怎么做

有域名 空间如何建网站,网站qq联系怎么做,wordpress官方的三个主题好排名,没有备案的网站怎么访问不了关于 Configuration 注解有一个特别经典的面试题#xff1a; Configuration 和 Component 有什么区别#xff1f; 无论小伙伴们之前是否背过相关的面试题#xff0c;今天这篇文章学完之后相信大家对这个问题都会有更深一层的理解#xff0c;废话不多少#xff0c;咱们开…关于 Configuration 注解有一个特别经典的面试题 Configuration 和 Component 有什么区别 无论小伙伴们之前是否背过相关的面试题今天这篇文章学完之后相信大家对这个问题都会有更深一层的理解废话不多少咱们开始分析。 1. 情景展现 Configuration 和 Component 到底有何区别呢我先通过如下一个案例在不分析源码的情况下小伙伴们先来直观感受一下这两个之间的区别。 Configuration public class JavaConfig01 { } Component public class JavaConfig02 { }首先分别向 Spring 容器中注入两个 BeanJavaConfig01 和 JavaConfig02其中JavaConfig01 上添加的是 Configuration 注解而 JavaConfig02 上添加的则是 Component 注解。 现在在 XML 文件中配置包扫描 context:component-scan base-packageorg.javaboy.demo.p6/最后加载 XML 配置文件初始化容器 public class Demo {public static void main(String[] args) {ClassPathXmlApplicationContext ctx new ClassPathXmlApplicationContext(beans_demo.xml);JavaConfig01 config01 ctx.getBean(JavaConfig01.class);JavaConfig02 config02 ctx.getBean(JavaConfig02.class);System.out.println(config01.getClass() config01.getClass());System.out.println(config02.getClass() config02.getClass());} }最终打印出来结果如下 从上面这段代码中我们可以得出来两个结论 Configuration 注解也是 Spring 组件注解的一种通过普通的 Bean 扫描也可以扫描到 Configuration。Configuration 注解注册到 Spring 中的 Bean 是一个 CGLIB 代理的 Bean而不是原始 Bean这一点和 Component 不一样Component 注册到 Spring 容器中的还是原始 Bean。 一个问题来了Configuration 标记的类为什么注册到 Spring 容器之后就变成了代理对象了呢闭着眼睛大家也能猜到肯定是为了通过代理来增强其功能那么究竟增强什么功能呢接下来我们通过源码分析来和小伙伴们梳理一下这里的条条框框。 2. 源码分析 要理解这个问题首先得结合我们前面的文章Configuration 注解的 Full 模式和 Lite 模式在该文中松哥提到了 Configuration 模式分为了 Full 模式和 Lite 模式所以对于 Configuration 注解的处理在加载的时候就需要首先区分出来是 Full 模式还是 Lite 模式。 负责 Configuration 注解的是 ConfigurationClassPostProcessor这个处理器是一个 BeanFactoryPostProcessorBeanFactoryPostProcessor 的作用就是在 Bean 定义的时候通过修改 BeanDefinition 来重新定义 Bean 的行为这个松哥之前有过专门的文章介绍不熟悉的小伙伴可以先看看这里 Spring 中 BeanFactory 和 FactoryBean 有何区别 同时ConfigurationClassPostProcessor 也是 BeanDefinitionRegistryPostProcessor 的实例BeanDefinitionRegistryPostProcessor 是干嘛的呢 BeanDefinitionRegistryPostProcessor 是 Spring 框架中的一个接口它的作用是在应用程序上下文启动时对 BeanDefinitionRegistry 进行后置处理。具体来说BeanDefinitionRegistryPostProcessor 可以用于修改或扩展应用程序上下文中的 BeanDefinition即在 Bean 实例化之前对 BeanDefinition 进行修改。它可以添加、删除或修改 BeanDefinition 的属性甚至可以动态地注册新的 BeanDefinition。通过实现 BeanDefinitionRegistryPostProcessor 接口我们可以在 Spring 容器启动过程中干预 Bean 的定义以满足特定的需求。这使得我们可以在应用程序上下文加载之前对 Bean 进行一些自定义的操作例如动态注册 Bean 或者修改 Bean 的属性。需要注意的是BeanDefinitionRegistryPostProcessor 在 BeanFactoryPostProcessor 之前被调用因此它可以影响到 BeanFactoryPostProcessor 的行为。 BeanFactoryPostProcessor 中的方法是 postProcessBeanFactory而 BeanDefinitionRegistryPostProcessor 中的方法是 postProcessBeanDefinitionRegistry根据前面的介绍postProcessBeanDefinitionRegistry 方法将在 postProcessBeanFactory 方法之前执行。 所以我们就从 postProcessBeanDefinitionRegistry 方法开始看起吧 2.1 postProcessBeanDefinitionRegistry Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException(postProcessBeanDefinitionRegistry already called on this post-processor against registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException(postProcessBeanFactory already called on this post-processor against registry);}this.registriesPostProcessed.add(registryId);processConfigBeanDefinitions(registry); }这个方面前面的代码主要是为了确保该方法执行一次我们就不多说了。关键在于最后的 processConfigBeanDefinitions 方法这个方法就是用来决策配置类是 Full 模式还是 Lite 模式的。 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) {if (logger.isDebugEnabled()) {logger.debug(Bean definition has already been processed as a configuration class: beanDef);}}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}//省略。。。 }我省略了其他代码大家看这个方法中会首先根据 beanName 取出来 BeanDefinition然后判断 BeanDefinition 中是否包含 ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE 属性这个属性上记录了当前配置类是 Full 模式还是 Lite 模式不同模式将来的处理方案肯定也是不同的。如果是第一次处理显然 BeanDefinition 中并不包含该属性因此就会进入到 ConfigurationClassUtils.checkConfigurationClassCandidate 方法中正是在该方法中判断当前配置类是 Full 模式还是 Lite 模式并进行标记checkConfigurationClassCandidate 方法的逻辑也挺长的我这里挑出来跟我们感兴趣的部分 static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {//省略。。。MapString, Object config metadata.getAnnotationAttributes(Configuration.class.getName());if (config ! null !Boolean.FALSE.equals(config.get(proxyBeanMethods))) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}else if (config ! null || isConfigurationCandidate(metadata)) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);}else {return false;}//省略 }Full 模式情况很简单就是如果配置类上存在 Configuration 注解并且该注解的 proxyBeanMethods 属性值不为 false那么就是 Full 模式这个跟松哥在 Configuration 注解的 Full 模式和 Lite 模式一文中的介绍是一致的。 Lite 模式就情况多一些首先 config!null 就是说现在也存在 Configuration 注解但是 proxyBeanMethods 属性值此时为 false那么就是 Lite 模式proxyBeanMethods 属性值为 true 的话就进入到 if 分支中了。 另外就是在 isConfigurationCandidate 方法中有一些判断逻辑去锁定是否为 Lite 模式 static boolean isConfigurationCandidate(AnnotationMetadata metadata) {// Do not consider an interface or an annotation...if (metadata.isInterface()) {return false;}// Any of the typical annotations found?for (String indicator : candidateIndicators) {if (metadata.isAnnotated(indicator)) {return true;}}// Finally, lets look for Bean methods...return hasBeanMethods(metadata); }这个方法的判断逻辑是这样 首先注解要是标记的是接口那就不能算是 Lite 模式。遍历 candidateIndicators判断当前类上是否包含这个 Set 集合中的注解这个 Set 集合中的注解有四个分别是 Component、ComponentScan、Import、ImportResource 四个也就是如果类上标记的是这四个注解的话那么也按照 Lite 模式处理。判断当前类中是否有 Bean 标记的方法如果有则按照 Lite 模式处理否则就不是 Lite 模式。 如果小伙伴们看过松哥之前的 Configuration 注解的 Full 模式和 Lite 模式一文那么上面这些代码应该都很好理解跟松哥在该文章中的介绍都是一致的。 好了经过上面的处理现在就已经标 BeanDefinition 中标记了这个配置类到底是 Full 模式还是 Lite 模式了。 2.2 postProcessBeanFactory 接下来我们就来看 postProcessBeanFactory 方法。 /*** Prepare the Configuration classes for servicing bean requests at runtime* by replacing them with CGLIB-enhanced subclasses.*/ Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException(postProcessBeanFactory already called on this post-processor against beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory)); }首先大家看一下这个方法的注释注释说的很明确了将 Configuration 类通过 CGLIB 进行增强以便在运行时较好的处理 Bean 请求。 这个方法中还会再次确认一下 postProcessBeanDefinitionRegistry 方法已经处理过了如果没有处理的话则会在该方法中调用 processConfigBeanDefinitions 去确认 Bean 使用的是哪种模式。 该方法的关键在于 enhanceConfigurationClasses这个就是用来通过动态代理增强配置类的当然这个方法也是比较长的我这里列出来一些关键的逻辑 public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {StartupStep enhanceConfigClasses this.applicationStartup.start(spring.context.config-classes.enhance);MapString, AbstractBeanDefinition configBeanDefs new LinkedHashMap();for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition beanDef beanFactory.getBeanDefinition(beanName);Object configClassAttr beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);if ((configClassAttr ! null || methodMetadata ! null) (beanDef instanceof AbstractBeanDefinition abd) !abd.hasBeanClass()) {// Configuration class (full or lite) or a configuration-derived Bean method// - eagerly resolve bean class at this point, unless its a lite configuration// or component class without Bean methods.boolean liteConfigurationCandidateWithoutBeanMethods (ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) annotationMetadata ! null !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));if (!liteConfigurationCandidateWithoutBeanMethods) {try {abd.resolveBeanClass(this.beanClassLoader);}}}if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {configBeanDefs.put(beanName, abd);}}ConfigurationClassEnhancer enhancer new ConfigurationClassEnhancer();for (Map.EntryString, AbstractBeanDefinition entry : configBeanDefs.entrySet()) {AbstractBeanDefinition beanDef entry.getValue();// If a Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);// Set enhanced subclass of the user-specified bean classClass? configClass beanDef.getBeanClass();Class? enhancedClass enhancer.enhance(configClass, this.beanClassLoader);if (configClass ! enhancedClass) {beanDef.setBeanClass(enhancedClass);}}enhanceConfigClasses.tag(classCount, () - String.valueOf(configBeanDefs.keySet().size())).end(); }这个方法的逻辑我整体上将之分为两部分 第一部分就是先找到 Full 模式的配置类的名称存入到 configBeanDefs 集合中。 具体寻找的逻辑就是根据配置类的模式去寻找如果配置类是 Full 模式就将之存入到 configBeanDefs 中。如果配置类是 Lite 模式且里边没有 Bean 标记的方法那就说明这可能并不是一个配置类就是一个普通 Bean那么就在这里加载类就行了。 第二步则是遍历 configBeanDefs 集合增强配置类。 这个如果大家了解 CGLIB 动态代理的话这个就很好懂了关于 CGLIB 动态代理松哥这里不啰嗦最近更新的 Spring 源码视频中都有详细讲到。那么这里主要是通过 enhancer.enhance 方法来生成代理类的如下 public Class? enhance(Class? configClass, Nullable ClassLoader classLoader) {if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {return configClass;}Class? enhancedClass createClass(newEnhancer(configClass, classLoader));return enhancedClass; } private Enhancer newEnhancer(Class? configSuperClass, Nullable ClassLoader classLoader) {Enhancer enhancer new Enhancer();enhancer.setSuperclass(configSuperClass);enhancer.setInterfaces(new Class?[] {EnhancedConfiguration.class});enhancer.setUseFactory(false);enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);enhancer.setAttemptLoad(true);enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));enhancer.setCallbackFilter(CALLBACK_FILTER);enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());return enhancer; }小伙伴们看到增强类中的 setCallbackFilter 是 CALLBACK_FILTER这个里边包含了几个方法拦截器跟我们相关的是 BeanMethodInterceptor我们来看下 private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {OverrideNullablepublic Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,MethodProxy cglibMethodProxy) throws Throwable {ConfigurableBeanFactory beanFactory getBeanFactory(enhancedConfigInstance);String beanName BeanAnnotationHelper.determineBeanNameFor(beanMethod);// Determine whether this bean is a scoped-proxyif (BeanAnnotationHelper.isScopedProxy(beanMethod)) {String scopedBeanName ScopedProxyCreator.getTargetBeanName(beanName);if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {beanName scopedBeanName;}}// To handle the case of an inter-bean method reference, we must explicitly check the// container for already cached instances.// First, check to see if the requested bean is a FactoryBean. If so, create a subclass// proxy that intercepts calls to getObject() and returns any cached bean instance.// This ensures that the semantics of calling a FactoryBean from within Bean methods// is the same as that of referring to a FactoryBean within XML. See SPR-6602.if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX beanName) factoryContainsBean(beanFactory, beanName)) {Object factoryBean beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX beanName);if (factoryBean instanceof ScopedProxyFactoryBean) {// Scoped proxy factory beans are a special case and should not be further proxied}else {// It is a candidate FactoryBean - go ahead with enhancementreturn enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);}}if (isCurrentlyInvokedFactoryMethod(beanMethod)) {return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);}return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);} }自己写过 CGLIB 动态代理的小伙伴都知道这里 intercept 方法的含义这就是真正的拦截方法了也就是说如果我们的配置类是 Full 模式的话那么将来调用 Bean 注解标记的方法的时候调用的其实是这里的 intercept 方法。 上面方法首先会判断当前代理是否为作用域代理我们这里当然不是。 接下来判断请求的 Bean 是否是一个 FactoryBean如果是则需要去代理其 getObject 方法当执行到 getObject 方法的时候就去 Spring 容器中查找需要的 Bean当然我们这里也不属于这种情况。 接下来判断当前正在执行的方法是否为容器中正在调用的工厂方法。 例如我有如下代码 Configuration public class JavaConfig {BeanUser user() {User user new User();user.setDog(dog());return user;}BeanDog dog() {return new Dog();} }那么如果是直接调用 dog() 方法则 isCurrentlyInvokedFactoryMethod 返回 true如果是在 user() 方法中调用的 dog() 方法则 isCurrentlyInvokedFactoryMethod 返回 false。 当 isCurrentlyInvokedFactoryMethod 返回 true 的时候就执行 invokeSuper 方法也就是真正的触发 dog() 方法的执行。 当 isCurrentlyInvokedFactoryMethod 返回 false 的时候则执行下面的 resolveBeanReference 方法这个方法会先去 Spring 容器中查找相应的 Bean如果 Spring 容器中不存在该 Bean则会触发 Bean 的创建流程。 现在小伙伴们应该明白了为什么 Full 模式下调用 Bean 注解标记的方法并不会导致 Bean 的重复创建了吧 好啦本文结合上文 Configuration 注解的 Full 模式和 Lite 模式 一起食用效果更佳哦
http://www.zqtcl.cn/news/753549/

相关文章:

  • 福田做网站需要多少钱做淘宝客网站性质
  • html网站怎么进入后台网站主题怎么写
  • wordpress怎么ftp建站高端网站建设域名注册
  • 我用织梦5.7做个网站应该把淘宝客店铺链接放到哪聊天软件开发需要多少钱
  • 站长工具爱站竞价单页网站制作
  • 网站分类目录大全购物网站大全棉鞋
  • 网站镜像做排名建立外贸英文网站应该怎么做
  • 上海做网站就用乐云seo手机网站cms 下载
  • 做网站需要固定ip么灵犀科技网站建设
  • 深圳高端做网站建设网站备案与不备案区别
  • 家居企业网站建设公司苏州高新区建设局网站管网
  • 体育门户网站模板seo网络推广有哪些
  • 石家庄网站建设教程百度云下载
  • 怎样查看网站建设时间公司网站关键词优化
  • 网站淘宝推广怎么做网站seo基本流程
  • miit网站备案济南哪里做网站
  • 做网站软件的公司前端优化
  • 哪个网站有做形象墙汉沽网站建设制作
  • 网站alexa排名查询免费发帖的平台有哪些
  • 德国网站后缀濮阳房产网站建设
  • 漕泾网站建设做网站php语言用什么工具
  • 专业营销的网站建设公司哪家好专门做二手书的网站
  • 建新网站开发流程图电子商务网站开发综合实训报告
  • 临汾网站建设销售长沙网站建设1681989
  • 最近国内外重大新闻事件贺贵江seo教程
  • 网站开发源码网站运营建设方案
  • 网站建设公司 跨界鱼科技专业简述网站的制作步骤
  • 手机网站cms网站优化推广哪家好
  • 网站被k换域名qq空间 wordpress
  • 网站模板种类昆明cms建站模板