Giter Site home page Giter Site logo

Comments (5)

ulisesbocchio avatar ulisesbocchio commented on May 16, 2024

Hi buddy thanks for your concern but I don't think it makes a lot difference in this case wether you do this:
StringEncryptor encryptor = registry.getBean(beanName, StringEncryptor.class);
or this:
StringEncryptor encryptor = registry.getBean(StringEncryptor.class);

Regardless of the implementation of getBean(name, type) and getBean(type)the bean needs to be already instantiated for you to get a hold of it. So at that point the bean needs have been initialized and instantiated already. The post processor pulling the bean guarantees that it's done at the end of wiring the entire application context, so it doesn't mess up with bean initialization order. Basically, if the bean had a dependency on something, it'd had been already instantiated at that point.

Looking the actual implementation of both methods in DefaultListableBeanFactory it's evident that they both rely on the same method protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly) from AbstractBeanFactory. The actual difference for both is that on getBean(Class<T>) the implementation is actually looking for candidates names by type first, then the name is used to retrieve the actual bean. The only side effect seems to be for FactoryBean beans, that may get eagerly initialized. But at the point the post processor is invoked the application context is fully wired.

I do think though your recommendation is valuable in the case of having 2 different different StringEncryptor beans in your application. In that case depending on how you set them up you may end up getting a NoUniqueBeanDefinitionException in the post processor as Spring wouldn't be able to decide which one to return.

Best,
Uli

from jasypt-spring-boot.

passionlim avatar passionlim commented on May 16, 2024

Thank you for a long reply.
I think that the situation is not sufficiently described.

In my case, I try to use mybatis, mybatis-spring for a persistence layer and jasypt-spring-boot for db property encryption. an expected senarios are below.

  1. jasypt-spring-boot decrypt database connecion info in PropertySources in Spring Envrionment.
  2. The decrypted db connection infos are used to create DataSource.
  3. The Datasource can be injected into Mybatis's SessionFactory.
  4. Mybatis's SessionFactory can be autowired or injected to MapperFactoryBean which is FactoryBean.
  5. MapperFactoryBean's BeanDefinition's are created in ImportBeanDefinitionRegistrar in Mybatis-Spring, but the BeanDefinition's should be instantiated after 1)~4).

In step 1), jasypt-spring-boot use getBeanByType() to get StringEncryptor. This method result in scanning all types of BeanDefinitions in ApplicationContext to find a matched bean instance. Usual beans are not in a problem for type chekcing, but a FactoryBean which has danamic resultType in getObjectType Method. MapperFactoryBean in Mybatis-Spring is that case.

The BeanDefinitions are created in MyBatis-Spring's BeanFactoryPostProcessor. The types of proper ObjectTypes are injected by property values. But, Spring's AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck does not use that injected property. It just instantiate by default constructor, and then it tries to get getObjectType which return null ( proper type is not yet injected ). If null is returned, spring try to institiate the FactoryBean to check type. so the order of bean creation depedencies are different what I expected.

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(String, RootBeanDefinition)

        FactoryBean<?> fb = (mbd.isSingleton() ?
                getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
                getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));

        if (fb != null) {
            // Try to obtain the FactoryBean's object type from this early stage of the instance.
            objectType.value = getTypeForFactoryBean(fb);
            // !!!!!!!! objectType.value is null !!!!!!!!!!!!
            if (objectType.value != null) {
                return objectType.value;
            }
        }

        // !!!!!!!! this cause this issue !!!!!!!!!!!!
        // No type found - fall back to full creation of the FactoryBean instance.
        return super.getTypeForFactoryBean(beanName, mbd);
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(String, RootBeanDefinition)

            Object instance = null;
            try {
                // Mark this bean as currently in creation, even if just partially.
                beforeSingletonCreation(beanName);
                // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
                instance = resolveBeforeInstantiation(beanName, mbd);
                if (instance == null) {
                    // !!!!!!!!!!!!!! just create without any arguments !!!!!!!!!!!!
                    bw = createBeanInstance(beanName, mbd, null);
                    instance = bw.getWrappedInstance();
                }
            }
            finally {
                // Finished partial creation of this bean.
                afterSingletonCreation(beanName);
            }
            FactoryBean<?> fb = getFactoryBean(beanName, instance);
            if (bw != null) {
                this.factoryBeanInstanceCache.put(beanName, bw);
            }

            ( org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getSingletonFactoryBeanForTypeCheck(String, RootBeanDefinition) ) 

Source References

from jasypt-spring-boot.

ulisesbocchio avatar ulisesbocchio commented on May 16, 2024

Thanks for the explanation. It makes sense. As I said in my previous comment, the only side effect seems to be for FactoryBean beans which unfortunately is your case, so I will fix this.
In terms of the fix, I have a few options to consider:

  • By name as you suggested
  • ListableFactoryBean provides methods that allow to specify whether to initialize FactoryBean beans when searching for a bean of a specific type. The side effect for Jasypt Spring Boot wold be that the StringEncryptor could not be provided as FactoryBean (Or the FactoryBean would have to be initialized manually, i.e. calling getObject() in a bean definition)
  • Allow Specification of the execution order of the EncryptablePropertySourcesPostProcessor. This may not work since you need the properties to be decrypted before the iBatis FactoryBean gets created.
  • Delay initialization of StringEncryptor until right before first encrypted property is used. I'm already doing this in the scenario when StringEncryptor is not provided.

from jasypt-spring-boot.

ulisesbocchio avatar ulisesbocchio commented on May 16, 2024

I followed your advice and am retrieving encryptor bean by name as of version 1.6. I just released it to central so it might take some time until it's available. Please review the documentation if you need to specify a custom encryptor as you now have to specify a bean name.

from jasypt-spring-boot.

passionlim avatar passionlim commented on May 16, 2024

great work!! thanks

from jasypt-spring-boot.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.