EDITORIAL words
Background and resources:
Cao worker said Spring Boot source (5) - how to read from the bean properties file
Cao worker said Spring Boot source (6) - Spring bean from how to resolve the xml file
Cao worker said Spring Boot source (7) - Spring parse xml file, which in the end got what (a)
Cao worker said Spring Boot source (22) - You say I Spring Aop rely AspectJ, I rely on it what the
Project code address Mind Mapping address
Engineering Schematic:
Overview
This lecture is relatively independent, we do not love bullshit, but to say this lecture Zuosa.
As we all know, @ Component-scan annotation in the annotation of the times, the most important use is to specify the name of a package, and then spring will be to package the following corresponding scanning annotated @ Controller, @ Service, @ Repository annotation and other categories, then registered as a bean.
spring boot times, this comment is to stand behind the scenes, as follows:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter( type = FilterType.CUSTOM,classes = {AutoConfigurationExcludeFilter.class})
}
)
public @interface SpringBootApplication {
}
In short, this annotation we too familiar. We talk about this, the core goal is to let everyone know better spring, way to do that is to allow ourselves to achieve the following objectives:
-
Defined main class
@MyConfiguration @MyComponentScan(value = "org.springframework.test") public class BootStrap { public static void main(String[] args) { ... }
The above defines two custom annotations have added a prefix "My". Following is a brief look.
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyConfiguration { }
This effect is similar to @configuration, means that we are a configuration class, usually have a bunch of other notes on configuration classes, to introduce other bean definition.
Then MyComponentScan notes:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyComponentScan { /* * 要扫描的包名 */ String value(); }
-
Under our target package, we need to have a scan bean, as follows:
package org.springframework.test; @MyComponent @MyComponentScan(value = "org.springframework.test1") public class PersonService { private String personname1; }
As used herein MyComponent annotation, this annotation, annotation we used to scan for those who like bean. For example, here, we hope PersonService this class are scanned as bean.
Secondly, we have defined a
@MyComponentScan(value = "org.springframework.test1")
, this is mainly: We want to be able to support recursive processing.Of course, you can ignore the first time being when it does not exist.
-
The final test results are as follows:
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.custom.MyConfigurationClassPostProcessor; import org.springframework.custom.annotation.MyComponentScan; import org.springframework.custom.annotation.MyConfiguration; import org.springframework.test1.AnotherPersonService; @MyConfiguration @MyComponentScan(value = "org.springframework.test") public class BootStrap { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(BootStrap.class); context.registerBeanDefinition(BootStrap.class.getName(), beanDefinition); /** * 注册一个beanFactoryPostProcessor,用来处理MyComponentScan注解 */ RootBeanDefinition def = new RootBeanDefinition(MyConfigurationClassPostProcessor.class); def.setSource(null); context.registerBeanDefinition(MyConfigurationClassPostProcessor.class.getName(), def); context.refresh(); PersonService bean = context.getBean(PersonService.class); System.out.println(bean); } }
Output is as follows:
12:39:27.259 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'personService'
org.springframework.test.PersonService@71ba5790
You can see, our goal is more than that. To achieve this goal, in fact, we modeled the realization spring he ctrl c / v one, which a lot of simplification.
Realization of ideas
The basic idea is equivalent to the realization of the spring, because the purpose of this series is to let everyone know better Spring, so no need to open a new path.
Notes a starting point for class configuration with @MyConfiguration
Specifies a @MyConfiguration annotated class, a start will be registered as a bean definition, similar to the spring boot the startup class, you know, spring boot, start the class is also an indirect annotated @SpringBootConfiguration, but it @SpringBootConfiguration , we look at:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
The comment is a comment @configuration.
In fact, manually specify a configuration class, this class configuration, there seems to source code is called the startUp config, as a starting point, through this starting point, we can find in this kind of starting point, more yuan notes, for example, in our company. real project, start class configuration on a bunch of things:
@SpringBootApplication
@EnableTransactionManagement
@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.xxx.cad.mapper")
@ComponentScan("com.xxx")
@EnableFeignClients
//@Slf4j
@Controller
@EnableScheduling
public class CadWebService {
This class is our starting point here is class. The starting point of the class, then registered as a bean.
A register BeanDefinitionRegistryPostProcessor, @MyComponentScan for processing the configuration class, to find more bean
We all know that @configuration configuration class, how is it being treated? It is through org.springframework.context.annotation.ConfigurationClassPostProcessor
.
This class does, implement the following interfaces:
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
This method is called as soon as the interface is in: Up to now, the configuration (whether xml, or as the first step above this, to manually register the bean definition) way to find the bean definition have been found; the original the next step is to find out which of singleton bean eager-init, and to initialize.
But then, before that, we have a step, it can be considered an extension point that allows us to modify the current definition of a collection of bean.
For example, if popular, something like this, for example, a company, organization, everyone went out to play, we volunteered, began to assume a reported 10 people scheduled Saturday departure; Until then, let us confirm the company , we can
- Increase, with families, with male and female friends;
- Delete, they do not go up
- Change, I would also like to write like Saturday bug, but I have another colleague lacks something, he can go
To achieve these, as long as you realize that the interface methods we mentioned earlier can be, we can then observe this method:
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
The parameters are registry, which is the current registration form, registration form gave you, what you have is not doing?
public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
...
}
Take the above example that several methods:
- registerBeanDefinition, this is the person added to the list
- removeBeanDefinition, this is their own not to go
- BeanDefinition getBeanDefinition (String beanName), this can be found in their own name registration information, change of name, no problem, right?
@Configuration annotation process is dependent on the realization of a BeanDefinitionRegistry
class interface, in this class, it did a lot of things:
If the current list, only the start of our class, right, marked with a lump of notes, what various Enable, what Component-scan it, there. But we label these, such as component-scan, not them the play, is to go to the corresponding package, to help us scan the bean; this is equivalent to saying that, to add people to the list.
The logic can be understood as substantially:
-
Get the initial list, here it is the bean definition of the start classes, as well as some of the other hand to get into the bean definition
-
By implementing the
BeanDefinitionRegistry
ofConfigurationClassPostProcessor
, take a look at the first step of the initial list, there is no comment @ Component-scan, if not comment directly return; if the annotation, to the third step; -
Get the package name @ component-scan in the configuration to be scanned, and then get all the class in this package, then look at these class full condition (for example, recognize only notes the controller, service, etc. annotations)
-
The third step out of the screen, to meet the conditions of the class, they qualified themselves, can be used as a bean; and then see if they have no qualifications as a configuration class, I get the following example:
@Component @ComponentScan(value = "xxxx.xxxx") public class PersonService { private String personname1; }
Itself, the above example, PersonService because of the credit component-scan, has been closed for the bean, but this is not the end, because it also notes above the @ComponentScan own annotations, and this, we need to recursive processing.
Some students might feel a little extreme, maybe, but, following the example of the extreme right:
@Component @Import({MainClassForTestAnnotationConfig.class}) public class PersonService { private String personname1;
If you feel @Import extreme, then @ImportResource to import xml file in the bean, this scene, sometimes still encounter it, for example, be compatible with the old program when.
And I want to say is that in
ConfigurationClassPostProcessor
the process of handling @configuration notes, if found to have the following behavior on the class, will be processed recursively:- Inner classes to parse
PropertySource
annotationComponentScan
annotationImport
annotationImportResource
annotation- There
Bean
annotated method - Processing superClass
Overall, this class is quite difficult, and will be processed recursively.
Our demo today, in order to focus, and to achieve a simple, deal only with the first recursive @ component-scan itself.
Next, take a look at implementation.
Implementation
Test class, the main logic flow of the overall drive
@MyConfiguration
@MyComponentScan(value = "org.springframework.test")
public class BootStrap {
public static void main(String[] args) {
// 1
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(BootStrap.class);
context.registerBeanDefinition(BootStrap.class.getName(), beanDefinition);
/**
* 2 注册一个beanFactoryPostProcessor
*/
RootBeanDefinition def = new RootBeanDefinition(MyConfigurationClassPostProcessor.class);
def.setSource(null);
context.registerBeanDefinition(MyConfigurationClassPostProcessor.class.getName(),
def);
// 3
context.refresh();
// 4
PersonService bean = context.getBean(PersonService.class);
System.out.println(bean);
AnotherPersonService anotherPersonService = context.getBean(AnotherPersonService.class);
System.out.println(anotherPersonService);
}
}
- At 1, the use of the default spring driven context annotations, provided: config starting class for the current class, registered to spring containers
- At 2, registered a MyConfigurationClassPostProcessor to spring container, similar to this and speaking in front of ConfigurationClassPostProcessor effect, used to resolve our own @MyComponentScan
- At 3, load context
- 4, the bean acquisition, detection.
MyConfigurationClassPostProcessor,解析@MyComponentScan
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
log.info("postProcessBeanDefinitionRegistry...");
/**
* 1: 找到标注了{@link org.springframework.custom.annotation.MyConfiguration}注解的类
* 这些类就是我们的配置类
* 我们通过这些类,可以发现更多的bean definition
*/
Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<BeanDefinitionHolder>();
for (String beanName : registry.getBeanDefinitionNames()) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (MyConfigurationUtils.checkConfigurationClassCandidate(beanDef)) {
beanDefinitionHolders.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 2
if (CollectionUtils.isEmpty(beanDefinitionHolders)) {
return;
}
// 3
MyConfigurationClassParser parser = new MyConfigurationClassParser(environment,registry);
parser.parse(beanDefinitionHolders);
}
- At 1, find the current, marked notes of all the bean definition MyConfiguration
- At 2, if there is, return
- At 3, the first step in the collection found, for further processing
Specific analytical work, who fell on the third step, specifically, is MyConfigurationClassParser class body.
MyConfigurationClassParser specific executor
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
String className = bd.getBeanClassName();
try {
processConfigurationClass(className);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex);
}
}
}
Here is the class for each configuration found for processing. For example, demo here we find is the startup class.
Then call the following method:
protected void processConfigurationClass(String className) throws IOException {
MetadataReader metadataReader = MyConfigurationUtils.getMetadataReader(className);
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
/**
* 1. 判断该类上,是否有标注{@link MyComponentScan}
*/
Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(MyComponentScan.class.getName(), true);
AnnotationAttributes componentScan = AnnotationAttributes.fromMap(annotationAttributes);
/**
* 2. 如果类上有这个{@link MyComponentScan},则需要进行处理
*/
if (componentScan != null) {
/**
* 3. 马上扫描这个base package路径下的bean,在里面,会注册beanDefinition到bean registry
*/
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, annotationMetadata.getClassName());
/**
* 4. 如果扫描回来的bean definition不为空,递归处理
*/
if (!CollectionUtils.isEmpty(scannedBeanDefinitions)) {
this.parse(scannedBeanDefinitions);
}
}
}
- At 1, such a determination, whether there is marked @MyComponentScan
- At 2, if the @MyComponentScan the class, then need to be processed
- At 3, immediately bean scanning path under the base package, on the inside, is registered to the bean Registry beanDefinition
- 4, the third step of scanning, bean, recursive processing, find more bean
The third step is the focus here, to a place called the componentScanParser to deal with this componentScanParser in this class initialization time of assignment:
public MyConfigurationClassParser(Environment environment, BeanDefinitionRegistry registry) {
this.environment = environment;
this.registry = registry;
this.componentScanParser = new MyComponentScanParser(componentScanBeanNameGenerator,
environment,registry);
}
MyComponentScanParser of the process
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String className) {
// 1
String basePackage = componentScan.getString("value");
// 2
includeFilters.add(new AnnotationTypeFilter(MyComponent.class));
// 3
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
/**
* 4 获取包下的全部bean definition
*/
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
/**
* 5 对扫描回来的bean,进行一定的处理,然后注册到bean registry
*/
for (BeanDefinition candidate : candidates) {
String generateBeanName = componentScanBeanNameGenerator.generateBeanName(candidate, registry);
if (candidate instanceof AbstractBeanDefinition) {
((AbstractBeanDefinition)candidate).applyDefaults(this.beanDefinitionDefaults);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
boolean b = checkCandidate(generateBeanName, candidate);
if (b) {
// 6
beanDefinitions.add(new BeanDefinitionHolder(candidate,generateBeanName));
}
}
/**
* 7 注册到bean definition registry
*/
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitions) {
registry.registerBeanDefinition(beanDefinitionHolder.getBeanName(),beanDefinitionHolder.getBeanDefinition());
}
return beanDefinitions;
}
- At 1, get MyComponentScan annotation value information indicating the package to be scanned
- At 2, set up to identify the bean rule, here is the comment of the @MyComponent, considered to be one of us
- At 3, the definition of variables, for storing returned results
- 4, under conditions satisfying all scan package, bean definition
- At 5, the processing step 4 to get the definition set bean
- At 6, is added to the result set to be returned
- At 7, registered with the spring container
Above, at only 4 needed Again, others are relatively simple.
Get to meet the conditions of the bean definition process
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
// 1
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
// 2
for (Resource resource : resources) {
log.info("Scanning " + resource);
if (!resource.isReadable()) {
continue;
}
// 3
MetadataReader metadataReader = MyConfigurationUtils.getMetadataReader(resource);
// 4
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
// 5
candidates.add(sbd);
}
else {
log.info("Ignored because not matching any filter: " + resource);
}
}
}
...
return candidates;
}
- 1 at the entire class, the scan package
- At 2, traversing the class
- At 3, acquire information on the class of notes
- 4, the use of annotation information, determines whether or not one of us (annotated @MyComponent)
- At 5, one of us, ready to take away
Wherein, at 4, to achieve the following:
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, MyConfigurationUtils.getMetadataReaderFactory())) {
return true;
}
}
return false;
}
You are to use includeFilters to match, in front of you remember, we set the bar:
includeFilters.add(new AnnotationTypeFilter(MyComponent.class));
General process is the case.
The realization dubbo, superficial analysis
I found, and like I said above, the difference could not be too much, I have not used dubbo, did not achieve the reference dubbo.
For example, it defines a class that implements a BeanDefinitionRegistryPostProcessor, called:
public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
Its implementation is as follows:
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registerBeans(registry, DubboBootstrapApplicationListener.class);
// 1
Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
// 2
registerServiceBeans(resolvedPackagesToScan, registry);
} else {
if (logger.isWarnEnabled()) {
logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
}
}
}
- At 1, found to be scanned package
- At 2, scans the specified package
At 2 above, to achieve the following:
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
// 1
scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));
// 2
scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));
// 3
for (String packageToScan : packagesToScan) {
// 4 Registers @Service Bean first
scanner.scan(packageToScan);
// 5
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
// 6
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 7
registerServiceBean(beanDefinitionHolder, registry, scanner);
}
}
}
}
- 1, set includeFilters, annotation types, notes the
org.apache.dubbo.config.annotation.Service
type even if - 2, or set includeFilters, only for previous compatible
- 3, traverse to scan package
- 4, scan the specified package
- 5, scan package, satisfying the condition set acquired
- 6, the fifth step is returned not empty, then start at step 7 below to register to spring
- 7, registered to the spring
to sum up
After the previous explanation, we should immediately more clearly now, if still a little ignorant, it is best to try the demo pulled down.
If you think a little help, help point a praise it.