Blog: bugstack.cn

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

Old driver, how did you move the bricks so fast?

Is the mood? Is a skill? Is the back door? In a word, that old driver’s code can always quickly complete the product every new demand, just like they are a family! And you are not the same, not only the product manager and operation, testing sister, have to buy you food, beg you to quickly fix the Bug, otherwise it is too late to online.

So why is it that other people’s code can always extend new features quickly, while yours can never be refactored, only rewritten, with minor changes, major changes, and no requirements? No need for their own running also can collapse, the middle of the night was pulled up: “you this how slow database inquiry, the other people’s business are dragging crotch!”

Some people say that 30 years old people, and just graduated to do the same live, is no progress! That’s bullshit, also is the same, but may not be all that is made to the results of the same, someone can use ifelse put product function, also some people can put the demand dissected into each function module, defining interfaces and abstract classes, implementation, and inheritance, using design patterns when can build up a set of new demand rapid implementation, problems can accurately locate the code logic. It’s like someone asking, “There are ten birds in a tree, one shot at a time, how many more?” What do you think of? Shooting a big, have you got it tied to a tree, the bird cage, are there any birds of disability, birds were killed it, bird of the eyes so that it and calculate the belly pregnant the edge of a bird, the bird you breaking the law, the tree is there other birds, and so on, these are a professional technical people honed in an industry experience, not 1 day 2 days look at a few books, brush a few brainwashed articles can absorb.

Second, the target

Is the Bean object we hand over to Spring to manage necessarily the same Bean we create with the class? Is it possible to create a Bean that is always a singleton, not a prototype pattern?

In the framework of Spring collection, the MyBatis framework we use, its core function is to meet the requirement that users can complete CRUD operations on the database through XML or annotation configuration without implementing Dao interface class. Therefore, in the implementation of such ORM framework, How to hand over a database operation Bean object to Spring to manage.

Because we can know when using Spring, MyBatis framework, there is no manual to create any operation database Bean object, there is only an interface definition, and this interface definition can be injected into other attributes that need to use Dao. At the heart of this process is the need to register complex, dynamically proxying objects into the Spring container. To meet the need for such an extension component development, we need to add this capability to the existing handwritten Spring framework.

Three,

The feature point about providing a Bean object that allows users to define complex Bean objects is great and meaningful, because by doing so Spring’s eco-seed incubator provides a standard on which any framework can access its services.

However, the design of such functional logic is not complicated, because the whole Spring framework has provided a succession of extended capabilities during the development process, you just need to provide a succession of processing interface calls and corresponding functional logic implementation in the appropriate place. The goal here is to provide a way to get objects twice from FactoryBean’s getObject method, so that all object classes that implement this interface can extend their own object functionality. MyBatis implements a MapperFactoryBean class and provides SqlSession in getObject method to perform CRUD method operation. The overall design structure is shown as follows:

  • The whole implementation process consists of two parts, one dealing with singletons or prototype objects, and the other dealing with obtaining specific call objects during the creation of FactoryBean type objectsgetObjectOperation.
  • SCOPE_SINGLETON,SCOPE_PROTOTYPE, the main difference between the creation and acquisition of object types isAbstractAutowireCapableBeanFactory#createBeanWhether to put the object into memory after it is created. If not, it will be recreated each time it is fetched.
  • After createBean creates the object, fills the properties, loads the dependencies, presets and postsets, and initializes the FactoryBean, it is time to determine whether the entire object is a FactoryBean objectgetObjectThe object. A singleton type judgment is added throughout the getBean processfactory.isSingleton()Determines whether to use memory to store object information.

Four, implementation,

1. Engineering structure

Small - spring - step - 09 └ ─ ─ the SRC ├ ─ ─ the main │ └ ─ ─ Java │ └ ─ ─ cn. Bugstack. Springframework │ ├ ─ ─ beans │ │ ├ ─ ─ factory │ │ │ ├ ─ ─ The config │ │ │ │ ├ ─ ─ AutowireCapableBeanFactory. Java │ │ │ │ ├ ─ ─ BeanDefinition. Java │ │ │ │ ├ ─ ─ Spring BeanFactoryPostProcessor. Java │ │ │ │ ├ ─ ─ BeanPostProcessor. Java │ │ │ │ ├ ─ ─ BeanReference. Java │ │ │ │ ├ ─ ─ ConfigurableBeanFactory. 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 │ │ ├ ─ ─ BeansException. Java │ │ ├ ─ ─ PropertyValue. Java │ │ └ ─ ─ PropertyValues. Java │ ├ ─ ─ the context │ │ ├ ─ ─ support │ │ │ ├ ─ ─ AbstractApplicationContext. Java │ │ │ ├ ─ ─ AbstractRefreshableApplicationContext. Java │ │ │ ├ ─ ─ AbstractXmlApplicationContext. Java │ │ │ ├ ─ ─ ApplicationContextAwareProcessor. Java │ │ │ └ ─ ─ ClassPathXmlApplicationContext. Java │ │ ├ ─ ─ ApplicationContext. Java │ │ ├ ─ ─ ApplicationContextAware. Java │ │ └ ─ ─ ConfigurableApplicationContext. Java │ ├ ─ ─ core. IO │ │ ├ ─ ─ ClassPathResource. Java │ │ ├ ─ ─ DefaultResourceLoader. Java │ │ ├ ─ ─ FileSystemResource. Java │ │ ├ ─ ─ the Resource. The Java │ │ ├ ─ ─ ResourceLoader. Java │ │ └ ─ ─ UrlResource. Java │ └ ─ ─ utils │ └ ─ ─ ClassUtils. Java └ ─ ─ the test └ ─ ─ Java └ ─ ─ Cn. Bugstack. Springframework. Test ├ ─ ─ bean │ ├ ─ ─ UserDao. Java │ └ ─ ─ UserService. Java └ ─ ─ ApiTest. JavaCopy the code

Project source: public account “bugStack wormhole stack”, reply: Spring column, get the full source code

Spring singletons, stereotypes, and the FactoryBean functionality implement class relationships, as shown in Figure 10-2

  • The entire class diagram above shows whether the instantiation of the added Bean is a singleton or a prototype pattern and the implementation of the FactoryBean.
  • In fact, the whole implementation process is not complicated, but in the existing AbstractAutowireCapableBeanFactory class and inheritance of abstract class AbstractBeanFactory extensions.
  • But this time we put AbstractBeanFactory inherited DefaultSingletonBeanRegistry class, with a layer of FactoryBeanRegistrySupport, This class in the Spring framework handles the support operations for FactoryBean registration.

2. Scope definition and XML parsing of beans

cn.bugstack.springframework.beans.factory.config.BeanDefinition

public class BeanDefinition {

    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

    private Class beanClass;

    private PropertyValues propertyValues;

    private String initMethodName;

    private String destroyMethodName;

    private String scope = SCOPE_SINGLETON;

    private boolean singleton = true;

    private boolean prototype = false;
    
    / /... get/set
}
Copy the code
  • Singleton and Prototype are two new attributes added to the BeanDefinition class to populate the attributes with the scope of the Bean object parsed from spring.xml.

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

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
      
        for (int i = 0; i < childNodes.getLength(); i++) {
            // Determine the element
            if(! (childNodes.item(i)instanceof Element)) continue;
            // Determine the object
            if (!"bean".equals(childNodes.item(i).getNodeName())) continue;

            // Parse the tag
            Element bean = (Element) childNodes.item(i);
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");
            String initMethod = bean.getAttribute("init-method");
            String destroyMethodName = bean.getAttribute("destroy-method");
            String beanScope = bean.getAttribute("scope");

            // Get Class to get the name of the ClassClass<? > clazz = Class.forName(className);// Priority id > name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            / / define the Bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethod);
            beanDefinition.setDestroyMethodName(destroyMethodName);

            if (StrUtil.isNotEmpty(beanScope)) {
                beanDefinition.setScope(beanScope);
            }
            
            // ...
            
            / / register BeanDefinitiongetRegistry().registerBeanDefinition(beanName, beanDefinition); }}}Copy the code
  • In the parsed XML processing class XmlBeanDefinitionReader, new parsing of scope in the Bean object configuration is added, and this attribute information is populated into the Bean definition.beanDefinition.setScope(beanScope)

3. Determine singleton and stereotype patterns when creating and modifying objects

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // Populate the Bean with properties
            applyPropertyValues(beanName, bean, beanDefinition);
            // Executes the Bean initialization methods and the BeanPostProcessor pre - and post-processing methods
            bean = initializeBean(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        // Register a Bean object that implements the DisposableBean interface
        registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

        // check SCOPE_SINGLETON and SCOPE_PROTOTYPE
        if (beanDefinition.isSingleton()) {
            addSingleton(beanName, bean);
        }
        return bean;
    }

    protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
        // Beans of non-singleton type do not perform destruction methods
        if(! beanDefinition.isSingleton())return;

        if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
            registerDisposableBean(beanName, newDisposableBeanAdapter(bean, beanName, beanDefinition)); }}/ /... Other features
}
Copy the code
  • The Singleton pattern differs from the prototype pattern in that it is not stored in memory, the object is recreated each time it is fetched, and non-Singleton beans do not need to perform destruction.
  • So there are two changes to the code here: one is to determine whether to add to addSingleton(beanName, bean) in createBean; , and another is registered registerDisposableBeanIfNecessary destroyed the judgmentif (! beanDefinition.isSingleton()) return;.

4. Define the FactoryBean interface

cn.bugstack.springframework.beans.factory.FactoryBean

public interface FactoryBean<T> {

    T getObject(a) throws Exception; Class<? > getObjectType();boolean isSingleton(a);

}
Copy the code
  • A FactoryBean needs to provide three methods to get the object, the type of the object, and whether it is a singleton. If it is a singleton, it will still be put into memory.

5. Implement a FactoryBean registry service

cn.bugstack.springframework.beans.factory.support.FactoryBeanRegistrySupport

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {

    /** * Cache of singleton objects created by FactoryBeans: FactoryBean name --> object */
    private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<String, Object>();

    protected Object getCachedObjectForFactoryBean(String beanName) {
        Object object = this.factoryBeanObjectCache.get(beanName);
        return(object ! = NULL_OBJECT ? object :null);
    }

    protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName) {
        if (factory.isSingleton()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                object = doGetObjectFromFactoryBean(factory, beanName);
                this.factoryBeanObjectCache.put(beanName, (object ! =null ? object : NULL_OBJECT));
            }
            return(object ! = NULL_OBJECT ? object :null);
        } else {
            returndoGetObjectFromFactoryBean(factory, beanName); }}private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName){
        try {
            return factory.getObject();
        } catch (Exception e) {
            throw new BeansException("FactoryBean threw exception on object[" + beanName + "] creation", e); }}}Copy the code
  • Main treatment is about FactoryBean FactoryBeanRegistrySupport class registration operation of this kind of object, is in a separate class, is to do only responsible for their respective under different field module needs to be done function, avoid because extension cause inflation to difficult to maintain.
  • The factoryBeanObjectCache operation is also defined to store singleton objects to avoid repeated creation.In daily use, are also basically created singleton objects
  • DoGetObjectFromFactoryBean is concrete to obtain FactoryBean# getObject () method, because both cache processing and object, so the extra provides a logical getObjectFromFactoryBean packaging, This part of the operation is not very similar to your daily business logic development.Fetch data from Redis or, if empty, from the database and write to Redis

6. Extend AbstractBeanFactory creation object logic

cn.bugstack.springframework.beans.factory.support.AbstractBeanFactory

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

    protected <T> T doGetBean(final String name, final Object[] args) {
        Object sharedInstance = getSingleton(name);
        if(sharedInstance ! =null) {
            // If it is a FactoryBean, call FactoryBean#getObject
            return (T) getObjectForBeanInstance(sharedInstance, name);
        }

        BeanDefinition beanDefinition = getBeanDefinition(name);
        Object bean = createBean(name, beanDefinition, args);
        return (T) getObjectForBeanInstance(bean, name);
    }  
   
    private Object getObjectForBeanInstance(Object beanInstance, String beanName) {
        if(! (beanInstanceinstanceof FactoryBean)) {
            return beanInstance;
        }

        Object object = getCachedObjectForFactoryBean(beanName);

        if (object == null) { FactoryBean<? > factoryBean = (FactoryBean<? >) beanInstance; object = getObjectFromFactoryBean(factoryBean, beanName); }return object;
    }
        
    // ...
}
Copy the code
  • First the inherited DefaultSingletonBeanRegistry AbstractBeanFactory before here, modified to FactoryBeanRegistrySupport inheritance. Because you need to extend the ability to create FactoryBean objects, this is like cutting off a chain service to handle additional services and linking the chain back together.
  • The new feature here is the addition of a call to the doGetBean method(T) getObjectForBeanInstance(sharedInstance, name)The operation to get a FactoryBean.
  • We do the specific Instanceof judgment in the getObjectForBeanInstance method, and we also get objects from the FactoryBean’s cache, If there is no call the FactoryBeanRegistrySupport# getObjectFromFactoryBean, perform specific operations.

Five, the test

1. Prepare

cn.bugstack.springframework.test.bean.IUserDao

public interface IUserDao {

    String queryUserName(String uId);

}
Copy the code
  • In this section we drop the UserDao and define an IUserDao interface in order to delegate custom objects through a FactoryBean.

cn.bugstack.springframework.test.bean.UserService

public class UserService {

    private String uId;
    private String company;
    private String location;
    private IUserDao userDao;

    public String queryUserInfo(a) {
        return userDao.queryUserName(uId) + "," + company + "," + location;
    }

    / /... get/set
}
Copy the code
  • In UserService, a new property of the original UserDao is changed to IUserDao. We will inject a proxy object into this property later.

2. Define the FactoryBean object

cn.bugstack.springframework.test.bean.ProxyBeanFactory

public class ProxyBeanFactory implements FactoryBean<IUserDao> {

    @Override
    public IUserDao getObject(a) throws Exception {
        InvocationHandler handler = (proxy, method, args) -> {

            Map<String, String> hashMap = new HashMap<>();
            hashMap.put("10001".Little Fuge.);
            hashMap.put("10002"."Eight glasses of water");
            hashMap.put("10003"."Will");
            
            return "You're being represented." + method.getName() + ":" + hashMap.get(args[0].toString());
        };
        return (IUserDao) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IUserDao.class}, handler);
    }

    @Override
    publicClass<? > getObjectType() {return IUserDao.class;
    }

    @Override
    public boolean isSingleton(a) {
        return true; }}Copy the code
  • This is the name of the ProxyBeanFactory class that implements the interface FactoryBean. It emulates the original functionality of UserDao, similar to the proxy operation in MyBatis framework.
  • GetObject () provides a proxy object for InvocationHandler that performs its functions when a method is called.

3. Configuration file


      
<beans>

    <bean id="userService" class="cn.bugstack.springframework.test.bean.UserService" scope="prototype">
        <property name="uId" value="10001"/>
        <property name="company" value="Tencent"/>
        <property name="location" value="Shenzhen"/>
        <property name="userDao" ref="proxyUserDao"/>
    </bean>

    <bean id="proxyUserDao" class="cn.bugstack.springframework.test.bean.ProxyBeanFactory"/>

</beans>
Copy the code
  • In the configuration file, we inject the proxy object proxyUserDao into the userDao of the userService.Compared to the previous section, the UserDao implementation class is removed and replaced with a proxy class

4. Unit Testing (singleton & Prototype)

@Test
public void test_prototype(a) {
    1. Initialize the BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    applicationContext.registerShutdownHook();   

    // 2. Get the Bean object to call the method
    UserService userService01 = applicationContext.getBean("userService", UserService.class);
    UserService userService02 = applicationContext.getBean("userService", UserService.class);
    
    // set scope="prototype/singleton"
    System.out.println(userService01);
    System.out.println(userService02);    

    // 4. Print a hexadecimal hash
    System.out.println(userService01 + "Hex hash:" + Integer.toHexString(userService01.hashCode()));
    System.out.println(ClassLayout.parseInstance(userService01).toPrintable());

}
Copy the code
  • In the spring. XML configuration file, you set scope=”prototype” so that each object fetched should be a new one.
  • This determines whether the object is a hash of a class object that you will see printed, so we print out the hexadecimal hash.

The test results

cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@1b0375b3 cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984@2f7c7260 Cn. Bugstack. Springframework. Test. Beans. UserService $$EnhancerByCGLIB $$4 cabb984 @ 1 b0375b3 hexadecimal hash: 1b0375b3 cn.bugstack.springframework.test.bean.UserService$$EnhancerByCGLIB$$4cabb984 object internals: OFFSET SIZE TYPE DESCRIPTION VALUE0     4                                                  (object header)                                           01 b3 75 03 (00000001 10110011 01110101 00000011) (58045185)
      4     4                                                  (object header)                                           1b 00 00 00 (00011011 00000000 00000000 00000000) (27)
      8     4                                                  (object header)                                           9f e1 01 f8 (10011111 11100001 00000001 11111000) (-134094433)
     12     4                                 java.lang.String UserService.uId                                           (object)
     16     4                                 java.lang.String UserService.company                                       (object)
     20     4                                 java.lang.String UserService.location                                      (object)
     24     4   cn.bugstack.springframework.test.bean.IUserDao UserService.userDao                                       (object)
     28     1                                          boolean UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$BOUND        true
     29     3                                                  (alignment/padding gap)                                  
     32     4                          net.sf.cglib.proxy.NoOp UserService$$EnhancerByCGLIB$$4cabb984.CGLIB$CALLBACK_0   (object)
     36     4                                                  (loss due to the next object alignment)
Instance size: 40 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total


Process finished with exit code 0
Copy the code

  • The little string that follows the object is the hexadecimal hash value, which also has a corresponding value in the result of the object’s header hash value. It’s just the other way around.
  • As you can also see, the ending hexadecimal hash values of cabb984@1b0375b3 and cabb984@2f7c7260 are not the same, so our prototype pattern works.

5. Unit Tests (proxy objects)

@Test
public void test_factory_bean(a) {
    1. Initialize the BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
    applicationContext.registerShutdownHook(); 

    // 2. Invoke the proxy method
    UserService userService = applicationContext.getBean("userService", UserService.class);
    System.out.println("Test Results:" + userService.queryUserInfo());
}
Copy the code
  • There isn’t much difference in the invocation of FactoryBean, because all the differences are already configured by spring.xml. Of course you can call the spring.xml configured object directlycn.bugstack.springframework.test.bean.ProxyBeanFactory

The test results

QueryUserName = queryUserName = queryUserName = queryUserName = queryUserName0
Copy the code
  • From our test results, our proxy class ProxyBeanFactory has replaced the UserDao functionality perfectly.
  • It doesn’t look complicated, even a little simple. But just a little bit of core design solves all the interlinking problems that Spring needs to have with other frameworks.If you are interested in this kind of content, you can also read Little Fu GeMiddleware Design and Development

Six, summarized

  • In the whole development process of Spring framework, each function interface class expanded like expansion in the early stage, but it is not so difficult to improve the function in the later stage. On the contrary, after in-depth understanding, it will feel that the function supplement is relatively simple. You just need to extend the corresponding service implementation within the scope of the encountered domain.
  • When you read about the implementation of FactoryBeans and the use of the test process before you need to develop components using FactoryBeans, it will be clear how factoryBeans create their complex Bean objects and when they are initialized and invoked. Problems can also be quickly troubleshooting, positioning and solution.
  • If you feel that classes, interfaces, implementations, and inheritances are too complex to navigate, your brain will not be able to grasp them for a while. The best way to do this is to start drawing class diagrams, sort out the structure of the implementation, and see what each class is doing.Look can only be known, to learn!

Seven, series recommendation

  • How do I stuff a Bean into a Spring container?
  • “Java Face book Manual” PDF, 417 pages 115,000 words, complete & release!
  • Math, how close is it to a programmer?
  • Work 3 years, see what data can monthly salary 30K?
  • PPT drawing like this, the duty defense can lead?