浅谈BeanPostProcessor加载次序及其对Bean造成的影响分析

前言

BeanPostProcessor是一个工厂钩子,允许Spring框架在新创建Bean实例时对其进行定制化修改。例如:通过检查其标注的接口或者使用代理对其进行包裹。应用上下文会从Bean定义中自动检测出BeanPostProcessor并将它们应用到随后创建的任何Bean上。

普通Bean对象的工厂允许在程序中注册post-processors,应用到随后在本工厂中创建的所有Bean上。典型的场景如:post-processors使用postProcessBeforeInitialization方法通过特征接口或其他类似的方式来填充Bean;而为创建好的Bean创建代理则一般使用postProcessAfterInitialization方法。

BeanPostProcessor本身也是一个Bean,一般而言其实例化时机要早过普通的Bean,但是BeanPostProcessor也会依赖一些Bean,这就导致了一些Bean的实例化早于BeanPostProcessor,由此会导致一些问题。最近在处理shiro和spring cache整合时就碰到了,导致的结果就是spring cache不起作用。现将问题场景、查找历程及解决方法展现一下。

1 问题场景

打算在项目中将shiro与spring cache整合,使用spring cache统一管理缓存,也包括shiro认证时的用户信息查询。项目中将service分层,outter层负责权限和session,inner层主打事务和缓存并与DAO交互,两层之间也可以较容易的扩展为RPC或微服务模式。因此在shiro的authRealm中依赖了innerUserService,并在innerUserService中配置了spring cache的标注,使用cache进行缓存。配置如下(摘录重要部分):

  @Bean(name="shiroFilter")
  public ShiroFilterFactoryBean shiroFilter(
   @Qualifier("securityManager") SecurityManager manager
   ) {
    ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
    bean.setSecurityManager(manager);
    ..............
    return bean;
  }
  //配置核心安全事务管理器
  @Bean(name="securityManager")
  public SecurityManager securityManager(@Qualifier("authRealm") AuthorizingRealm authRealm,
   @Qualifier("sessionManager") SessionManager sessionManager,
   @Qualifier("cookieRememberMeManager") RememberMeManager rememberMeManager,
   @Qualifier("cacheManager") CacheManager cacheManager) {
    System.err.println("--------------shiro已经加载----------------");
    DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
    manager.setRealm(authRealm);
    manager.setSessionManager(sessionManager);
    manager.setRememberMeManager(rememberMeManager);
    manager.setCacheManager(cacheManager);
    return manager;
  }
  //配置自定义权限登录器
  @Bean(name="authRealm")
  public AuthorizingRealm authRealm(IInnerUserService userService) {
   MyRealm myrealm = new MyRealm(IInnerUserService);
   logger.info("authRealm myRealm initiated!");
    return myrealm;
  }
  @Bean
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
   return new LifecycleBeanPostProcessor(Ordered.LOWEST_PRECEDENCE);
  }

其中MyRealm是自定义的shiro AuthorizingRealm,用于执行认证与授权,其实现依赖innerUserService从库中查找用户信息,示例代码如下:

public class MyRealm extends AuthorizingRealm {
 IInnerUserService userService;
 public MyRealm(){
 super();
 }
 public MyRealm(IInnerUserService userService){
 this.userService = userService;
 }
 public IInnerUserService getUserService() {
 return userService;
 }
 public void setUserService(IInnerUserService userService) {
 this.userService = userService;
 }
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(
  PrincipalCollection principals) {
 //null usernames are invalid
    if (principals == null) {
      throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
    }
    Set<String> roleNames = new HashSet<String>();
    Set<String> permissions = new HashSet<String>();
 User user = (User)getAvailablePrincipal(principals);
 roleNames.add("role1");
 roleNames.add("role2");
 permissions.add("user:create");
 permissions.add("user:update");
 permissions.add("user:delete");
 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
    info.setStringPermissions(permissions);
    return info;
 }

 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(
  AuthenticationToken token) throws AuthenticationException {
 String username = (String)token.getPrincipal(); //得到用户名
    String password = new String((char[])token.getCredentials()); //得到密码
    User user = userService.findByUsernameInner(username);
    if(user==null){
     throw new UnknownAccountException();
    }else if(!password.equals(user.getPassword()))
 {
     throw new IncorrectCredentialsException();
 }
    else{
     return new SimpleAuthenticationInfo(user, password, getName());
    }
 }
}

而在innerUserService中配置了spring cache的标注,示例代码如下:

@Service
public class IInnerUserServiceImpl implements IInnerUserService {
 Logger logger = LoggerFactory.getLogger(IInnerUserServiceImpl.class);

 @Autowired
 IUserDao userDao;

 @Override
 @Cacheable(value = "mycache", key = "#username")
 public User findByUsernameInner(String username) {
 User user = userDao.findByUsername(username);
 logger.info("Real execute find from database, username:{}", username);
 return user;
 }
}

并在配置文件上标注了@EnableCaching(mode=AdviceMode.PROXY)以启动spring cache。这里不过多解释具体shiro和spring cache的使用,有兴趣的同学请自行搜索相关资料。

按理说这样的配置在认证的时候应该可以直接使用到innerUserService中配置的spring cache缓存。

但,问题出现了,当authRealm中依赖了innerUserService以后,定义在innerUserService上的spring cache就神奇的失效了。而authRealm不依赖innerUserService的时候,cache却运行的好好的。

接下来是问题查找的路径。

2 解决问题之旅

2.1 spring cache失效的表象原因

首先要找到spring cache失效的表象/直接原因,我们知道spring cache使用Spring AOP和拦截器的方式拦截定义了特定标注的方法,然后执行特定逻辑。因此其实现依赖于动态代理机制auto-proxy,而经过初步调试发现,当被authRealm依赖以后,innerUserService就不会被代理了,因此无从进入AOP的pointcut,也就是说AOP切面失效了!

2.2 从spring cache的集成机制分析深层次原因

为何没有被代理呢,我们先来确认一下正常情况下什么时候进行代理封装,这时关于BeanPostProcessor的定义浮现脑海,据文档记载BeanPostProcessor允许在Bean实例化的前后对其做一些猥琐的事情,比如代理。我们在BeanPostProcessor的实现类中发现了InstantiationAwareBeanPostProcessor、SmartInstantiationAwareBeanPostProcessor、AbstractAutoProxyCreator、InfrastructureAdvisorAutoProxyCreator这一脉。而反观@enableCache标注在启动的时候会@import CachingConfigurationSelector,其selectImports方法会返回AutoProxyRegistrar和ProxyCachingConfiguration的全类名(我们定义了mode=AdviceMode.PROXY),也就是加载这两个类。第一个的作用就是注册InfrastructureAdvisorAutoProxyCreator到BeanDefinitionRegistry中。第二个的作用就是注册了BeanFactoryCacheOperationSourceAdvisor和CacheInterceptor。

因此,当正常情况下,一个添加了spring cache相关标注的bean会在创建后被InfrastructureAdvisorAutoProxyCreator基于advisor进行代理增强,代理后便可在拦截器CacheInterceptor中对其方法进行拦截,然后执行cache相关逻辑。此处省略具体处理逻辑,有兴趣请参考相关文档。

所以第一怀疑就是innerUserService没有经过InfrastructureAdvisorAutoProxyCreator的代理增强。果然调试发现,被authRealm依赖的情况下在InnerUserService的Bean实例化时,用于处理该Bean的PostBeanProcessor明显比没被authRealm依赖时少,并且不含有InfrastructureAdvisorAutoProxyCreator。

而且,被依赖时会多打出来一行信息:

...................
Bean 'IInnerUserServiceImpl' of type [shiro.web.inner.service.impl.IInnerUserServiceImpl] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
...................

据此推断,可能是innerUserService启动时机过早,导致的后面那些BeanPostProcessor们来没来得及实例化及注册呢。

2.3 BeanPostProcessor启动阶段对其依赖的Bean造成的影响

首先确认了authRealm也是受害者,因为shiroFilter->SecurityManager->authRealm的依赖关系导致其不得不提前实例化。表面上的罪魁祸首是shiroFilter,但是到底是谁导致的shiroFilter预料之外的提前启动呢。shiroFilter与InfrastructureAdvisorAutoProxyCreator的具体启动时机到底是什么时候呢。

又经过一番混天暗地的调试,终于了解了BeanPostProcessor的启动时机。在AbstractBeanFactory中维护了BeanPostProcessor的列表:

private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();

并实现了ConfigurableBeanFactory定义的方法:

void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

因此我们首先监控AbstractBeanFactory.addBeanPostProcessor(),看看启动过程中谁调用了该方法来注册BeanPostProcessor。发现实例化及注册PostBeanFactory的阶段分为四个:

第一阶段是在启动时调用过程会调用AbstractApplicationContext.refresh(),其中的prepareBeanFactory方法中注册了

ApplicationContextAwareProcessor、ApplicationListenerDetector:
........
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
........
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
........

然后在postProcessBeanFactory方法中注册了WebApplicationContextServletContextAwareProcessor:

beanFactory.addBeanPostProcessor(
  new WebApplicationContextServletContextAwareProcessor(this));

然后在invokeBeanFactoryPostProcessors方法中调用

代码如下:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

其中对已经注册的BeanFactoryPostProcessors挨个调用其postProcessBeanFactory方法,其中有一个ConfigurationClassPostProcessor,其postProcessBeanFactory方法中注册了一个ImportAwareBeanPostProcessor:

beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));

最后在registerBeanPostProcessors方法中调用

PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);

在该方法中,首先注册BeanPostProcessorChecker:

代码如下:

beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

该BeanPostProcessorChecker就是输出上面那行信息的真凶,它会在Bean创建完后检查可在当前Bean上起作用的BeanPostProcessor个数与总的BeanPostProcessor个数,如果起作用的个数少于总数,则报出上面那句信息。

然后分成三个阶段依次实例化并注册实现了PriorityOrdered的BeanPostProcessor、实现了Ordered的BeanPostProcessor、没实现Ordered的BeanPostProcessor,代码如下:

 // Separate between BeanPostProcessors that implement PriorityOrdered,
 // Ordered, and the rest.
 List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
 List<BeanPostProcessor> internalPostProcessors = new ArrayList<BeanPostProcessor>();
 List<String> orderedPostProcessorNames = new ArrayList<String>();
 List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
 for (String ppName : postProcessorNames) {
  if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  priorityOrderedPostProcessors.add(pp);
  if (pp instanceof MergedBeanDefinitionPostProcessor) {
   internalPostProcessors.add(pp);
  }
  }
  else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
  orderedPostProcessorNames.add(ppName);
  }
  else {
  nonOrderedPostProcessorNames.add(ppName);
  }
 }

 // First, register the BeanPostProcessors that implement PriorityOrdered.
 sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
 registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

 // Next, register the BeanPostProcessors that implement Ordered.
 List<BeanPostProcessor> orderedPostProcessors = new ArrayList<BeanPostProcessor>();
 for (String ppName : orderedPostProcessorNames) {
  BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  orderedPostProcessors.add(pp);
  if (pp instanceof MergedBeanDefinitionPostProcessor) {
  internalPostProcessors.add(pp);
  }
 }
 sortPostProcessors(orderedPostProcessors, beanFactory);
 registerBeanPostProcessors(beanFactory, orderedPostProcessors);

 // Now, register all regular BeanPostProcessors.
 List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanPostProcessor>();
 for (String ppName : nonOrderedPostProcessorNames) {
  BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  nonOrderedPostProcessors.add(pp);
  if (pp instanceof MergedBeanDefinitionPostProcessor) {
  internalPostProcessors.add(pp);
  }
 }
 registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

 // Finally, re-register all internal BeanPostProcessors.
 sortPostProcessors(internalPostProcessors, beanFactory);
 registerBeanPostProcessors(beanFactory, internalPostProcessors);

 // Re-register post-processor for detecting inner beans as ApplicationListeners,
 // moving it to the end of the processor chain (for picking up proxies etc).
 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));

需要注意的是,除了第一个阶段,其他阶段同一个阶段的BeanPostProcessor是在全部实例化完成以后才会统一注册到beanFactory的,因此,同一个阶段的BeanPostProcessor及其依赖的Bean在实例化的时候是无法享受到相同阶段但是先实例化的BeanPostProcessor的“服务”的,因为它们还没有注册。

从上面调试与源代码分析,BeanPostProcessor的实例化与注册分为四个阶段,第一阶段applicationContext内置阶段、第二阶段priorityOrdered阶段、第三阶段Ordered阶段、第四阶段nonOrdered阶段。而BeanPostProcessor同时也是Bean,其注册之前一定先实例化。而且是分批实例化和注册,也就是属于同一批的BeanPostProcesser全部实例化完成后,再全部注册,不存在先实例化先注册的问题。而在实例化的时候其依赖的Bean同样要先实例化。

因此导致一个结果就是,被PriorityOrderedBeanPostProcessor所依赖的Bean其初始化时无法享受到PriorityOrdered、Ordered、和nonOrdered的BeanPostProcessor的服务。而被OrderedBeanPostProcessor所依赖的Bean无法享受Ordered、和nonOrdered的BeanPostProcessor的服务。最后被nonOrderedBeanPostProcessor所依赖的Bean无法享受到nonOrderedBeanPostProcessor的服务。

由于InfrastructureAdvisorAutoProxyCreator的启动阶段是Ordered,因此我们需要确保没有任何priorityOrdered和Ordered的BeanPostProcessor直接或间接的依赖到shiroFilter,也就是依赖到我们的innerUserService。

同时,在PriorityOrdered接口的注解中也提到了该情况:

Note: {@code PriorityOrdered} post-processor beans are initialized in
  * a special phase, ahead of other post-processor beans. This subtly
  * affects their autowiring behavior: they will only be autowired against
  * beans which do not require eager initialization for type matching.

2.4 BeanPostProcessor在进行依赖的Bean注入时,根据Bean名称进行类型检查时导致的“误伤”

OK,问题貌似已查明,修改Configuration中所有PriorityOrdered和Ordered类型的PostBeanProcessor的Bean配置,使其不再依赖shiroFilter。再次启动,却发现仍然提前启动了shiroFilter->SecurityManager->authRealm->innerUserService。

百思不得其解,又是一轮昏天暗地的调试,查找shiroFilter具体的启动时机。发现在一个叫做dataSourceInitializerPostProcessor的BeanPostProcessor实例化的时候,在根据类型获得其依赖的参数时,对shiroFilter执行了初始化。导致后续SecurityManager->authRealm->innerUserService统统提前初始化。但是在dataSourceInitializerPostProcessor之前的BeanPostProcessor却没有。经调试它们是否会导致shiroFilter初始化的区别在调用AbstractBeanFactory.isTypeMatch方法时出现:

 public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException{
 .....................
 // Check bean class whether we're dealing with a FactoryBean.
 if (FactoryBean.class.isAssignableFrom(beanType)) { //(1)判断名称对应的Bean是否是一个FactoryBean,若是FactoryBean才执行本句
  if (!BeanFactoryUtils.isFactoryDereference(name)) {
  // If it's a FactoryBean, we want to look at what it creates, not the factory class.
  beanType = getTypeForFactoryBean(beanName, mbd);
  if (beanType == null) {
   return false;
  }
  }
 }
 .....................
 }

然后进入AbstractAutowireCapableBeanFactory.getTypeForFactoryBean方法:

 @Override
 protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
 String factoryBeanName = mbd.getFactoryBeanName();
 String factoryMethodName = mbd.getFactoryMethodName();

 if (factoryBeanName != null) {
  if (factoryMethodName != null) {
  // Try to obtain the FactoryBean's object type from its factory method declaration
  // without instantiating the containing bean at all.
  BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
  if (fbDef instanceof AbstractBeanDefinition) {
   AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
   if (afbDef.hasBeanClass()) {
   Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
   if (result != null) {
    return result;
   }
   }
  }
  }
  // If not resolvable above and the referenced factory bean doesn't exist yet,
  // exit here - we don't want to force the creation of another bean just to
  // obtain a FactoryBean's object type...
  if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {  //(2)判断该bean对应的factoryBeanName是否已经初始化了,如果没有,就返回。如果有,则继续
  return null;
  }
 }

 // Let's obtain a shortcut instance for an early getObjectType() call...
 FactoryBean<?> fb = (mbd.isSingleton() ?
  getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
  getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));

 ......................
 }

其中,有一个重要的判断:

    // If not resolvable above and the referenced factory bean doesn't exist yet,
 // exit here - we don't want to force the creation of another bean just to
 // obtain a FactoryBean's object type...
 if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
 return null;
 }

注解说的很明确,如果名字对应的factoryBean所在的factoryBean工厂尚未解析并实例化,那就直接退出,不会强制创建该facotryBean工厂,也就是Configuration对应的Bean。再次调试,果然发现,在先前的BeanPostProcessor和dataSourceInitializerPostProcessor之间,存在一个lifecycleBeanPostProcessor,而lifecycleBeanPostProcessor是在我们的Configuration中显示定义的,因此,当lifecycleBeanPostProcessor启动时会导致Configuration实例化。

dataSourceInitializerPostProcessor和在它之前的BeanPostProcessor对shiroFilter行为的不同在这里得到了完美的解释。本质上说dataSourceInitializerPostProcessor并不重要,重要的是lifecycleBeanPostProcessor将Configuration初始化了。就算不是dataSourceInitializerPostProcessor,那另一个BeanPostProcessor实例化时同样会将shiroFilter初始化。

最终隐藏大BOSS查明,解决方案就简单了,将lifecycleBeanPostProcessor移出到一个单独的Configuration就好了。

3. 总结

3.1 BeanPostProcessor启动顺序,以及其对于依赖的Bean的影响

BeanPostProcessor的启动时机。分为四个阶段,第一阶段context内置阶段、第二阶段priorityOrdered阶段、第三阶段Ordered阶段、第四阶段nonOrdered阶段。

而BeanPostProcessor同时也是Bean,其注册之前一定先实例化。而且是分批实例化和注册,也就是属于同一批的BeanPostProcesser全部实例化完成后,再全部注册,不存在先实例化先注册的问题。而在实例化的时候其依赖的Bean同样要先实例化。

因此导致一个结果就是,被PriorityOrderedBeanPostProcessor所依赖的Bean其初始化以后无法享受到PriorityOrdered、Ordered、和nonOrdered的BeanPostProcessor的服务。而被OrderedBeanPostProcessor所依赖的Bean无法享受Ordered、和nonOrdered的BeanPostProcessor的服务。最后被nonOrderedBeanPostProcessor所依赖的Bean无法享受到nonOrderedBeanPostProcessor的服务。

3.2 注意避免BeanPostProcessor启动时的“误伤”陷阱

BeanPostProcessor实例化时,自动依赖注入根据类型获得需要注入的Bean时,会将某些符合条件的Bean(FactoryBean并且其FactoryBeanFactory已经实例化的)先实例化,如果此FacotryBean又依赖其他普通Bean,会导致该Bean提前启动,造成误伤(无法享受部分BeanPostProcessor的后处理,例如典型的auto-proxy)。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 解析Java的Spring框架的BeanPostProcessor发布处理器

    BeanPostProcessor 的接口定义,可以实现提供自己的实例化逻辑,依赖解析逻辑等,也可以以后在Spring容器实例化完毕,配置和初始化一个bean通过插入一个或多个的BeanPostProcessor实现一些自定义逻辑回调方法实现. 可以配置多个的BeanPostProcessor接口,控制这些的BeanPostProcessor接口,通过设置属性顺序执行顺序提供的BeanPostProcessor实现了Ordered接口. BeanPostProcessor可以对bean(或对象)

  • Spring BeanPostProcessor接口使用详解

    Spring中提供了很多PostProcessor供开发者进行拓展,例如:BeanPostProcessor.BeanFactoryPostProcessor.BeanValidationPostProcessor等一系列后处理器.他们的使用方式大多类似,了解其中一个并掌握他的使用方式,其他的可以触类旁通. 这里以BeanPostProcessor为例展示其使用方式. BeanPostProcessor接口提供了两个供开发者自定义的方法:postProcessBeforeInitializati

  • 浅谈BeanPostProcessor加载次序及其对Bean造成的影响分析

    前言 BeanPostProcessor是一个工厂钩子,允许Spring框架在新创建Bean实例时对其进行定制化修改.例如:通过检查其标注的接口或者使用代理对其进行包裹.应用上下文会从Bean定义中自动检测出BeanPostProcessor并将它们应用到随后创建的任何Bean上. 普通Bean对象的工厂允许在程序中注册post-processors,应用到随后在本工厂中创建的所有Bean上.典型的场景如:post-processors使用postProcessBeforeInitializat

  • 浅谈Volley加载不出图片的问题

    问题分析:加载后台图片的时候,发现加载不出来,后来发现图片的url格式是: http://192.168.1.71/\carhome\shop\778c2bc3ec0a49e1969b24b3a8e62f31\detail\DSC04209.JPG 因为Volley请求,不识别url中有"\" 所以需要把"|"替换成"/" 以下是工具类 public class StringUtil { public static String formatUr

  • 浅谈Tensorflow加载Vgg预训练模型的几个注意事项

    写这个博客的关键Bug: Value passed to parameter 'input' has DataType uint8 not in list of allowed values: float16, bfloat16, float32, float64.本博客将围绕 加载图片 和 保存图片到本地 来详细解释和解决上述的Bug及其引出来的一系列Bug. 加载图片 首先,造成上述Bug的代码如下所示 image_path = "data/test.jpg" # 本地的测试图片

  • 浅谈vue加载优化策略

    vue.js是一个比较流行的前端框架,与react.js.angular.js相比来说,vue.js入手曲线更加流畅,不管掌握多少都可以快速上手.但是单页面应用也都有其弊病,有时候首屏加载慢的让人捏舌.今天我们以vue cli3.x来说一说如何行之有效的缓解此问题! 方法一 路由懒加载 首屏加载慢的原因无非就是单页面应用需要加载完整个路由表上的页面,而路由懒加载就是来解决这个问题的.如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了.下面这个

  • 浅谈vue-cli加载不到dev-server.js的解决办法

    在使用vue开发过程中,难免需要去本地数据地址进行请求,而原版配置在dev-server.js中,新版vue-webpack-template已经删除dev-server.js,改用webpack.dev.conf.js代替,所以 配置本地访问在webpack.dev.conf.js里配置即可. #webpack.dev.conf.js //首先 // nodejs开发框架express,用来简化操作 const express = require('express') // 创建node.js

  • Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案

    Android ListView异步加载图片错位.重复.闪烁分析以及解决方案,具体问题分析以及解决方案请看下文. 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而Ima

  • 浅谈vue 多个变量同时赋相同值互相影响

    首先,该项目用到了element-ui中的Tabs 标签:然后来龙去脉是酱紫的: 一个项目中需动态渲染一个列表billItemLIsts,列表中包含n组小列表,其中小列表的state_pj用于改变该小列表中的radio,于是: <el-tabs tab-position="left" style="max-height:280px;"> <el-tab-pane v-for="(itema,index) in billItemLIsts&

  • Android用于加载xml的LayoutInflater源码超详细分析

    1.在view的加载和绘制流程中:文章链接 我们知道,定义在layout.xml布局中的view是通过LayoutInflate加载并解析成Java中对应的View对象的.那么具体的解析过程是哪样的. 先看onCreate方法,如果我们的Activity是继承自AppCompactActivity.android是通过getDelegate返回的对象setContentView,这个mDelegate 是AppCompatDelegateImpl的实例. @Override protected

  • lazy init控制加载在Spring中如何实现源码分析

    目录 一.lazy-init说明 二.lazy-init 属性被设置的地方 三.lazy-init发挥作用的地方 四.问答 一.lazy-init说明 ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化(也就是依赖注入). 提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean. 通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小

  • Python实现动态加载模块、类、函数的方法分析

    本文实例讲述了Python实现动态加载模块.类.函数的方法.分享给大家供大家参考,具体如下: 动态加载模块: 方式1:系统函数__import__() 方式2:imp, importlib 模块 方式3:exec 函数 动态加载类和函数 首先,使用加载模块,使用内置函数提供的反射方法getattr(),依次按照层级获取模块->类\全局方法->类对象\类方法. test_import_module.py class ClassA: def test(self): print('test') in

随机推荐