I blog at bugstack.cn

Precipitate, share, grow, let oneself and others can have a harvest! 😄

One, foreword

It's so complicated! The big rush is over before you know it!

Have you experienced 618 and Double 11? Do you make a few cents by engaging in all the complicated marketing activities of big Time? Have you ever developed a big push that took you a week to read and understand the gameplay but only used 3 days? Sometimes the requirements of some products are really too complex, so complex that the development and testing need to be continuously learned in the whole process to finally understand why the product is such a play. If it is a long-term activity, it may also be considered, to cultivate the user’s mind? But this whole set of new pull, help, activation, order, insurance, coupons, consumption, open red envelopes and so on a series of prostitution operation down, if the online only 3 days, or only 1 day, that TM even participate in the user did not understand, the activity is over, finally can call what kind of good data? To this process complex, estimate even wool when all despise!!

The above is just an example, most of the time it won’t be so disgusting, and the judges won’t get over it! And the same point in the program design and development and use is the same, if you put your code logic implementation too scattered, let the external caller at the time of use, you need to call your interface more and many times, still no news touch, only their training in rotation your interface to check the order status regularly, also can check 10 at a time, you said no more, And so anti-human design, will give the caller to do you experience.

Therefore, if we can accomplish the purpose of the case, we want to be as simple as possible process, pattern clear, automatic service. This is also reflected in the Spring framework, the popularity of the framework and it can bring convenience is inseparable, and if we can achieve such convenience, it must be a good design and implementation.

Second, the target

In fact, by this chapter, we have implemented all the core content about IOC and AOP, but the use is a bit like the earlier Spring version, which needs to be configured in spring.xml one by one. This is a big difference from the actual Spring framework currently in use, and it’s built on core functionality logic that makes it easier to use with fewer configurations.

Including: package scan register, use of annotation configuration, placeholder attribute of filling, etc., and our goal is to fill on the current core logic some automation function, let everybody can learn this part of the design and implementation, to realize some implementation process about the code logic, sum up the experiences of some coding.

Three,

First we need to consider 🤔. In order to simplify the configuration of Bean objects and make the entire registration of Bean objects automatically scanned, the basic elements are: Scan path entry, XML parse scan information, annotate Bean objects to be scanned, scan Class objects to extract basic information about Bean registration, assemble registration information, and register Bean objects. With the support of these conditional elements, it is possible to complete the registration of Bean objects by using custom annotations and configuring scan paths. BeanFactoryPostProcessor: ${token}; BeanFactoryPostProcessor: ${token}; This is because it handles the ability to modify BeanDefinition properties after all BeanDefinitions have been loaded and before the Bean object is instantiated, in order to later incorporate such content into automated configuration processing. The overall design structure is as follows:

In conjunction with the bean life cycle, package scanning is simply a matter of scanning classes for specific annotations, extracting information about the classes and assembling them into BeanDefinitions to register with the container.

Parses the
tag in XmlBeanDefinitionReader. Scan class assembly BeanDefinition and registered to the container operation in ClassPathBeanDefinitionScanner# doScan in implementation.

  • Automatic scan registration scans classes that have custom annotations added, extracts the class information during XML loading, and assembers the BeanDefinition to register with the Spring container.
  • So we’re going to use<context:component-scan />Configure the package path and parse it in XmlBeanDefinitionReader and handle it accordingly.This processing involves scanning the class, retrieving annotation information, and so on
  • Finally, it includes a section onBeanFactoryPostProcessorBecause we need to load the placeholder configuration information, we need to use BeanFactoryPostProcessor after all beanDefinitions have been loaded and before we instantiate the Bean object. Example Modify the properties of BeanDefinition.This part of the implementation also prepares for subsequent processing regarding placeholder configuration to annotations

Four, implementation,

1. Engineering structure

small-spring-step-13└ ─ ─ the SRC ├ ─ ─ the main │ └ ─ ─ Java │ └ ─ ─ cn. Bugstack. Springframework │ ├ ─ ─ aop │ │ ├ ─ ─ aspectj │ │ │ └ ─ ─ AspectJExpressionPointcut. Java │ │ │ └ ─ ─ AspectJExpressionPointcutAdvisor. Java │ │ ├ ─ ─ framework │ │ │ ├ ─ ─ adapter │ │ │ │ └ ─ ─ MethodBeforeAdviceInterceptor. Java │ │ │ ├ ─ ─ autoproxy │ │ │ │ └ ─ ─ MethodBeforeAdviceInterceptor. Java │ │ │ ├ ─ ─ │ ├─ bass exercises. ├─ Bass exercises. ├─ Bass exercises. ├─ Bass exercises. ├─ Bass exercises ReflectiveMethodInvocation. Java │ │ ├ ─ ─ AdvisedSupport. Java │ │ ├ ─ ─ Advisor. Java │ │ ├ ─ ─ BeforeAdvice. Java │ │ ├ ─ ─ Classfilter.java │ ├── MethodBeforeadvice.java │ ├─ ├─ MethodMatcher.java │ ├─ Pointcut │ ├── exercises │ ├─ Exercises │ ├─ Exercises │ ├─ exercises │ ├─ Exercises │ ├─ Exercises │ ├─ Exercises AutowireCapableBeanFactory. Java │ │ │ │ ├ ─ ─ BeanDefinition. Java │ │ │ │ ├ ─ ─ spring BeanFactoryPostProcessor. Java │ │ │ │ ├ ─ ─ BeanPostProcessor. Java │ │ │ │ ├ ─ ─ BeanReference. Java │ │ │ │ ├ ─ ─ ConfigurableBeanFactory. Java │ │ │ │ ├ ─ ─ InstantiationAwareBeanPostProcessor. Java │ │ │ │ └ ─ ─ SingletonBeanRegistry. Java │ │ │ ├ ─ ─ support │ │ │ │ ├ ─ ─ AbstractAutowireCapableBeanFactory. Java │ │ │ │ ├ ─ ─ AbstractBeanDefinitionReader. Java │ │ │ │ ├ ─ ─ AbstractBeanFactory. Java │ │ │ │ ├ ─ ─ BeanDefinitionReader. Java │ │ │ │ ├ ─ ─ BeanDefinitionRegistry. Java │ │ │ │ ├ ─ ─ CglibSubclassingInstantiationStrategy. Java │ │ │ │ ├ ─ ─ DefaultListableBeanFactory. Java │ │ │ │ ├ ─ ─ DefaultSingletonBeanRegistry. Java │ │ │ │ ├ ─ ─ DisposableBeanAdapter. Java │ │ │ │ ├ ─ ─ FactoryBeanRegistrySupport. Java │ │ │ │ ├ ─ ─ InstantiationStrategy. Java │ │ │ │ └ ─ ─ SimpleInstantiationStrategy. Java │ │ │ ├ ─ ─ support │ │ │ │ └ ─ ─ XmlBeanDefinitionReader. Java │ │ │ ├ ─ ─ Aware. Java │ │ │ ├ ─ ─ BeanClassLoaderAware. Java │ │ │ ├ ─ ─ the BeanFactory. Java │ │ │ ├ ─ ─ BeanFactoryAware. Java │ │ │ ├ ─ ─ BeanNameAware. Java │ │ │ ├ ─ ─ ConfigurableListableBeanFactory. Java │ │ │ ├ ─ ─ DisposableBean. Java │ │ │ ├ ─ ─ FactoryBean. Java │ │ │ ├ ─ ─ HierarchicalBeanFactory. Java │ │ │ ├ ─ ─ InitializingBean. Java │ │ │ ├ ─ ─ ListableBeanFactory. Java │ │ │ └ ─ ─ is accomplished. The Java │ │ ├ ─ ─ BeansException. Java │ │ ├ ─ ─ Java │ ├─ ├─ Class.htm │ ├─ Class.htm │ ├── Annotation │ ├── ├─ ClassPathBeanDefinitionScanner. Java │ │ │ ├ ─ ─ ClassPathScanningCandidateComponentProvider. Java │ │ │ └ ─ ─ the Scope. The Java │ │ ├ ─ ─ event │ │ │ ├ ─ ─ AbstractApplicationEventMulticaster. Java │ │ │ ├ ─ ─ ApplicationContextEvent. Java │ │ │ ├ ─ ─ ApplicationEventMulticaster. Java │ │ │ ├ ─ ─ ContextClosedEvent. Java │ │ │ ├ ─ ─ ContextRefreshedEvent. Java │ │ │ └ ─ ─ SimpleApplicationEventMulticaster. Java │ │ ├ ─ ─ support │ │ │ ├ ─ ─ AbstractApplicationContext. Java │ │ │ ├ ─ ─ AbstractRefreshableApplicationContext. Java │ │ │ ├ ─ ─ AbstractXmlApplicationContext. Java │ │ │ ├ ─ ─ ApplicationContextAwareProcessor. Java │ │ │ └ ─ ─ ClassPathXmlApplicationContext. Java │ │ ├ ─ ─ ApplicationContext. Java │ │ ├ ─ ─ ApplicationContextAware. Java │ │ ├ ─ ─ ApplicationEvent. Java │ │ ├ ─ ─ ApplicationEventPublisher. Java │ │ ├ ─ ─ ApplicationListener. Java │ │ └ ─ ─ ConfigurableApplicationContext. Java │ ├ ─ ─ core. IO │ │ ├ ─ ─ ClassPathResource. Java │ │ ├ ─ ─ DefaultResourceLoader. Java │ │ ├ ─ ─ FileSystemResource. Java │ │ ├ ─ ─ the Resource. The Java │ │ ├ ─ ─ ResourceLoader. Java │ │ └ ─ ─ UrlResource.java │ ├── offensive │ ├── component.java │ ├─ utils │ ├─ ch.htm Cn. Bugstack. Springframework. Test ├ ─ ─ bean │ ├ ─ ─ IUserService. Java │ └ ─ ─ UserService. Java └ ─ ─ ApiTest. JavaCopy the code

Project source code: public number “bugstack wormhole stack”, reply: Spring column, obtain complete source code

The auto-load package scans the class relationships that register Bean objects and set placeholder properties during the Bean lifecycle, as shown in Figure 14-2

  • Terms of the structure of the whole class relations, actually involves the content is not much, mainly including on class XmlBeanDefinitionReader is XML parsing ClassPathBeanDefinitionScanner# doScan use.
  • The doScan method handles all classes that are annotated in the specified path, unifies the class information: name, scope, etc., and creates a BeanDefinition for the Bean object registration operation.
  • Accomplished now looks like a single piece of content, the content of the follow-up to the integration with automatic loading Bean object, namely can use placeholder configuration on annotation some attribute information in the configuration file.

2. Handle placeholder configuration

cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer

public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {

    /**
     * Default placeholder prefix: {@value} * /
    public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";

    /**
     * Default placeholder suffix: {@value} * /
    public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";

    private String location;

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // Load the properties file
        try {
            DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
            Resource resource = resourceLoader.getResource(location);
            Properties properties = new Properties();
            properties.load(resource.getInputStream());

            String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
            for (String beanName : beanDefinitionNames) {
                BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

                PropertyValues propertyValues = beanDefinition.getPropertyValues();
                for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
                    Object value = propertyValue.getValue();
                    if(! (valueinstanceof String)) continue;
                    String strVal = (String) value;
                    StringBuilder buffer = new StringBuilder(strVal);
                    int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
                    int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
                    if(startIdx ! = -1&& stopIdx ! = -1 && startIdx < stopIdx) {
                        String propKey = strVal.substring(startIdx + 2, stopIdx);
                        String propVal = properties.getProperty(propKey);
                        buffer.replace(startIdx, stopIdx + 1, propVal);
                        propertyValues.addPropertyValue(newPropertyValue(propertyValue.getName(), buffer.toString())); }}}}catch (IOException e) {
            throw new BeansException("Could not load properties", e); }}public void setLocation(String location) {
        this.location = location; }}Copy the code
  • Depending on the properties of BeanFactoryPostProcessor during the Bean lifecycle, the property information can be changed before the Bean object is instantiated. So here we implement the BeanFactoryPostProcessor interface to load the configuration file and to extract the placeholder configuration in the properties file.
  • This allows you to put the extracted configuration information into the property configuration,buffer.replace(startIdx, stopIdx + 1, propVal); propertyValues.addPropertyValue

3. Define intercept annotations

cn.bugstack.springframework.context.annotation.Scope

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {

    String value(a) default "singleton";

}
Copy the code
  • Custom annotations for configuring scopes to make it easier to get the scope of Bean objects by configuring Bean object annotations.However, the default Singleton is usually used

cn.bugstack.springframework.stereotype.Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {

    String value(a) default "";

}
Copy the code
  • Component custom annotations are familiar to you. They are used to configure a Class. There are also services and controllers, but all of them are handled in the same way, so I’ll show you just one Component here.

4. Process object scan assembly

cn.bugstack.springframework.context.annotation.ClassPathScanningCandidateComponentProvider

public class ClassPathScanningCandidateComponentProvider {

    public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = newLinkedHashSet<>(); Set<Class<? >> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);for(Class<? > clazz : classes) { candidates.add(new BeanDefinition(clazz));
        }
        returncandidates; }}Copy the code
  • The first step is to provide a path that can be configuredbasePackage=cn.bugstack.springframework.test.beanFindCandidateComponents, a tool method that resolves classes information, scans all @Component annotated Bean objects.

cn.bugstack.springframework.context.annotation.ClassPathBeanDefinitionScanner

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

    private BeanDefinitionRegistry registry;

    public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    public void doScan(String... basePackages) {
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition beanDefinition : candidates) {
                // Resolve the scope of the Bean singleton, prototype
                String beanScope = resolveBeanScope(beanDefinition);
                if(StrUtil.isNotEmpty(beanScope)) { beanDefinition.setScope(beanScope); } registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition); }}}private String resolveBeanScope(BeanDefinition beanDefinition) { Class<? > beanClass = beanDefinition.getBeanClass(); Scope scope = beanClass.getAnnotation(Scope.class);if (null! = scope)return scope.value();
        return StrUtil.EMPTY;
    }

    private String determineBeanName(BeanDefinition beanDefinition) { Class<? > beanClass = beanDefinition.getBeanClass(); Component component = beanClass.getAnnotation(Component.class); String value = component.value();if (StrUtil.isEmpty(value)) {
            value = StrUtil.lowerFirst(beanClass.getSimpleName());
        }
        returnvalue; }}Copy the code
  • ClassPathBeanDefinitionScanner is inherited from ClassPathScanningCandidateComponentProvider class specific scan packet processing, in addition to access to scan the class information in doScan later, You also need to get the scope of the Bean and the class name, which is basically an acronym if you don’t configure the class name.

5. Invoke scan in parsing XML

cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();

        // Parse the context: Component-scan tag to scan the class in the package and extract the relevant information for assembling the BeanDefinition
        Element componentScan = root.element("component-scan");
        if (null! = componentScan) { String scanPath = componentScan.attributeValue("base-package");
            if (StrUtil.isEmpty(scanPath)) {
                throw new BeansException("The value of base-package attribute can not be empty or null");
            }
            scanPackage(scanPath);
        }
       
        / /... Omit the other
            
        / / register BeanDefinition
        getRegistry().registerBeanDefinition(beanName, beanDefinition);
    }

    private void scanPackage(String scanPath) {
        String[] basePackages = StrUtil.splitToArray(scanPath, ', ');
        ClassPathBeanDefinitionScanner scanner = newClassPathBeanDefinitionScanner(getRegistry()); scanner.doScan(basePackages); }}Copy the code
  • For XmlBeanDefinitionReader, new custom configuration attributes are handled after the configuration file is loadedcomponent-scanCall scanPackage method, after parsing, actually is also in our ClassPathBeanDefinitionScanner# doScan function.
  • Please note that XmlBeanDefinitionReader has been completely replaced with dom4j for easy loading and parsing of XML.

Five, the test

1. Prepare

@Component("userService")
public class UserService implements IUserService {

    private String token;

    public String queryUserInfo(a) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Little Fuge, shenzhen 100001";
    }

    public String register(String userName) {
        try {
            Thread.sleep(new Random(1).nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Registered user:" + userName + "Success!";
    }

    @Override
    public String toString(a) {
        return "UserService#token = { " + token + "}";
    }

    public String getToken(a) {
        return token;
    }

    public void setToken(String token) {
        this.token = token; }}Copy the code
  • Add a custom annotation to the UserService class@Component("userService")And a property informationString token. This is to test the package scan and the placeholder properties, respectively.

2. Property configuration file

token=RejDlI78hu223Opo983Ds
Copy the code
  • In this example, a token attribute is configured to obtain the token as a placeholder

3. Spring.xml configuration objects

spring-property.xml


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context">

    <bean class="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:token.properties"/>
    </bean>

    <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
        <property name="token" value="${token}"/>
    </bean>

</beans>
Copy the code
  • loadingclasspath:token.propertiesSet the placeholder property value${token}

spring-scan.xml


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context">

    <context:component-scan base-package="cn.bugstack.springframework.test.bean"/>

</beans>
Copy the code
  • addcomponent-scanProperty to set the packet scan root path

4. Unit Tests (placeholders)

@Test
public void test_property(a) {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-property.xml");
    IUserService userService = applicationContext.getBean("userService", IUserService.class);
    System.out.println("Test results:" + userService);
}
Copy the code

The test results

Test result: UserService# Token = {RejDlI78hu223Opo983Ds} Process Finished with exit code0
Copy the code
  • The test results show that the token property of UserService has been set in the configuration file by placeholdertoken.propertiesAttribute value.

5. Unit testing (package scanning)

@Test
public void test_scan(a) {
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");
    IUserService userService = applicationContext.getBean("userService", IUserService.class);
    System.out.println("Test results:" + userService.queryUserInfo());
}
Copy the code

The test results

Test results: Little Fuge,100001, Shenzhen Process finished with exit code0
Copy the code
  • As you can see from this test result, you can now use annotations to complete Class registration of Bean objects.

Six, summarized

  • Through the whole content implementation can be seen that the current function is not complex, are in IOC and AOP core on the basis of the completion of the function. These complete functions also complete the Bean lifecycle, making the entire functionality easier to use.
  • As you continue to implement The features of Spring, you can also incorporate some of the ideas you normally use with Spring, such as how Spring dynamically switches data sources, and how thread pools are configured. These are not the most basic core scope, but they are also very important.
  • Maybe sometimes these classes to achieve more content for new people, can be a little bit of hands-on implementation step by step, in some slightly more difficult content to achieve, in fact, the back is not so difficult to understand.

Vii. Series recommendation

  • Graduated in 13 years, with two years from outsourcing into the Internet factory
  • Working for two or three years, do not understand the framework drawings are drawn?
  • Interview: Sharing and analysis of meituan’s side (including solutions)
  • LinkedList inserts faster than ArrayList? Are you sure?
  • Netty+JavaFx actual combat: imitation desktop version of wechat chat