Even if you understand the Bean lifecycle and @PostConstruct, have you ever come across interfaces named “Aware” — like BeanNameAware or ApplicationContextAware — in a library’s source code and wondered what they’re for? This time, we’ll sort out what each of the four Aware interfaces injects, and when it’s okay to use them versus when you should replace them with constructor injection.

Aware is a “setter that receives Spring’s internal information”

Every Aware interface has exactly one method, setXxx(Xxx xxx). When you implement it, the Spring container automatically calls that setter and passes you the information. It’s called after dependency injection, but before @PostConstruct.

BeanNameAware - learn your own bean name

With setBeanName(String name), you receive your own bean name within the container. How bean names are determined is covered in a separate article, but this mechanism lets a bean find out its own name at runtime.

public class LoggingAwareBean implements BeanNameAware {
    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}

It’s almost never needed in business logic — it’s mostly used in library implementations.

BeanFactoryAware - receive the BeanFactory itself

With setBeanFactory(BeanFactory beanFactory), you receive the very BeanFactory that created you.

public class RegistryBean implements BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
}

This is meant for low-level processing like scope switching or proxy generation, and has no place in everyday business logic.

ApplicationContextAware - receive the entire container

With setApplicationContext(ApplicationContext context), you receive the entire container. Of the four, this is the interface you’ll see most often.

public class ReportService implements ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
    }

    public void generate() {
        context.getBean(MailSender.class).send("done");
    }
}

Being able to pull out any bean later with getBean() looks convenient at first glance, but this very “do-anything” convenience is the root cause of the problems described below.

EnvironmentAware - receive properties and profiles

With setEnvironment(Environment environment), you receive the Environment.

public class FeatureToggle implements EnvironmentAware {
    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    public boolean isBetaEnabled() {
        return environment.getProperty("feature.beta.enabled", Boolean.class, false);
    }
}

In most cases, direct DI via @Value is sufficient, and EnvironmentAware is only needed in special cases where you need to look at configuration values before bean definitions are resolved.

When it’s okay to use Aware

None of the Aware interfaces are meant for business logic — they’re wiring code for cooperating with the Spring container. A good example is when you’re building your own extension points, such as a custom BeanPostProcessor or FactoryBean, and need internal container information. Conversely, if you’re “just writing business logic,” there’s almost no reason to use Aware.

Why using it in business logic gets painful

If you implement ApplicationContextAware in a business logic class, your unit tests will need a mock ApplicationContext, making tests heavier. The dependencies you need end up hidden inside a setter instead of constructor arguments, so you can’t tell what a class depends on just by looking at it. Heavy use of getBean() tends to lead to a service locator pattern, where you manually resolve dependencies yourself instead of letting the DI container do it — a design that runs counter to the basic idea behind DI.

Replace it with constructor injection

If you know exactly what dependencies you need, just use plain constructor injection.

@Service
public class ReportService {
    private final MailSender mailSender;

    public ReportService(MailSender mailSender) {
        this.mailSender = mailSender;
    }

    public void generate() {
        mailSender.send("done");
    }
}

If all you need is to publish events, constructor-inject ApplicationEventPublisher; if all you need is to read configuration values, constructor-inject @Value or @ConfigurationProperties. That’s enough. With dependencies made explicit, you can simply new the class in tests, with no need to start up Spring.

Or choose a narrower Aware interface

If you really do need an Aware, there are narrower alternatives to ApplicationContextAware. If all you need is to publish events, there’s ApplicationEventPublisherAware; if all you need is to load resources, there’s ResourceLoaderAware. The first thing to consider should always be “wouldn’t constructor injection be enough?”

Summary

The Aware interfaces are a mechanism for passing information held by the Spring container to a bean — an API for wiring with the container, built on top of the basic mechanism of @Component. When you see them in library code, it’s almost always a legitimate use case, but if you find yourself wanting to write one in your own business logic, consider replacing it with constructor injection or a narrower Aware interface first.