Skip to content

Support @Bean method visibility [SPR-7170] #11829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue May 4, 2010 · 11 comments
Closed

Support @Bean method visibility [SPR-7170] #11829

spring-projects-issues opened this issue May 4, 2010 · 11 comments
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) status: bulk-closed An outdated, unresolved issue that's closed in bulk as part of a cleaning process

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented May 4, 2010

Eberhard Wolff opened SPR-7170 and commented

In JavaConfig beans created by protected methods with @Bean annotation had visibility inside the @Configuration class only but were not exported into the global ApplicationContext and therefore cannot be referenced from there or other @Configuration annotated classes:

@Configuration
public class MyConfiguration {
  @Bean
  public ABean globallyVisible() { ... }

  @Bean
  protected ABean visibleOnlyInThisClass() { ... }
}

This is great to structure large configuration because beans can be hidden. Bring that feature back please.


Affects: 3.0 GA, 3.0.1, 3.0.2

Issue Links:

27 votes, 22 watchers

@spring-projects-issues
Copy link
Collaborator Author

Eberhard Wolff commented

As pointed out at https://www.blogger.com/comment.g?blogID=14001982&postID=1001503393511544614 the visibility needs to be defined more precisely. i.e. would a BeanPostProcessor / BeanFactoryPostProcessor be allowed to see it?

@spring-projects-issues
Copy link
Collaborator Author

Chris Beams commented

There are a number of efforts that will require changes to the core container during the Spring 3.1 timeline, and it's conceivable that we could do something like this. However, doing so would add significant complexity, and I'm not yet sure it's worth it. This issue has a few votes already; I'd love to hear some specific use cases for this issue. How would it help you? How does the lack of such a feature visibility hurt you presently?

I'm scheduling this for 3.1, just so it remains on the radar. In the meantime, let's hear what folks have to say.

@spring-projects-issues
Copy link
Collaborator Author

Chris Beams commented

Assigning to 3.1 M2 for general tracking purposes.

@spring-projects-issues
Copy link
Collaborator Author

Oliver Drotbohm commented

Actually the use case is quite obvious IMHO, but might be I am exposed to that kind of issues more that others. Enabling good application architecture you want to split up your app into subsystems that expose functionality (provided interface) expose what they need (required interface) and hide internals (the classical component notion).

Currently you can only achieve this by using OSGi with e.g. Spring DM. There you expose only certain Spring beans and turn required OSGi services into Spring beans on the other side. Now let's imagine OSGi is overkill, we want to stick to a plain JavaSE, JavaEE environment, don't need dynamics, just want to enforce visibility at wiring time. With the visibility feature you could simply have something like this:

@Configuration
public class MySubsystemConfig {

  // Required dependency
  @Autowired 
  DataSource dataSource;

  @Bean 
  public MyService myService() {
    return new MyServiceImpl(myRepository());
  }

  @Bean 
  private MyRepository myRepository() {
    return new MyRepositoryImpl(myJdbcTemplate());
  }

  @Bean 
  private JdbcOperations myJdbcTemplate() {
    return new MyJdbcTemplate(dataSource);
  }
}

This way you create two beans that are managed by Spring, but cannot be referenced from other subsystems ore more strictly speaking from other configurations. I'd also argue they should not be candidates for autowiring. Only MyService would be accessible by other configurations and thus allow a more coarse grained view onto that subsystem. This especially makes it more easy for client subsystems using this one, as it's pretty much clear how to approach it. Furthermore it would be impossible to use MyRepository e.g. which could then accidentally use the repository to bypass functionality implemented in the service.

As simple classic example assume we have some UserManagement based on a UserRepository. The service adds password encryption on user creation and stuff. This subsystem was implemented by one team. Another team implemented some kind of legacy data importer that had to import users from a legacy database. It accidentally used the repository directly and the error popped up quite lately as application users claimed they could not log in.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Mar 12, 2011

Chris Beams commented

Marking this issue as duplicated by #7548, which requests bean visibility control from an XML perspective. Any visibility solution implemented on the Java (@Bean) side should apply to XML as well (i.e.: an appropriate attribute in XML should be added or at least considered for addition).

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Mar 12, 2011

Chris Beams commented

Slating this for review during the Spring 3.2 timeline. As mentioned before, this is a pretty significant chunk of work and potential complexity. I'd like to see additional votes and anecdotes regarding real-world need, and waiting for 3.2 gives that some time.

3.2 is also when we'll revisit the use of CGLIB throughout the framework, including its use in supporting @Configuration classes (#10325). By moving to a different enhancement approach, we may be able to avoid some of the current constraints like being unable to declare private @Bean methods, which is the obviously intuitive way to implement bean visibility (The approach that JavaConfig took of protected / default visibility is an awkward alternative, and could/should have slightly different semantics than private anyway).

We'll also look at other related issues like @Bean definition finality using the final modifier (#12637), so this will be part of an overall theme to provide finer-grained control over bean definition modularity.

@spring-projects-issues
Copy link
Collaborator Author

Eberhard Wolff commented

I still think this is a valuable improvement. Recently the OSGi community even said that the service model is the most important part of OSGi. This would give a very similar approach I guess.

Having said this you could also solve the issue by limiting the visibility of the classes / interfaces. If the configuration is in the same package as the implementation you can use default visibility for the non-public beans - they could not be accessed outside the package then.

@spring-projects-issues
Copy link
Collaborator Author

Andreas Benneke commented

Here is another example based on our real world problem while migrating from XML to Java @Configuration:

Our DataSource configuration in XML looked something like this:

<bean id="myDS" class="LoggingDataSource">
   <constructor-arg>
      <bean class="MultiTenancyDataSource" init-method="start" destroy-method="stop">
         <property name="dataSource">
            <jee:jndi-lookup jndi-name="jdbc/myDS"
               resource-ref="true" />
         </property>
         <property name="company" value="myCompany" />
      </bean>
   </constructor-arg>
</bean>

Please note that

  • there is effectively only one bean exposed to the public
  • the inner beans are still managed by Spring (the MultiTenancyDataSource itself does require this due to the init/destroy methods as well as some internal logic which is based on additional @Autowired properties)

Translating this configuration to Java we ended up with something similar to this one:

   @Bean
   public JndiObjectFactoryBean internalJndiDataSource() {
      final JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
      factory.setExpectedType(DataSource.class);
      factory.setJndiName("jdbc/myDS");
      factory.setResourceRef(true);
      return factory;
   }

   @Bean(initMethod = "start", destroyMethod = "stop")
   public DataSource internalMultiTenancyDataSource(
            @Value("#{internalJndiDataSource}") final DataSource internalJndiDataSource) {
      final MultiTenancyDataSource ds = new MultiTenancyDataSource();
      ds.setDataSource(internalJndiDataSource);
      ds.setCompany("myCompany");
      return ds;
   }

   @Bean
   public DataSource myDS(
            @Value("#{internalMultiTenancyDataSource}") final DataSource internalMultiTenancyDataSource) {
      return new LoggingDataSource(internalMultiTenancyDataSource);
   }

To have all the Spring magic working on the FactoryBean as well as the MultiTenancyDataSource they are currently both required to be configured in their own @Bean methods. So we now have +three+ datasources publically available, which

  • is not necessary, because two of them are for internal use only
  • makes a simple @Autowired (without additional Qualifier) on the DataSource impossible

Both points are no big deal, but they require additional explanation and expose complexity we would prefer to hide.

We would love to see any possibility to create the "inner"/"nested" beans in Java @Configurations as well or a way to restrict the visibility of a bean. Thank you!

@spring-projects-issues spring-projects-issues added status: waiting-for-triage An issue we've not yet triaged or decided on type: enhancement A general enhancement in: core Issues in core modules (aop, beans, core, context, expression) has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import labels Jan 11, 2019
@spring-projects-issues spring-projects-issues removed the type: enhancement A general enhancement label Jan 11, 2019
@rstoyanchev rstoyanchev added status: bulk-closed An outdated, unresolved issue that's closed in bulk as part of a cleaning process and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 11, 2019
@spring-projects-issues
Copy link
Collaborator Author

Bulk closing outdated, unresolved issues. Please, reopen if still relevant.

@nightswimmings
Copy link

nightswimmings commented Jan 31, 2020

A testcase where this is absolutely desirable is when creating custom starters. You want Spring autowiring magic within the scope of the starter, but not exposing all autconfiguration's beans to the context of the end client.
For instance, imagine an starter using a datasource, and having to define Emf, Datasource, Objectmappers in a way that they can be referenced internally but don't alter the context of the application importing the custom starter

@tilm4nn
Copy link

tilm4nn commented Jan 21, 2024

I currently encountered another situation where this would be of great value: We are using spring-ws and would like to create multiple MessageDispatcherServlets with different URLs for different services. And each of these should be configured with their own EndpointMappings, EndpointInterceptors,... Currently each servlet sees (and then uses) all Mappings/Interceptors of the whole application. Thus we either end up creating and wiring all the spring-ws components by hand :( Or we start subclassing and customizing all Mappings/Interceptors adding code to check the Request-URL to decide if it should be invoked or not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) status: bulk-closed An outdated, unresolved issue that's closed in bulk as part of a cleaning process
Projects
None yet
Development

No branches or pull requests

4 participants