Conditions of annotation

Conditional annotations were briefly introduced in IoC and four commonly used annotations were listed. They have @Conditional annotations inside and their scope is basically class and method. The implementation of Conditional annotations will be described in detail next.

ConditionEvaluator - > shouldSkip () : / / judge whether there is a Conditional comments, there is no direct return the if (metadata = = null | |! metadata.isAnnotated(Conditional.class.getName())) { return false; }... For (Condition Condition: conditions) {ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } / / execution annotation value condition. Here matches approach to match, returns a Boolean value if ((requiredPhase = = null | | requiredPhase = = phase) &&! Condition. Matches (this.context, metadata)) {return true; } } return false; SpringBootCondition->matches(): ConditionOutcome outcome = getMatchOutcome(context, metadata);Copy the code

The conditions here is a class or method of annotation @ Conditional annotations value, if there are multiple needs are met, @ ConditionalOnClass OnClassCondition, ConditionalOnBean (OnBeanCondition) and conditionprofile (Matches).

OnClassCondition

OnClassCondition->getMatchOutcome(): List<String> conditionalonclass.class = getCandidates(metadata, conditionalonclass.class); if (onClasses ! = null) { List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader); // Return if (! missing.isEmpty()) { return ConditionOutcome .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) .didNotFind("required class", "required classes") .items(Style.QUOTE, missing)); / / omission partly annotations ConditionalOnMissingClass processing, logic similar... FilteringSpringBootCondition->filter(): Matches (classnamefilter.matches (candidate, classLoader)) {matches. Add (candidate); } return matches; FilteringSpringBootCondition->ClassNameFilter: protected enum ClassNameFilter { PRESENT { @Override public boolean matches(String className, ClassLoader classLoader) { return isPresent(className, classLoader); } }, MISSING { @Override public boolean matches(String className, ClassLoader classLoader) { return ! isPresent(className, classLoader); }}; isPresent(): try { forName(className, classLoader); return true; } catch (Throwable ex) { return false; }}Copy the code

@ ConditionalOnClass processing load is a given class, without skip, @ ConditionalOnMissingClass rather than the opposite.

OnBeanCondition

Assume that ConditionalOnBean has the following value

OnBeanCondition->getMatchOutcome(): ConditionMessage matchMessage = ConditionMessage.empty(); If (metadata. IsAnnotated (ConditionalOnBean. Class. GetName ())) {/ / the annotation information encapsulated into BeanSearchSpec object / / if you don't specify types and attribute names, To see if the annotation works with @bean, if so, add the fully qualified name of the returned class BeanSearchSpec spec = new BeanSearchSpec(Context, metadata, conditionAlonBean.class); MatchResult matchResult = getMatchingBeans(context, spec); // Return if (! matchResult.isAllMatched()) { String reason = createOnBeanNoMatchReason(matchResult); return ConditionOutcome.noMatch(ConditionMessage .forCondition(ConditionalOnBean.class, spec).because(reason)); } / / omission partly annotations ConditionalOnMissingBean processing, logic similar... OnBeanCondition->getMatchingBeans(): beans.getTypes()) { Collection<String> typeMatches = getBeanNamesForType(beanFactory, type, typeExtractor, context.getClassLoader(), considerHierarchy); . } // The ellipsis is used to handle annotations and names, using logic similar to...... return matchResult; OnBeanCondition->getBeanNamesForType(): try { return getBeanNamesForType(beanFactory, considerHierarchy, ClassUtils.forName(type, classLoader), typeExtractor); } the catch (a ClassNotFoundException | NoClassDefFoundError ex) {/ / here also took into account the loading class does not exist the return Collections. EmptySet (); }Copy the code

The BeanSearchSpec object encapsulated by @conditionalonBean is as follows

The result is BeanTypeRegistry’s getNamesForType() method

BeanTypeRegistry->getNamesForType(): // Go through the BDN and manualSingletonNames of the IoC container, add updateTypesIfNecessary() if the current class member variable beanTypes does not have a name; Return this.beantypes.entryset ().stream().filter((entry) -> {Class<? Return this.beantypes.entryset ().stream(). > beanType = extractType(entry.getValue(), typeExtractor); return beanType ! = null && type.isAssignableFrom(beanType); }).map(Map.Entry::getKey).collect(Collectors.toCollection(LinkedHashSet::new));Copy the code

ForType, instead of using the getBeanNamesForType method of the IoC container, we use a very clever way of traversing + verifying the instantiated object.

Using @conditionalonBean has the issue of bean loading order. Only beans with @Component initially scanned in the project will be judged after they have all been put into the container, so it is recommended to use this annotation only at the @Component level.

ConditionalOnMissingBean is used in conjunction with @bean. ConditionalOnMissingBean does not set any attributes and returns the fully qualified name of the class that defines the Bean. The beanName is added to the BDN and BDM when it is not in the IoC container

ProfileCondition

As observed in the Matches method of ProfileCondition, the @profile attribute is retrieved and the environment attribute is checked to see if the attribute is contained in the Profile, returning true if it is, and false if it is not.

< AbstractEnvironment >isProfileActive(): AbstractEnvironment->isProfileActive(): // Get activeProfiles Set<String> currentActiveProfiles = doGetActiveProfiles(); // Get activeProfiles Set<String> currentActiveProfiles = doGetActiveProfiles(); // Current TactiveProfiles returns true if it contains, Otherwise it returns false return (currentActiveProfiles. The contains (profile) | | (currentActiveProfiles. IsEmpty () && doGetDefaultProfiles().contains(profile)));Copy the code

conclusion

To explore how Conditional annotations work, find the @conditional attribute value of the Conditional annotation and check its matches() method.

Lazy loading

Instantiation and dependency injection of IoC

Lazy loading, which refers to the @lazy annotation, will not be instantiated immediately if it is used on a bean, but will be instantiated if the bean has references or dependencies, and will not inject dependencies immediately if it is used on a field.

For bean reference code in the DefaultListableBeanFactory – > preInstantiateSingletons ().

Assume that the @lazy @autowired annotation is used for field references.

DefaultListableBeanFactory->resolveDependency() Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); ContextAnnotationAutowireCandidateResolver->getLazyResolutionProxyIfNecessary(): return (isLazy(descriptor) // --1 ? buildLazyResolutionProxy(descriptor, beanName) : null); ContextAnnotationAutowireCandidateResolver - > isLazy () : / / / / - 1 traversal field annotations, look to whether have @ Lazy annotations for (the Annotation Ann: descriptor.getAnnotations()) { Lazy lazy = AnnotationUtils.getAnnotation(ann, Lazy.class); if (lazy ! = null && lazy.value()) { return true; } } ContextAnnotationAutowireCandidateResolver->buildLazyResolutionProxy(): @Override public Object getTarget() { Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);  . } ProxyFactory pf = new ProxyFactory(); pf.setTargetSource(ts); Class<? > dependencyType = descriptor.getDependencyType(); if (dependencyType.isInterface()) { pf.addInterface(dependencyType); } return pf.getProxy(beanFactory.getBeanClassLoader()); ProxyFactory->getProxy(): return createAopProxy().getProxy(classLoader); ProxyCreatorSupport->createAopProxy(): return getAopProxyFactory().createAopProxy(this); DefaultAopProxyFactory->createAopProxy(): // If it is an interface, return the Jdk proxy, Otherwise returns if additional agent (config. IsOptimize () | | config. IsProxyTargetClass () | | hasNoUserSuppliedProxyInterfaces (config)) {return  new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); }Copy the code

When @lazy is applied to a field, it returns a dynamic proxy. When the field method is called, the proxy class calls the getTarget() method to inject and get the object from the IoC container and calls the object’s methods for the final invocation.

Appendix: Common classes and methods for IO reading

Nio ByteBuffer common methods

File input streamFileInputStream

  • read()Read a byte, return -1 at the end, same as below
  • read(byte b[])Reads up to the length of array B, populates the array with values, and returns -1, similarly
  • read(byte b[], int off, int len)Read len bytes, starting with the array’s off index, and fill the array. Note that off+len cannot exceed the length of the array. Return -1, similarly
  • getChannel()Returns a file channel, related to NIO

Buffered input streamBufferedInputStream(fileInputStream)

Read bytes up to the length of the built-in byte array (default: 8192), populate the array with values, and read the built-in byte array instead of the real IO

As shown in the figure above, in is the file stream, buf is the built-in byte array, count is the length of bytes read at this time, pos is the subscript, len is the displacement of pos after len bytes read

When pos reaches count, IO is performed again. If count returns 0, the read is complete, and pos=count=0

ByteArrayInputStream(byte[])

When instantiated, it puts an array of bytes inside, reads the array of bytes, no in object, other input stream with buffer

If pos and count are equal, the read is complete

File character stream FileReader

Read files as characters, using read internal maintenance ByteBuffer instead of real IO

As shown in the figure above, in UTF-8 encoding, one Chinese character is three bytes, one letter is one byte, and two characters are read at a time by default. If position returns 0, the read is complete. Postion =limit=0

BufferedReader(fileReader)

FileReader IO fills the ByteBuffer, then reads the maximum number of bytes from the ByteBuffer (default: 8192), converts the bytes into characters to fill the array, and reads the built-in character array instead of the real IO

When nextChar reaches nChars, ByteBuffer is read again. If ByteBuffer finishes reading, it finishes reading, and nextChar=nChar

  • readLine()Read a line