Spring源码分析(三) IOC 之 createBean()和doCreateBean()
a、在createBean中又是主要做了什么事情?
完成bean得创建,填充属性、循环依赖 、aop等一系列过程
1、createBean()
在createBean中主要干了3件事情
1、解析class -> resolveBeanClass()
2、验证及准备覆盖的方法,lookup-method replace-method -> prepareMethodOverrides()
3、实例化之前得解析工作也就是 给BPP一个机会来返回代理对象替换普通对象 AOP中有一些对象 就是再此处生成resolveBeforeInstantiation()
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean '" + beanName + "'");}RootBeanDefinition mbdToUse = mbd;// Make sure bean class is actually resolved at this point, and// clone the bean definition in case of a dynamically resolved Class// which cannot be stored in the shared merged bean definition.// 锁定class,根据设置的class属性或者根据className来解析classClass<?> resolvedClass = resolveBeanClass(mbd, beanName);// 进行条件筛选,重新赋值RootBeanDefinition,并设置BeanClass属性if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {// 重新创建一个RootBeanDefinition对象mbdToUse = new RootBeanDefinition(mbd);// 设置BeanClass属性值mbdToUse.setBeanClass(resolvedClass);}// Prepare method overrides.// 验证及准备覆盖的方法,lookup-method replace-method,当需要创建的bean对象中包含了lookup-method和replace-method标签的时候,会产生覆盖操作try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.// 给BeanPostProcessors一个机会来返回代理来替代真正的实例,应用实例化前的前置处理器,用户自定义动态代理的方式,针对于当前的被代理类需要经过标准的代理流程来创建对象Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}try {// 实际创建bean的调用Object beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isTraceEnabled()) {logger.trace("Finished creating instance of bean '" + beanName + "'");}return beanInstance;}catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {// A previously detected exception with proper bean creation context already,// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.throw ex;}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);}}
1.1 resolveBeanClass()
锁定class,根据设置的class属性或者根据className 来解析 Class
@Nullable
protected Class<?> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class<?>... typesToMatch) {if (mbd.hasBeanClass()) {return mbd.getBeanClass();}if (System.getSecurityManager() != null) {return AccessController.doPrivileged((PrivilegedExceptionAction<Class<?>>)() -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());} else {return doResolveBeanClass(mbd, typesToMatch);}
}@Nullable
private Class<?> doResolveBeanClass(RootBeanDefinition mbd, Class<?>... typesToMatch) {ClassLoader beanClassLoader = getBeanClassLoader();ClassLoader classLoaderToUse = beanClassLoader;if (!ObjectUtils.isEmpty(typesToMatch)) {// When just doing type checks (i.e. not creating an actual instance yet),// use the specified temporary class loader (e.g. in a weaving scenario).ClassLoader tempClassLoader = getTempClassLoader();if (tempClassLoader != null) {classLoaderToUse = tempClassLoader;if (tempClassLoader instanceof DecoratingClassLoader) {DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;for (Class<?> typeToMatch : typesToMatch) {dcl.excludeClass(typeToMatch.getName());}}}}String className = mbd.getBeanClassName();if (className != null) {Object evaluated = evaluateBeanDefinitionString(className, mbd);if (!className.equals(evaluated)) {// A dynamically resolved expression, supported as of 4.2...if (evaluated instanceof Class) {return (Class<?>) evaluated;} else if (evaluated instanceof String) {return ClassUtils.forName((String) evaluated, classLoaderToUse);}}// When resolving against a temporary class loader, exit early in order// to avoid storing the resolved Class in the bean definition.if (classLoaderToUse != beanClassLoader) {return ClassUtils.forName(className, classLoaderToUse);}}return mbd.resolveBeanClass(beanClassLoader);
}
1.2 prepareMethodOverrides()
对Override属性标记及验证,在 Spring 配置中存在 lookup - method 和 replace - method 两个配置功能,而这两个配置的加载其实就是将配置统一存放在 BeanDefinition 中的 methodOverrides 属性里,这两个功能实现原理其实是在 bean 实例化的时候如果检测到存在 methodOverrides 属性,会动态地为当前 bean 生成代理并使用对应的拦截器为 bean 做增强处理。
对于方法的匹配来讲,如果一个类中存在若干个重载方法,那么,在函数调用及增强的时候还需要根据参数类型进行匹配,来最终确认当前调用的到底是哪个函数。如果当前类中的方法只有一个,那么就设置重载该方法没有被重载,这样在后续调用的时候便可以直接使用找到的方法,而不需要进行方法的参数匹配验证了,而且还可以提前对方法存在性进行验证。
/*** Validate and prepare the method overrides defined for this bean. 验证并准备为此 bean 定义的方法覆盖。* Checks for existence of a method with the specified name. 检查具有指定名称的方法是否存在。* @throws BeanDefinitionValidationException in case of validation failure*/public void prepareMethodOverrides(){// Check that lookup methods exist and determine their overloaded status.if (hasMethodOverrides()) {// MethodOverrides methodOverrides = new MethodOverrides();// final Set<MethodOverride> overrides = new CopyOnWriteArraySet<>();getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);}}/*** Validate and prepare the given method override.* Checks for existence of a method with the specified name,* marking it as not overloaded if none found.* @param mo the MethodOverride object to validate* @throws BeanDefinitionValidationException in case of validation failure*/protected void prepareMethodOverride(MethodOverride mo) {// 获取对应方法名的个数int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());if (count == 1) {// 标记 MethodOverrides 为未覆盖,避免参数类型检查的开销// Mark override as not overloaded, to avoid the overhead of arg type checking.mo.setOverloaded(false);}}
1.3 resolveBeforeInstantiation()
实例化的前置处理,对后处理器中的所有 InstantiationAwareBeanPostProcessor 类型的后处理器 调用 postProcessBeforelnstantiation() 和 BeanPostProcessor 的 postProcessAfterInitialization()。
/*** Apply before-instantiation post-processors, resolving whether there is a* before-instantiation shortcut for the specified bean.* @param beanName the name of the bean* @param mbd the bean definition for the bean* @return the shortcut-determined bean instance, or {@code null} if none*/@Nullableprotected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;// 如果尚未被解析if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// Make sure bean class is actually resolved at this point.if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}mbd.beforeInstantiationResolved = (bean != null);}return bean;}
1.3.1 applyBeanPostProcessorsBeforeInstantiation()
在 bean 的实例化前会调用后处理器的方法进行处理
bean 的实例化前调用,给子类一个修改 BeanDefinition 的机会,也就是说当程序经过这个方法后, bean 可能已经不是我们认为的 bean 了,而是或许成为了一个经过处理的代理 bean ,可能是通过 cglib 生成的,也可能是通过其他技术生成的。aop的相关有些类就是在这边提前处理了。
这里也可以自己扩展不过一般用不到
扩展点1:实现InstantiationAwareBeanPostProcessor 接口
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {/*** 实例化之前的操作*/@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {// 这里也可以返回普通对象System.out.println("当前beanName:" + beanName + "--> 执行:postProcessBeforeInstantiation 实例化之前");if (beanClass == MyConfig.class) {// 创建动态代理类的增强类Enhancer enhancer = new Enhancer();// 设置类加载器enhancer.setClassLoader(beanClass.getClassLoader());// 设置被动态代理类所代理的 被代理类enhancer.setSuperclass(beanClass);// 设置方法拦截器enhancer.setCallback(new QzkMethodInterceptor());// 创建代理类BeforeInstantiation beforeInstantiation = (BeforeInstantiation) enhancer.create();System.out.println("创建代理对象:" + beforeInstantiation);return beforeInstantiation;}return nulll;}/*** 实例化之后的操作*/@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {// MyConfig config = (MyConfig)bean; 这里也可以调整 属性的值或者其他操作//其实就是 相当于 经历spring的完整的对象创建过程然后调整属性的值的一个操作config.age=10;System.out.println("当前beanName:" + beanName + "--> 执行:postProcessAfterInstantiation 实例化之后");return false;}/*** 对当前属性值的一个相关处理工作*/@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {System.out.println("当前beanName:" + beanName + "--> 执行:postProcessProperties 属性值的处理");return pvs;}/*** 初始化之前的做哪些操作*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("当前beanName:" + beanName + "--> 执行:postProcessBeforeInitialization 初始化之前");return bean;}/*** 初始化之后做哪些操作*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("当前beanName:" + beanName + "--> 执行:postProcessAfterInitialization 初始化之后");return bean;}
}
1.3.2 applyBeanPostProcessorsAfterInitialization()
实例化后的后处理器应用,Spring 中的规则是在 bean 的初始化后尽可能保证将注册的后处理器的 postProcessAfterlnitialization() 应用到该 bean 中,因为如果返回的 bean 不为空,那么便不会再次经历普通 bean 的创建过程,所以只能在这里应用后处理器的 postProcessAfterInitialization()。
2、doCreateBean()
整体的流程如下,每个细节过程还有具体的图,整体的可能看不清
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)throws BeanCreationException {// Instantiate the bean.// 这个beanWrapper是用来持有创建出来的bean对象的BeanWrapper instanceWrapper = null;// 获取factoryBean实例缓存if (mbd.isSingleton()) {// 如果是单例对象,从factorybean实例缓存中移除当前bean定义信息instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}// 没有就创建实例if (instanceWrapper == null) {// 根据执行bean使用对应的策略创建新的实例,如,工厂方法,构造函数主动注入、简单初始化instanceWrapper = createBeanInstance(beanName, mbd, args);}// 从包装类中获取原始beanObject bean = instanceWrapper.getWrappedInstance();// 获取具体的bean对象的Class属性Class<?> beanType = instanceWrapper.getWrappedClass();// 如果不等于NullBean类型,那么修改目标类型if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;}// Allow post-processors to modify the merged bean definition.// 允许beanPostProcessor去修改合并的beanDefinitionsynchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {// MergedBeanDefinitionPostProcessor后置处理器修改合并bean的定义applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.// 判断当前bean是否需要提前曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// Initialize the bean instance.// 初始化bean实例Object exposedObject = bean;try {// 对bean的属性进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的beanpopulateBean(beanName, mbd, instanceWrapper);// 执行初始化逻辑exposedObject = initializeBean(beanName, exposedObject, mbd);}catch (Throwable ex) {if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {throw (BeanCreationException) ex;}else {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);}}if (earlySingletonExposure) {// 从缓存中获取具体的对象Object earlySingletonReference = getSingleton(beanName, false);// earlySingletonReference只有在检测到有循环依赖的情况下才会不为空if (earlySingletonReference != null) {// 如果exposedObject没有在初始化方法中被改变,也就是没有被增强if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);for (String dependentBean : dependentBeans) {// 返回false说明依赖还没实例化好if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}// 因为bean创建后所依赖的bean一定是已经创建的// actualDependentBeans不为空则表示当前bean创建后其依赖的bean却没有全部创建完,也就是说存在循环依赖if (!actualDependentBeans.isEmpty()) {throw new BeanCurrentlyInCreationException(beanName,"Bean with name '" + beanName + "' has been injected into other beans [" +StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +"] in its raw version as part of a circular reference, but has eventually been " +"wrapped. This means that said other beans do not use the final version of the " +"bean. This is often the result of over-eager type matching - consider using " +"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");}}}}// Register bean as disposable.try {// 注册bean对象,方便后续在容器销毁的时候销毁对象registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
2.1 createBeanInstance()
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// Make sure bean class is actually resolved at this point.// 确认需要创建的bean实例的类可以实例化Class<?> beanClass = resolveBeanClass(mbd, beanName);// 确保class不为空,并且访问权限是publicif (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}// 判断当前beanDefinition中是否包含实例供应器,此处相当于一个回调方法,利用回调方法来创建beanSupplier<?> instanceSupplier = mbd.getInstanceSupplier();if (instanceSupplier != null) {return obtainFromSupplier(instanceSupplier, beanName);}// 如果工厂方法不为空则使用工厂方法初始化策略if (mbd.getFactoryMethodName() != null) {return instantiateUsingFactoryMethod(beanName, mbd, args);}// 一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器// 在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中,避免再次创建相同bean时再次解析// Shortcut when re-creating the same bean...// 标记下,防止重复创建同一个beanboolean resolved = false;// 是否需要自动装配boolean autowireNecessary = false;// 如果没有参数if (args == null) {synchronized (mbd.constructorArgumentLock) {// 因为一个类可能由多个构造函数,所以需要根据配置文件中配置的参数或传入的参数来确定最终调用的构造函数。// 因为判断过程会比较,所以spring会将解析、确定好的构造函数缓存到BeanDefinition中的resolvedConstructorOrFactoryMethod字段中。// 在下次创建相同时直接从RootBeanDefinition中的属性resolvedConstructorOrFactoryMethod缓存的值获取,避免再次解析if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}// 有构造参数的或者工厂方法if (resolved) {// 构造器有参数if (autowireNecessary) {// 构造函数自动注入return autowireConstructor(beanName, mbd, null, null);}else {// 使用默认构造函数构造return instantiateBean(beanName, mbd);}}// Candidate constructors for autowiring?// 从bean后置处理器中为自动装配寻找构造方法, 有且仅有一个有参构造或者有且仅有@Autowired注解构造Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);// 以下情况符合其一即可进入// 1、存在可选构造方法// 2、自动装配模型为构造函数自动装配// 3、给BeanDefinition中设置了构造参数值// 4、有参与构造函数参数列表的参数if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);}// Preferred constructors for default construction?// 找出最合适的默认构造方法ctors = mbd.getPreferredConstructors();if (ctors != null) {// 构造函数自动注入return autowireConstructor(beanName, mbd, ctors, null);}// No special handling: simply use no-arg constructor.// 使用默认无参构造函数创建对象,如果没有无参构造且存在多个有参构造且没有@AutoWired注解构造,会报错return instantiateBean(beanName, mbd);}
总结: bean对象的创建方式
2.2 applyMergedBeanDefinitionPostProcessors()
应用MergedBeanDefinitionPostProcessors类型的beanPostProcessor到指定的beanDefinition中
执行postProcessMergedBeanDefinition方法,这里主要是为了合并bean的定义的。
其中 执行的类有主要以下3个
AutowiredAnnotataionBeanPostProcessor处理@Autowired,@Value注解
CommonAnnotationBeanPostProcessor处理@Resource
InitDestroyAnnotationBeanPostProcessor处理@PostConstruct,@PreDestroy
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof MergedBeanDefinitionPostProcessor) {MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);}}}/***spring通过此方法找出所有需要注入的字段,同时做缓存* 这个接口下有很多的实现类,最常见的就是* AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor* Post-process the given merged bean definition for the specified bean.* @param beanDefinition the merged bean definition for the bean* @param beanType the actual type of the managed bean instance* @param beanName the name of the bean* @see AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors*/void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
2.2.1 CommonAnnotationBeanPostProcessor
@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {// 处理@PostConstruct和@PreDestroy注解super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);//找出beanType所有被@Resource标记的字段和方法封装到InjectionMetadata中InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);}/*** 解析@Resource注解* @param beanName* @param clazz* @param pvs* @return*/private InjectionMetadata findResourceMetadata(String beanName, final Class<?> clazz, @Nullable PropertyValues pvs) {// Fall back to class name as cache key, for backwards compatibility with custom callers.// 获取对应的bean名称作为缓存keyString cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// Quick check on the concurrent map first, with minimal locking.// 从缓存中获取注入元数据对象InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}// 将返回的metadata对象放入injectionMetadataCache缓存中,缓存key为beanName,供后续方法从缓存中取出metadata = buildResourceMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;}private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {// 判断当前clazz是否是候选classif (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {return InjectionMetadata.EMPTY;}// 创建InjectedElement集合对象List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();// 查询是否有webService,ejb,Resource的属性注解,但是不支持静态属性ReflectionUtils.doWithLocalFields(targetClass, field -> {if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");}currElements.add(new WebServiceRefElement(field, field, null));}else if (ejbClass != null && field.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static fields");}currElements.add(new EjbRefElement(field, field, null));}else if (field.isAnnotationPresent(Resource.class)) {//注意静态字段不支持if (Modifier.isStatic(field.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static fields");}//如果不想注入某一类型对象 可以将其加入ignoredResourceTypes中if (!this.ignoredResourceTypes.contains(field.getType().getName())) {//字段会封装到ResourceElementcurrElements.add(new ResourceElement(field, field, null));}}});// 处理方法ReflectionUtils.doWithLocalMethods(targetClass, method -> {//找出我们在代码中定义的方法而非编译器为我们生成的方法Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}//如果重写了父类的方法,则使用子类的if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {// 静态字段不支持if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);}PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));}else if (ejbClass != null && bridgedMethod.isAnnotationPresent(ejbClass)) {if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@EJB annotation is not supported on static methods");}if (method.getParameterCount() != 1) {throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);}PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new EjbRefElement(method, bridgedMethod, pd));}else if (bridgedMethod.isAnnotationPresent(Resource.class)) {// 不支持静态方法if (Modifier.isStatic(method.getModifiers())) {throw new IllegalStateException("@Resource annotation is not supported on static methods");}Class<?>[] paramTypes = method.getParameterTypes();if (paramTypes.length != 1) {throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);}if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);currElements.add(new ResourceElement(method, bridgedMethod, pd));}}}});elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);}
2.2.2 InitDestroyAnnotationBeanPostProcessor
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {// 调用方法获取生命周期元数据并保存LifecycleMetadata metadata = findLifecycleMetadata(beanType);// 验证相关方法metadata.checkConfigMembers(beanDefinition);}private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {if (this.lifecycleMetadataCache == null) {// Happens after deserialization, during destruction...// 在bean销毁过程中,反序列化后调用return buildLifecycleMetadata(clazz);}// Quick check on the concurrent map first, with minimal locking.// 首先尝试从缓存中获取元数据LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);// 如果从缓存中获取失败则尝试加锁创建元数据if (metadata == null) {synchronized (this.lifecycleMetadataCache) {// 加锁后再次尝试获取元数据,防止多线程重复执行metadata = this.lifecycleMetadataCache.get(clazz);if (metadata == null) {// 构建生命周期元数据metadata = buildLifecycleMetadata(clazz);// 将构建好的元数据放入缓存中this.lifecycleMetadataCache.put(clazz, metadata);}return metadata;}}return metadata;}/*** 构造生命周期元数据(解析带@PostConstruct和@PreDestroy注解的方法),当其构造完成后会将元数据缓存到lifecycleMetadataCache集合中并返回* 此时就完成了相关方法(初始化方法和销毁方法)的扫描解析和缓存工作** @param clazz* @return*/private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {return this.emptyLifecycleMetadata;}// 实例化后的回调方法(@PostConstruct)List<LifecycleElement> initMethods = new ArrayList<>();// 销毁前的回调方法(@PreDestroy)List<LifecycleElement> destroyMethods = new ArrayList<>();// 获取正在处理的目标类Class<?> targetClass = clazz;do {// 保存每一轮循环搜索到的相关方法final List<LifecycleElement> currInitMethods = new ArrayList<>();final List<LifecycleElement> currDestroyMethods = new ArrayList<>();// 反射获取当前类中的所有方法并依次对其调用第二个参数的lambda表达式ReflectionUtils.doWithLocalMethods(targetClass, method -> {// 当前方法的注解中包含initAnnotationType注解时(@PostConstruct)if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {// 如果有,把它封装成LifecycleElement对象,存储起来LifecycleElement element = new LifecycleElement(method);// 将创建好的元素添加到集合中currInitMethods.add(element);if (logger.isTraceEnabled()) {logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);}}// 当前方法的注解中包含destroyAnnotationType注解(PreDestroy)if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {// 如果有,把它封装成LifecycleElement对象,存储起来currDestroyMethods.add(new LifecycleElement(method));if (logger.isTraceEnabled()) {logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);}}});// 将本次循环中获取到的对应方法集合保存到总集合中initMethods.addAll(0, currInitMethods);// 销毁方法父类晚于子类destroyMethods.addAll(currDestroyMethods);// 获取当前类的父类targetClass = targetClass.getSuperclass();}// 如果当前类存在父类且父类不为object基类则循环对父类进行处理while (targetClass != null && targetClass != Object.class);// 有一个不为空就封装一个LifecycleMetadata对象,否则就返回空的emptyLifecycleMetadatareturn (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :new LifecycleMetadata(clazz, initMethods, destroyMethods));}
2.2.3 AutowiredAnnotationBeanPostProcessor
/*** 处理合并的bean定义信息* 1、解析@Autowired等注解然后转换* 2、把注解信息转换为InjectionMetadata然后缓存到上面的injectionMetadataCache里面* @param beanDefinition the merged bean definition for the bean* @param beanType the actual type of the managed bean instance* @param beanName the name of the bean*/@Overridepublic void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {// 解析注解并缓存InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);}
/*** 方法名为查找到该bean的依赖注入元信息,内部只要查找到了就会加入到缓存内,下次没必要再重复查找了~* 它是一个模版方法,真正做事的方法是:buildAutowiringMetadata,它复杂把标注有@Autowired注解的属性转换为Metadata元数据信息,从而消除注解的定义* 此处查找包括了字段依赖注入和方法依赖注入~~~* @param beanName* @param clazz* @param pvs* @return*/private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// Fall back to class name as cache key, for backwards compatibility with custom callers.String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// Quick check on the concurrent map first, with minimal locking.// 从缓存中获取该类的信息InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);// 判断是否需要刷新缓存if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}// 构建自动装配的属性和方法元数据metadata = buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;}/*** 去寻找有Autowired和Value注解的属性和方法,也包括自定义的父类的,封装成AutowiredMethodElement放入集合中* @param clazz* @return*/private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {// 如果clazz是JDK中的类,直接忽略,因为不可能标注有这些标注if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {return InjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();// 遍历类中的每个属性,判断属性是否包含指定的属性(通过 findAutowiredAnnotation 方法)// 如果存在则保存,这里注意,属性保存的类型是 AutowiredFieldElementReflectionUtils.doWithLocalFields(targetClass, field -> {MergedAnnotation<?> ann = findAutowiredAnnotation(field);if (ann != null) {//Autowired注解不支持静态方法if (Modifier.isStatic(field.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static fields: " + field);}return;}//查看是否是required的boolean required = determineRequiredStatus(ann);currElements.add(new AutowiredFieldElement(field, required));}});// 遍历类中的每个方法,判断属性是否包含指定的属性(通过 findAutowiredAnnotation 方法)// 如果存在则保存,这里注意,方法保存的类型是 AutowiredMethodElementReflectionUtils.doWithLocalMethods(targetClass, method -> {Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {return;}MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {if (Modifier.isStatic(method.getModifiers())) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation is not supported on static methods: " + method);}return;}// 如果方法没有入参,输出日志,不做任何处理if (method.getParameterCount() == 0) {if (logger.isInfoEnabled()) {logger.info("Autowired annotation should only be used on methods with parameters: " +method);}}boolean required = determineRequiredStatus(ann);PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);// AutowiredMethodElement里封装了一个PropertyDescriptor(比字段多了一个参数)currElements.add(new AutowiredMethodElement(method, required, pd));}});// 父类的都放在第一位,所以父类是最先完成依赖注入的elements.addAll(0, currElements);targetClass = targetClass.getSuperclass();}while (targetClass != null && targetClass != Object.class);// InjectionMetadata就是对clazz和elements的一个包装而已return InjectionMetadata.forElements(elements, clazz);}
2.3 populateBean()
该方法的核心流程主要就是填充属性,
一部分代码是支持老的xml的注入的方式
一部分是支持新式的注解的方式例如像@Resource,@Autowired注解,这一部分的关键代码就在于postProcessProperties()中
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {// 如果beanWrapper为空if (bw == null) {// 如果mbd有需要设置的属性if (mbd.hasPropertyValues()) {// 抛出bean创建异常throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");}else {// Skip property population phase for null instance.// 没有可填充的属性,直接跳过return;}}// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the// state of the bean before properties are set. This can be used, for example,// to support styles of field injection.// 给任何实现了InstantiationAwareBeanPostProcessors的子类机会去修改bean的状态再设置属性之前,可以被用来支持类型的字段注入// 否是"synthetic"。一般是指只有AOP相关的pointCut配置或者Advice配置才会将 synthetic设置为true// 如果mdb是不是'syntheic'且工厂拥有InstantiationAwareBeanPostProcessor// 这里可以写接口可以让所有类都不能依赖注入if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {//遍历工厂中的BeanPostProcessor对象for (BeanPostProcessor bp : getBeanPostProcessors()) {//如果 bp 是 InstantiationAwareBeanPostProcessor 实例if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;// //postProcessAfterInstantiation:一般用于设置属性if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}}//PropertyValues:包含以一个或多个PropertyValue对象的容器,通常包括针对特定目标Bean的一次更新//如果mdb有PropertyValues就获取其PropertyValues//这个是程序员在 bd中 写入的属性rootBeanDefinition.getPropertyValues().add("type","男的");或者是XML中定义Bean的时候 加的PropertyValue标签PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);// 获取 mbd 的 自动装配模式// 这里正常时用来依赖注入 以前xml方式配置的那种// <bean id="person" class="com.Person" autowire="byName"></bean>int resolvedAutowireMode = mbd.getResolvedAutowireMode();// 如果 自动装配模式 为 按名称自动装配bean属性 或者 按类型自动装配bean属性if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {//MutablePropertyValues:PropertyValues接口的默认实现。允许对属性进行简单操作,并提供构造函数来支持从映射 进行深度复制和构造MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// Add property values based on autowire by name if applicable.// 根据autotowire的名称(如适用)添加属性值if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {//通过bw的PropertyDescriptor属性名,查找出对应的Bean对象,将其添加到newPvs中autowireByName(beanName, mbd, bw, newPvs);}// Add property values based on autowire by type if applicable.// 根据自动装配的类型(如果适用)添加属性值if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {//通过bw的PropertyDescriptor属性类型,查找出对应的Bean对象,将其添加到newPvs中autowireByType(beanName, mbd, bw, newPvs);}//让pvs重新引用newPvs,newPvs此时已经包含了pvs的属性值以及通过AUTOWIRE_BY_NAME,AUTOWIRE_BY_TYPE自动装配所得到的属性值pvs = newPvs;}//这里是对非autowiring的属性进行依赖注入处理//工厂是否拥有InstiationAwareBeanPostProcessorboolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();//mbd.getDependencyCheck(),默认返回 DEPENDENCY_CHECK_NONE,表示 不检查//是否需要依赖检查boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);//经过筛选的PropertyDesciptor数组,存放着排除忽略的依赖项或忽略项上的定义的属性PropertyDescriptor[] filteredPds = null;//如果工厂拥有InstiationAwareBeanPostProcessor,那么处理对应的流程,主要是对几个注解的赋值工作包含的两个关键子类是CommonAnnoationBeanPostProcessor,AutowiredAnnotationBeanPostProcessorif (hasInstAwareBpps) {//如果pvs为nullif (pvs == null) {//尝试获取mbd的PropertyValuespvs = mbd.getPropertyValues();}//遍历工厂内的所有后置处理器for (BeanPostProcessor bp : getBeanPostProcessors()) {//如果 bp 是 InstantiationAwareBeanPostProcessor 的实例if (bp instanceof InstantiationAwareBeanPostProcessor) {//将bp 强转成 InstantiationAwareBeanPostProcessor 对象InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;//postProcessProperties:在工厂将给定的属性值应用到给定Bean之前,对它们进行后处理,不需要任何属性扫描符。// 让ibp对pvs增加对bw的Bean对象的propertyValue,或编辑pvs的proertyValue//依赖注入过程,@Autowired、@Resource、@Value等注解的注入PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);//如果pvs为nullif (pvsToUse == null) {//如果filteredPds为nullif (filteredPds == null) {//mbd.allowCaching:是否允许缓存,默认时允许的。缓存除了可以提高效率以外,还可以保证在并发的情况下,返回的PropertyDesciptor[]永远都是同一份//从bw提取一组经过筛选的PropertyDesciptor,排除忽略的依赖项或忽略项上的定义的属性filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}// 老版本用这个完成依赖注入过程,@Autowired的支持pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);//如果pvsToUse为null,将终止该方法精致,以跳过属性填充if (pvsToUse == null) {return;}}//让pvs引用pvsToUsepvs = pvsToUse;}}}//如果需要依赖检查if (needsDepCheck) {//如果filteredPds为nullif (filteredPds == null) {//从bw提取一组经过筛选的PropertyDesciptor,排除忽略的依赖项或忽略项上的定义的属性filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}//检查依赖项:主要检查pd的setter方法需要赋值时,pvs中有没有满足其pd的需求的属性值可供其赋值checkDependencies(beanName, mbd, filteredPds, pvs);}//这个方法建议不看,是老版本用<property name="xxx" value="xxx"/>这种标签才会进来的。//标签做依赖注入的代码实现,里面超鸡儿复杂。现在都用注解了可以不看if (pvs != null) {//应用给定的属性值,解决任何在这个bean工厂运行时其他bean的引用。必须使用深拷贝,所以我们 不会永久地修改这个属性applyPropertyValues(beanName, mbd, bw, pvs);}}
2.3.1 postProcessProperties()
这个其实再上面的applyMergedBeanDefinitionPostProcessors()方法中已经讲过了 ,上面那个是会先提前读取 然后放入对应的缓存中 ,再这边是直接 读取缓存的。然后进行注入,采用反射的方式进行。这里只讲注入的代码段
AutowiredAnnotationBeanPostProcessor
@AutoWired
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;}private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// Fall back to class name as cache key, for backwards compatibility with custom callers.String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());// Quick check on the concurrent map first, with minimal locking.//此处缓存中有数据,直接就返回了InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {synchronized (this.injectionMetadataCache) {metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {if (metadata != null) {metadata.clear(pvs);}//这个重要方法上篇分享过了,这里就不说了metadata = buildAutowiringMetadata(clazz);this.injectionMetadataCache.put(cacheKey, metadata);}}}return metadata;public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {//循环每个带有@Atowared注解的参数或者方法,依次通过反射赋值for (InjectedElement element : elementsToIterate) {if (logger.isTraceEnabled()) {logger.trace("Processing injected element of bean '" + beanName + "': " + element);}//反射核心方法,处理参数或者方法,点击element.inject(target, beanName, pvs);}}}element.inject(target, beanName, pvs);方法:参数赋值@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {value = resolvedCachedArgument(beanName, this.cachedFieldValue);}else {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(1);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();try {//这里会触发依赖注入属性的getBean操作,前几篇分析过,此处省略value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}synchronized (this) {if (!this.cached) {if (value != null || this.required) {this.cachedFieldValue = desc;registerDependentBeans(beanName, autowiredBeanNames);if (autowiredBeanNames.size() == 1) {String autowiredBeanName = autowiredBeanNames.iterator().next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {this.cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName, field.getType());}}}else {this.cachedFieldValue = null;}this.cached = true;}}}if (value != null) {ReflectionUtils.makeAccessible(field);//最终通过反射赋值,参数如下。field.set(bean, value);}}}// 方法赋值:@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {if (checkPropertySkipping(pvs)) {return;}Method method = (Method) this.member;Object[] arguments;if (this.cached) {// Shortcut for avoiding synchronization...arguments = resolveCachedArguments(beanName);}else {int argumentCount = method.getParameterCount();arguments = new Object[argumentCount];DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();for (int i = 0; i < arguments.length; i++) {MethodParameter methodParam = new MethodParameter(method, i);DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);currDesc.setContainingClass(bean.getClass());descriptors[i] = currDesc;try {Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);if (arg == null && !this.required) {arguments = null;break;}arguments[i] = arg;}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);}}synchronized (this) {if (!this.cached) {if (arguments != null) {DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);registerDependentBeans(beanName, autowiredBeans);if (autowiredBeans.size() == argumentCount) {Iterator<String> it = autowiredBeans.iterator();Class<?>[] paramTypes = method.getParameterTypes();for (int i = 0; i < paramTypes.length; i++) {String autowiredBeanName = it.next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {cachedMethodArguments[i] = new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName, paramTypes[i]);}}}this.cachedMethodArguments = cachedMethodArguments;}else {this.cachedMethodArguments = null;}this.cached = true;}}}if (arguments != null) {try {ReflectionUtils.makeAccessible(method);method.invoke(bean, arguments);//方法执行}catch (InvocationTargetException ex) {throw ex.getTargetException();}}}
2.3.2 循环依赖问题
在填充属性的时候会涉及到循环依赖的问题
> 导致循环依赖原因
- 由构造函数造成的循环依赖
- 通过setter方法注入,且是多例模式下可能导致内存溢出
- 通过setter方法注入,且是单例模式下可能导致内存溢出
> 解决方式
在Spring中只有第3种方式的循环依赖问题被解决,其他两种会报异常。
第1种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
第2种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。
> 三级缓存
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {1.singletonobject 单例池 存放已完成初始化的bean//一级缓存(单例池,经过完成生命周期的对象会放入其中)private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);2.存放未完成初始化的但是已经实例化的bean//二级缓存(刚实例化还未初始化的原始对象会放入其中)private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);3.存放某个bean的beanfactory//三级缓存(存放创建某个对象的工厂)private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);。。。。。。。。
}
2.4 initializeBean()概览
到此时,bean 已完成了如下两个重要工作
调用 createBeanInstance() 方法:完成 bean 的实例化工作
调用 populateBean() 方法:完成 bean 的属性填充注入工作
接下来就是进行初始化工作了,先上流程图,在看细节
/*** 初始化给定的bean实例,应用工厂回调以及init方法和BeanPostProcessors** Initialize the given bean instance, applying factory callbacks* as well as init methods and bean post processors.* <p>Called from {@link #createBean} for traditionally defined beans,* and from {@link #initializeBean} for existing bean instances.* @param beanName the bean name in the factory (for debugging purposes)* @param bean the new bean instance we may need to initialize* @param mbd the bean definition that the bean was created with* (can also be {@code null}, if given an existing bean instance)* @return the initialized bean instance (potentially wrapped)* @see BeanNameAware* @see BeanClassLoaderAware* @see BeanFactoryAware* @see #applyBeanPostProcessorsBeforeInitialization* @see #invokeInitMethods* @see #applyBeanPostProcessorsAfterInitialization*/
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {// 如果安全管理器不为空if (System.getSecurityManager() != null) {// 以特权的方式执行回调bean中的Aware接口方法AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {// Aware接口处理器,调用BeanNameAware、BeanClassLoaderAware、beanFactoryAwareinvokeAwareMethods(beanName, bean);}Object wrappedBean = bean;//如果mdb为null || mbd不是"synthetic"。一般是指只有AOP相关的prointCut配置或者Advice配置才会将 synthetic设置为trueif (mbd == null || !mbd.isSynthetic()) {// 将BeanPostProcessors应用到给定的现有Bean实例,调用它们的postProcessBeforeInitialization初始化方法。// 返回的Bean实例可能是原始Bean包装器wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}try {//调用初始化方法,先调用bean的InitializingBean接口方法,后调用bean的自定义初始化方法invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {//捕捉调用初始化方法时抛出的异常,重新抛出Bean创建异常:调用初始化方法失败throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}//如果mbd为null || mbd不是"synthetic"if (mbd == null || !mbd.isSynthetic()) {// 将BeanPostProcessors应用到给定的现有Bean实例,调用它们的postProcessAfterInitialization方法。// 返回的Bean实例可能是原始Bean包装器wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}//返回包装后的Beanreturn wrappedBean;}
2.4.1 invokeAwareMethods() 方法
实现 Aware 接口用于让 bean 能拥有某些额外的感知能力
如果 bean 实现了 BeanNameAware 接口,则将 beanName 设置进去
如果 bean 实现了 BeanClassLoaderAware 接口,则将 ClassLoader 设置进去
如果 bean 实现了 BeanFactoryAware 接口,则将 beanFactory 设置进去
private void invokeAwareMethods(String beanName, Object bean) {//如果 bean 是 Aware 实例if (bean instanceof Aware) {//如果bean是BeanNameAware实例if (bean instanceof BeanNameAware) {//调用 bean 的setBeanName方法((BeanNameAware) bean).setBeanName(beanName);}//如果bean是 BeanClassLoaderAware 实例if (bean instanceof BeanClassLoaderAware) {//获取此工厂的类加载器以加载Bean类(即使无法使用系统ClassLoader,也只能为null)ClassLoader bcl = getBeanClassLoader();if (bcl != null) {//调用 bean 的 setBeanClassLoader 方法((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);}}//如果bean是 BeanFactoryAware 实例if (bean instanceof BeanFactoryAware) {// //调用 bean 的 setBeanFactory 方法((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}}
2.4.2 applyBeanPostProcessorsBeforeInitialization() 方法(重点)
在 bean 初始化之前调用,可以改变 bean 的一些属性
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {//初始化返回结果为existingBeanObject result = existingBean;//遍历 该工厂创建的bean的BeanPostProcessors列表for (BeanPostProcessor processor : getBeanPostProcessors()) {// postProcessBeforeInitialization:在任何Bean初始化回调之前(如初始化Bean的afterPropertiesSet或自定义的init方法)// 将此BeanPostProcessor 应用到给定的新Bean实例。Bean已经填充了属性值。返回的Bean实例可能时原始Bean的包装器。// 默认实现按原样返回给定的 BeanObject current = processor.postProcessBeforeInitialization(result, beanName);// 如果 current为nullif (current == null) {//直接返回result,中断其后续的BeanPostProcessor处理return result;}//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装result = current;}//返回经过所有BeanPostProcess对象的后置处理的层层包装后的resultreturn result;}
2.4.3 invokeInitMethods() 方法(重点)
Spring为 bean 提供了两种初始化的方式,第一种是实现 InitializingBean 接口调用 afterPropertiesSet() 方法来完成初始化,
第二种通过反射调用 init-method 指定的方法相比,效率相对低。但是 init-method 方式消除了对 Spring 的依赖
如果调用 afterPropertiesSet() 方法时出错,那么就不会调用 init-method 指定的方法了
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)throws Throwable {// InitializingBean:当Bean的所有属性都被BeanFactory设置好后,Bean需要执行相应的接口:例如执行自定义初始化,或者仅仅是检查所有强制属性是否已经设置好。// bean是InitializingBean实例标记boolean isInitializingBean = (bean instanceof InitializingBean);// isExternallyManagedInitMethod是否外部受管理的Init方法名// 如果bean是InitializingBean实例&&(mdb为null||'afterPropertiesSet'不是外部受管理的Init方法名)if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {// 如果是日志级别为跟踪模式if (logger.isTraceEnabled()) {logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");}// 如果安全管理器不为nullif (System.getSecurityManager() != null) {try {// 以特权方式调用 bean的 afterPropertiesSet 方法AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {((InitializingBean) bean).afterPropertiesSet();return null;}, getAccessControlContext());}catch (PrivilegedActionException pae) {throw pae.getException();}}else {// 调用bean的afterPropertiesSet方法((InitializingBean) bean).afterPropertiesSet();}}// 如果mbd不为null&&bean不是NullBean类if (mbd != null && bean.getClass() != NullBean.class) {// 获取mbd指定的初始化方法名String initMethodName = mbd.getInitMethodName();// 如果initMethodName不为null&&(bean不是InitializingBean实例&&'afterPropertiesSet'是初始化方法名)// &&initMethodName不是外部受管理的Init方法名if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) {// 在bean上调用指定的自定义init方法invokeCustomInitMethod(beanName, bean, mbd);}}}protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd)throws Throwable {// 获取初始化方法名称String initMethodName = mbd.getInitMethodName();Assert.state(initMethodName != null, "No init method set");// 获取初始化方法Method initMethod = (mbd.isNonPublicAccessAllowed() ?BeanUtils.findMethod(bean.getClass(), initMethodName) :ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));if (initMethod == null) {if (mbd.isEnforceInitMethod()) {throw new BeanDefinitionValidationException("Could not find an init method named '" +initMethodName + "' on bean with name '" + beanName + "'");}else {if (logger.isTraceEnabled()) {logger.trace("No default init method named '" + initMethodName +"' found on bean with name '" + beanName + "'");}// Ignore non-existent default lifecycle methods.return;}}if (logger.isTraceEnabled()) {logger.trace("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'");}Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {ReflectionUtils.makeAccessible(methodToInvoke);return null;});try {AccessController.doPrivileged((PrivilegedExceptionAction<Object>)() -> methodToInvoke.invoke(bean), getAccessControlContext());}catch (PrivilegedActionException pae) {InvocationTargetException ex = (InvocationTargetException) pae.getException();throw ex.getTargetException();}}else {try {ReflectionUtils.makeAccessible(methodToInvoke);// 反射执行methodToInvoke.invoke(bean);}catch (InvocationTargetException ex) {throw ex.getTargetException();}}}
2.4.4 applyBeanPostProcessorsAfterInitialization() 方法(重点)
postProcessAfterInitialization() 方法是 BeanPostProcessor 接口提供的方法,可以看到它是在 bean 初始化之后调用的,这时的 bean 已完成了实例化,属性填充注入,初始化的工作;可以认为是一个完整的 bean 已在 spring 容器中了,这个地方也是产生代理对象的地方 也就是AOP的入口
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {//初始化结果对象为result,默认引用existingBeanObject result = existingBean;//遍历该工厂创建的bean的BeanPostProcessors列表for (BeanPostProcessor processor : getBeanPostProcessors()) {//回调BeanPostProcessor#postProcessAfterInitialization来对现有的bean实例进行包装Object current = processor.postProcessAfterInitialization(result, beanName);//一般processor对不感兴趣的bean会回调直接返回result,使其能继续回调后续的BeanPostProcessor;// 但有些processor会返回null来中断其后续的BeanPostProcessor// 如果current为nullif (current == null) {//直接返回result,中断其后续的BeanPostProcessor处理return result;}//让result引用processor的返回结果,使其经过所有BeanPostProcess对象的后置处理的层层包装result = current;}//返回经过所有BeanPostProcess对象的后置处理的层层包装后的resultreturn result;}
2.4.5 registerDisposableBeanIfNecessary()
/*** 将给定Bean添加到该工厂中的可丢弃Bean列表中,注册器可丢弃Bean接口和/或在工厂关闭时调用给定销毁方法(如果适用)。只适用单例** Add the given bean to the list of disposable beans in this factory,* registering its DisposableBean interface and/or the given destroy method* to be called on factory shutdown (if applicable). Only applies to singletons.* @param beanName the name of the bean* @param bean the bean instance* @param mbd the bean definition for the bean* @see RootBeanDefinition#isSingleton* @see RootBeanDefinition#getDependsOn* @see #registerDisposableBean* @see #registerDependentBean*/protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {// 如果有安全管理器器,获取其访问控制上下文AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);// 如果mbd不是Prototype作用域 && bean在关闭时需要销毁if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {// 如果mbd是单例作用域if (mbd.isSingleton()) {// Register a DisposableBean implementation that performs all destruction// work for the given bean: DestructionAwareBeanPostProcessors,// DisposableBean interface, custom destroy method.// 注册一个一次性Bean实现来执行给定Bean的销毁工作:DestructionAwareBeanPostProcessors 一次性Bean接口,自定义销毁方法。// DisposableBeanAdapter:实际一次性Bean和可运行接口适配器,对给定Bean实例执行各种销毁步骤// 构建Bean对应的DisposableBeanAdapter对象,与beanName绑定到 注册中心的一次性Bean列表中registerDisposableBean(beanName,new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));}else {// A bean with a custom scope...// 具有自定已作用域的Bean// 获取mdb的作用域Scope scope = this.scopes.get(mbd.getScope());// 如果作用域为nullif (scope == null) {// 非法状态异常:无作用登记为作用名称'mbd.getScope'throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");}// 注册一个回调,在销毁作用域中将构建Bean对应的DisposableBeanAdapter对象指定(或者在销毁整个作用域时执行,// 如果作用域没有销毁单个对象,而是全部终止)scope.registerDestructionCallback(beanName,new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));}}}
2.4.6 补充:Spring中bean初始化的方式
一、ContextRefreshedEvent事件
ContextRefreshedEvent:是Spring容器初始化完成后调用的事件。
ContextRefreshedEvent的父类是ApplicationContextEvent,是一个事件。所以我们通过ApplicationListener来实现
@Component
public class PersonAfterListener implements ApplicationListener<ContextRefreshedEvent> {@Autowiredprivate Person person;@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {person.run("ContextRefreshedEvent");}
}
二、PostConstruct 注解
PostConstruct注解修饰的方式,是在spring容器启动时运行的。优先级大于ContextRefreshedEvent事件。
@Component
public class PersonAfterPostConstruct {@Autowiredprivate Person person;@PostConstructpublic void postConstruct(){person.run("PostConstruct");}
}
三、InitializingBean
InitializingBean是spring容器在启动并初始化好内部示例后调用的,用来最终为总体bean添加最后属性和操作。
@Component
public class PersonAfterInitializingBean implements InitializingBean {@Autowiredprivate Person person;@Overridepublic void afterPropertiesSet() throws Exception {person.run("InitializingBean");}
}
四、init-method方法
这种方法有一定的局限性,并且可能会覆盖曾经的init操作,需要慎用。
Bean在加载到Spring容器中时需要先将Bean的定义信息抽象为BeanDefinition,其中有一个属性init-method代表将来Bean初始化时要调用的方法。
我们通过BeanFactoryPostProcessor来注入init-method方法,并且该方法必须是没有参数的。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;@Component
public class PersonAfterInit implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {BeanDefinition person = beanFactory.getBeanDefinition("person");person.setInitMethodName("run");}
}
五 、实现 SmartInitializingSingleton 接口
SmartInitializingSingleton是Bean容器在初始化所有非懒加载的单例Bean后调用的方法。
@Component
public class PersonAfterSmartInitializingSingleton implements SmartInitializingSingleton {@Autowiredprivate Person person;@Overridepublic void afterSingletonsInstantiated() {person.run("SmartInitializingSingleton");}
}
六、CommandLineRunner(仅限Spring Boot)
CommandLineRunner 是一个Spring boot 接口,在应用初始化后执行,且仅会执行一次。可以用来打印项目中配置文件的参数,方便排查问题。
@Component
public class PersonAfterCommandLineRunner implements CommandLineRunner {@Autowiredprivate Person person;@Overridepublic void run(String... args) throws Exception {person.run("CommandLineRunner");}
}
相关文章:
Spring源码分析(三) IOC 之 createBean()和doCreateBean()
a、在createBean中又是主要做了什么事情? 完成bean得创建,填充属性、循环依赖 、aop等一系列过程 1、createBean() 在createBean中主要干了3件事情 1、解析class -> resolveBeanClass() 2、验证及准备覆盖的方法,lookup-method replace-method -> …...
【鸿蒙(HarmonyOS)】UI开发的两种范式:ArkTS、JS(以登录界面开发为例进行对比)
文章目录 一、引言1、开发环境2、整体架构图 二、认识ArkUI1、基本概念2、开发范式(附:案例)(1)ArkTS(2)JS 三、附件 一、引言 1、开发环境 之后关于HarmonyOS技术的分享,将会持续使…...
Flink中的批和流
批处理的特点是有界、持久、大量,非常适合需要访问全部记录才能完成的计算工作,一般用于离线统计。 流处理的特点是无界、实时, 无需针对整个数据集执行操作,而是对通过系统传输的每个数据项执行操作,一般用于实时统计。 而在Flin…...
【LeetCode-中等题】150. 逆波兰表达式求值
文章目录 题目方法一:栈 题目 方法一:栈 class Solution {public int evalRPN(String[] tokens) {Deque<Integer> deque new LinkedList<>();String rpn "-*/";//符号集 用来判断扫描的是否为运算符int sum 0;for(int i 0 ; i…...
搭建ELK+Filebead+zookeeper+kafka实验
部署 Zookeeper 集群 准备 3 台服务器做 Zookeeper 集群 192.168.10.17 192.168.10.21 192.168.10.22 1.安装前准备 关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 安装 JDK yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-…...
java专题练习(抢红包)
package 专题练习;import java.util.Random;public class grab_red_packet {/* 需求:直播抽奖,分别由{2,588,888,1000,10000}五个奖金,请用代码模拟抽奖,奖项出现顺序要随机且不重复打印效果:588元的奖金被抽出*///思路://1. 先用数组把奖金定义好//2. 用random方法给出随机数索…...
AVR 单片机 调试环境 JTAG MKII
注意 驱动 的厂家: 如果驱动备改变为其他厂家的驱动 就与 AVR Studio7不兼容 保证驱动选择正确是 能够使用硬件调试的关键 如果驱动不对,使用 USB驱动修改工具 修改 比如 UsbDriverTool.exe...
C++ - AVL树实现(下篇)- 调试小技巧
前言 本博客是 AVL树的下篇,上篇请看:C - AVL 树 介绍 和 实现 (上篇)_chihiro1122的博客-CSDN博客 上篇当中写插入操作,和其中涉及的 旋转等等细节,还有AVL树的大体框架。 调试小技巧 条件断点 在大项目…...
Mybatis懒加载
懒加载是什么? 按需加载所需内容,当调用到关联的数据时才与数据库交互否则不交互,能大大提高数据库性能,并不是所有场景下使用懒加载都能提高效率。 Mybatis懒加载:resultMap里面的association、collection有延迟加载功…...
DSOX3012A是德科技keysight DSOX3012A示波器
181/2461/8938是德科技DSOX3012A(安捷伦)示波器 是德科技DSOX3012A(安捷伦)是InfiniiVision 3000 X系列中的双通道型号。这款可升级示波器采用突破性技术设计,提供卓越的性能和功能。其独特的5仪器合一设计为相同的预算提供了更大的范围。 是德科技DSOX3012A示波器…...
基于网络表示学习的 新闻推荐算法研究与系统实现
摘要 第1章绪论 新闻推荐通常是利用用户的阅读行为和习惯、阅读选择和爱好等信息,为 用户推荐新闻内容。新闻推荐能够减少用户在数量庞大数据信息中获取信息的 时间消耗,从而能够缓解“信息过载[7]”的难题。以文本为内容的新闻,和商品、 电影、短视频等推荐系统相比,新闻推…...
<Altium Designer> 将.DSN文件导入并转换成SchDoc文件
目录 01 使用向导方式导入.DSN 02 消除Unique Identifiers Errors 03 文章总结 大家好,这里是程序员杰克。一名平平无奇的嵌入式软件工程师。 本文主要是总结和分享将OrCAD Capture画的原理图文件(.DSN)导入到Altium Designer,转换成对应的原理图文件…...
视频定格合璧,批量剪辑轻松插入图片
大家好!想要将视频与图片完美融合吗?现在,我们为您推出一款强大的批量剪辑工具,让您能够轻松在图片中插入视频,让您的创作更加精彩动人! 首先,第一步我们要进入媒体梦工厂主页面,并…...
【Tensorflow 2.12 电影推荐项目搭建】
Tensorflow 2.12 电影推荐项目搭建 学习笔记工具、环境创建项目项目配置安装相关python包召回模型实现排序模型实现实现电影推荐导入模块设置要推荐的用户召回推荐排序推荐推荐结果结尾学习笔记 Tensorflow 2.12 电影推荐项目搭建记录~ Tensorflow是谷歌开源的机器学习框架,可…...
python+opencv特征匹配算法
pythonopencv特征匹配算法 1.安装 pip install opencv-python pip install numpy2.算法明细 import cv2 import numpy as np# 读取两张图像 img1 cv2.imread(image1.jpg,0) # queryImage img2 cv2.imread(image2.jpg,0) # trainImage# 初始化SIFT对象 sift cv2.xfeatur…...
android Compose 实现 webView
在Compose中,目前还没有原生的WebView组件。但是,您可以使用Android Jetpack组件中的AndroidView来将传统的WebView集成到Compose中。下面是一个示例代码: Composable fun WebViewScreen(url: String) {AndroidView(factory { context ->…...
算法基础-数学知识-欧拉函数、快速幂、扩展欧几里德、中国剩余定理
算法基础-数学知识-欧拉函数、快速幂、扩展欧几里德、中国剩余定理 欧拉函数AcWing 874. 筛法求欧拉函数 快速幂AcWing 875. 快速幂AcWing 876. 快速幂求逆元 扩展欧几里德(裴蜀定理)AcWing 877. 扩展欧几里得算法AcWing 878. 线性同余方程 中国剩余定理…...
ElasticSearch系列-索引原理与数据读写流程详解
索引原理 倒排索引 倒排索引(Inverted Index)也叫反向索引,有反向索引必有正向索引。通俗地来讲,正向索引是通过key找value,反向索引则是通过value找key。ES底层在检索时底层使用的就是倒排索引。 索引模型 现有索…...
【码银送书第七期】七本考研书籍
八九月的朋友圈刮起了一股晒通知书潮,频频有大佬晒出“研究生入学通知书”,看着让人既羡慕又焦虑。果然应了那句老话——比你优秀的人,还比你努力。 心里痒痒,想考研的技术人儿~别再犹豫了。小编咨询了一大波上岸的大佬ÿ…...
docker容器的设置本地时间(/etc/localtime)和本地时区(/etc/timezone)
本地时区的修改 一般情况下,我们启动docker容器时指定了环境变量: -e TZ:Asia/Ho_Chi_Minh ,容器内的时区就会变成东八区,某些软件则会读取该环境变量作为其使用的时区,该环境变量相当于"残缺版"的命令&…...
侯捷老师C++课程:内存管理
内存管理 第一讲:primitives c应用程序 c内存的基本工具 测试程序: #include <iostream> using namespace std; #include <complex> #include <ext/pool_allocator.h>int main() {// 三种使用方法void* p1 malloc(512); // 512 b…...
A股风格因子看板 (2023.09 第05期)
该因子看板跟踪A股风格因子,该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子,用以分析市场风格切换、组合风格暴露等。 今日为该因子跟踪第05期,指数组合数据截止日2023-08-31,要点如下 近1年A股风格因子检验统…...
修炼离线:(二)sqoop插入hbase 脚本(增量)
一:mysql创建表,插入数据。 二:hbase创建表。 habse shell create aa(表名),cf(列族)三:mysql_hbase脚本。 #!/bin/shmysqlHost$1 mysqlUserName$2 mysqlUserPass$3 mysqlDbName$4 myqlTbName$5 hbaseTbName$6 hbaseTbRowkey$7…...
跨平台编程开发工具Xojo 2023 Release mac中文版功能介绍
Xojo mac是一款跨平台的软件开发工具,它允许开发人员使用一种编程语言来创建应用程序,然后可以在多个操作系统上运行。Xojo 2023是Xojo开发工具的最新版本,它提供了许多功能和改进,以帮助开发人员更轻松地构建高质量的应用程序。 …...
OpenCV Series : Target Box Outline Border
角点 P1 [0] (255, 000, 000) P2 [1] (000, 255, 000) P3 [2] (000, 000, 255) P4 [3] (000, 000, 000)垂直矩形框 rect cv2.minAreaRect(cnt)targetColor roi_colortargetThickness 1targetColor (255, 255, 255)if lineVerbose:if …...
【AD】【规则设置】设置四层板
设置四层板 一般 4层板,都会把 地 和 VCC放在内层。1、使用快捷键D-K 进入层叠管理器,添加负片层添加完后,修改层名,方便辨识修改格式:属性层号 2、进入相应layer 设置网络设置GND层设置VCC层特点:在层内可…...
Linux安装JDK1.8并配置环境变量
Linux安装JDK并配置环境变量Linux安装JDK并配置环境变量Linux安装JDK并配置环境变量 一、查询已有JAVA环境版本信息 java -version 二、下载Oracle JDK安装包 https://www.oracle.com/java/technologies/downloads/archive/ 三、安装 配置JDK 以下方式适用于安装各版本JDK&…...
面向面试知识--MySQL数据库与索引
面向面试知识–MySQL数据库与索引 优化难点与面试点 什么是MySQL索引? 索引的MySQL官方定义:索引是帮助MySQL快速获取数据的数据结构。 动力节点原文: MysQL官方对于索引的定义:索引是帮助MySQL高效获取数据的数据结构。 MysQL在存储数据之…...
portainer + portainer/agent
参考链接 https://docs.portainer.io/ portainer 免费版 portainer-ce 免费版 portainer-ee 企业版 portainer-agent docker本机代理 agent 下载地址 https://download.csdn.net/download/a309450028a/87451332 portainer 下载地址 https://download.csdn…...
C# 截取字符串
在 C# 中,可以使用 Substring 方法来截取字符串的一部分。该方法有两个参数:起始索引和要截取的字符数。 以下是使用 Substring 方法截取字符串的示例: string str "Hello World"; string result str.Substring(6); // 从索引为…...
网页设计与网站建设 在线测试/seo网站推广教程
先点hex 后点 decimal 再开始start attack,这是一个软件bug,需要手动让它刷新。...
wordpress跟php/重庆seo网站收录优化
如果用jquery的话: var lable_a $(li).children(a); lable_a.click(function() { lable_a.css(color, black); $(this).css(color, red); });...
网站建设第三方平台/百度网盘资源搜索入口
今天主要学了爬虫技术,爬取mp4格式,爬取豆瓣电影。一、爬虫原理 1.什么是互联网? 指的是由一堆网络设备,把一台台的计算机互联网到一起称之为互联网。 2.互联网建立的目的? 互联网建立的目的是为了数据的传递以及数据的…...
门户网站建设方案公司/兰州网络推广
本节书摘来自异步社区《Nmap渗透测试指南》一书中的第1章1.9节扫描指定段,作者 商广明,更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.9 扫描指定段在Nmap中我们可以指定扫描一个C段,这个功能不需要其他额外的选项,只需要使用“-…...
郑州网站建设微信小程序/营销型企业网站建设步骤
Maven下载与安装 1、下载1)Maven的系统要求: Maven对内存和操作系统没有要求 Maven安装本身仅需大约10MB,本地仓库视使用情况有所不同 Maven3.3及以上版本需要JDK1.7及以上(截至今日,Maven官网提供的最新版本为3.5.2&a…...
北京市住房和建设委员会网站/东莞营销网站建设
http://pengranxiang.iteye.com/blog/848862 首先我们先介绍一下为什么要让 Apache 与 Tomcat 之间进行连接。事实上 Tomcat 本身已经提供了 HTTP 服务,该服务默认的端口是 8080,装好 tomcat 后通过 8080 端口可以直接使用 Tomcat 所运行的应用程序&…...