Skip to content

Change Private Methods and Private fields to Protected in Generated Java Service Classes #33715

Description

@vw98075
Overview of the feature request

Change the access modifier of private methods in JHipster-generated Java service classes — particularly UserService and entity service classes — from private to protected.

This is a purely structural change with zero behavioral impact. No logic changes, no API changes, no breaking changes of any kind. It is a single keyword change per method that unlocks clean, upgrade-safe extension of generated service classes.

Motivation for or Use Case

Change the access modifier of private methods and private fields in JHipster-generated Java service classes — particularly UserService and entity service classes — from private to protected.
A typical generated service class has both categories of private members that block clean extension:
java// Both of these block subclass access today

@Service
public class FooService {

    private final FooRepository fooRepository;   // ← field, must be protected
    private final FooMapper fooMapper;           // ← field, must be protected

    private FooDTO someHelperMethod(Foo foo) {   // ← method, must be protected
        ...
    }
}

This is a purely structural change with zero behavioral impact. No logic changes, no API changes, no breaking changes of any kind. It is a single keyword change per field and method that unlocks clean, upgrade-safe extension of generated service classes.

Motivation for or Use Case

  1. JHipster's own documentation contradicts the current generated code
    JHipster's official documentation at /tips/035_tip_combine_generation_and_custom_code explicitly recommends and demonstrates the side-by-side extension pattern — ExtendedFooService extending the generated FooService — as the canonical approach for customizing generated code without modifying it. This is the correct strategy for surviving JHipster upgrades cleanly.

However, both private methods and private fields in the generated service classes directly undermine this recommendation. When ExtendedFooService extends FooService, private members of the parent are completely invisible to the subclass, forcing developers into one of these unacceptable outcomes:

Modifying the generated file to change private to protected on both fields and methods — which defeats the entire purpose of the upgrade-safe extension pattern since every change must be manually reapplied after every regeneration
Using Java Reflection to access private fields and invoke private methods — which is fragile, bypasses the Java module system, produces runtime errors instead of compile-time errors, and is universally considered a serious code smell
Re-injecting the same dependencies already held by the parent class — for example, declaring a second fooRepository field in the subclass pointing to the same bean — which creates redundant injection, obscures intent, and produces confusing bean graphs

Duplicating private method logic in the subclass — which creates two diverging implementations of the same logic and introduces a silent maintenance burden on every JHipster upgrade

The project's documentation and its generated code are in direct contradiction. protected on both fields and methods resolves this contradiction cleanly.
2. The Open/Closed Principle
Generated code that is explicitly designed and documented for customization must follow the Open/Closed Principle — open for extension, closed for modification. Private fields and private methods in this context invert that principle entirely: they force modification of generated files precisely because extension is blocked. Changing to protected restores OCP compliance, which is the foundational design principle that the side-by-side pattern depends on.
3. Spring Framework's own extensibility idiom
JHipster is built on Spring, and Spring itself applies protected fields and methods consistently and deliberately throughout its own extensible base classes — WebMvcConfigurationSupport, AbstractSecurityWebApplicationInitializer, AbstractAuthenticationProcessingFilter, and many others. This is the well-established Template Method Pattern from GoF, applied precisely for the use case of allowing subclasses to extend behavior without reimplementing it. JHipster generated service classes are consumed in the same way — as base classes meant to be extended — yet they ignore the extensibility idiom that Spring itself consistently models for both fields and methods.
4. The encapsulation argument does not apply to generated code
The standard justification for private over protected is encapsulation — hiding implementation internals from subclasses. This argument is fundamentally weakened in the context of generated code for three reasons. First, the source is fully visible to the developer — there are no hidden internals. Second, protected still fully encapsulates from all external classes; it only opens members to the class hierarchy, which is the intended relationship. Third, the developer who subclasses UserService already owns and can see every line of the parent class, including every field declaration and every private method. The encapsulation concern, entirely valid in a library context, does not translate to a code generation context where the consumer owns the source.
5. The change carries zero risk
Changing private to protected on both fields and methods is:

Binary compatible — no existing compiled code breaks
Source compatible — no existing code requires any rewriting
Behaviorally neutral — runtime behavior is completely unchanged
Non-breaking for all current JHipster users without exception

This is the lowest-risk category of change in the Java language specification. It cannot break any codebase that currently compiles and runs correctly.

Related issues or PR
  • Checking this box is mandatory (this is just to show you read everything)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions