Java Reference

BeanContext

Introduction

This page describes the BeanContext class. It’s primary responsibility is to "wire" system beans together (injecting all dependent objects into beans that use them).

Related to this is "Ready Processing", where you’ll learn how to control the start-up phases of your application.

API Components

This article discusses just the BeanContext class.

BeanContext

We’ve used the BeanContext in our autowiring discussions above. Now, let’s look closer at what the BeanContext class provides. It is composed of the following:

  • Two add() methods

  • Two update() methods

  • Four getBean() methods

  • Two getBeans() methods

  • One connect() method

API: BeanContext class
public class BeanContext {

    // Add methods:
    void add(Object bean);
    void add(String name, Object bean);

    // Update methods:
    void update();
    void update(String phase);

    // Get bean methods:
    <T> T getBean(Class<? extends T> clazz);
    <T> T getBean(Class<? extends T> clazz, boolean inherit);
    <T> T getBean(String name, Class<T> clazz);
    <T> T getBean(String name, Class<T> clazz, boolean inherit);

    // Get beans methods:
    <T> List<T> getBeans(Class<T> clazz);
    <T> List<T> getBeans(Class<T> clazz, boolean inherit);

    // Connect method:
    void connect(Object bean);
}

Example Code

This section demonstrates many of the BeanContext methods.

Source code for this and most other Java reference pages is available on GitHub.

add() methods

There are two add() methods that add a bean to the context. Either of these can be called at any time, and will simply add the bean to the context’s pending list.

In order to trigger autowiring, either update() or update(phase) must be called. This allows a any number of beans to be added before wiring occurs.

Let’s take a look at how you would use each of these.

ctx.add(bean)

The first version (bean only, no name) simply takes the bean itself, adding it to the context. It is used when there is one and only one bean of the given type (the most frequent case).

For example, you might have only a single DataSource object for your entire application. The following code snippet shows how that DataSource object would be created and referenced:

Example using context add() method in conjuction with @Autowired
// In the main application:
DataSource dataSource1 = new OurDataSource();
ctx.add(dataSource1);
// . . .

// In another class that depends on dataSource,
// this dataSource field will be instantiated with the dataSource1 object,
// as this is the one and only DataSource bean:
@Autowired
private DataSource dataSource;
// . . .

ctx.add(name, bean)

The second version (bean and name) takes both the bean itself AND a name. It is used when there are multiple beans of the same type, therefore the only way to differentiate them is by naming them.

For example, suppose your dispenser has two identical ice trays (primary and backup), and that they were both of type "IceTray". Any object that needs access to an ice tray must be able to indicate with one(s) it wants to use.

To do that, we must name the two different beans:

Beans in context differentiated by name
// In the main application:
IceTray iceTray1 = new OurIceTray("P");
IceTray iceTray2 = new OurIceTray("B");
ctx.add("Primary", iceTray1);
ctx.add("Backup", iceTray2);
// . . .

// In another class that uses both ice trays,
// it must name the bean it wants,
// as they are two of the very same type:
@Autowired(qualifier = "Primary")
private IceTray primaryIceTray;  // gets instantiated with iceTray1

@Autowired(qualifier = "Backup")
private IceTray backupIceTray;  // gets instantiated with iceTray2
// . . .

update() methods

There are two update() methods, both of which will autowire any number of beans.

ctx.update()

The first and simplest version takes no parameters. It updates the context without any "phase". Once called, the context can no longer use phases as we assume the context is now fully initialized.

If any fields are still in the pending list that reference an unused phase, they will throw an exception, as the assumption is that the phase was accidentally skipped and therefore something is missing. Any non-phase fields that cannot be resolved also generate an exception.

It is possible, although not recommended, to add more beans to the context and call update() again to wire them up. However, as mentioned above, they must all be successful or an exception will be thrown.

An example of this method is shown in the next section.

ctx.update(phase)

The second version of the update() method accepts a phase name. A "phase" allows autowiring of a subset of beans, which provides for better control over when autowiring occurs.

For example, you might want to ensure that all database-related objects are wired first, followed by user interface objects, followed by everything else.

To achieve this, you’d write something like the following:

Updating bean context in phases
// In the main application:
ctx.update("one");  // autowire all phase "one" beans
ctx.update("two");  // autowire all phase "two" beans
ctx.update();       // autowire all remaining beans
// . . .

// In another class, mark fields that should
//  be instantiated in a particular phase.
@Autowired(phase = "one")
private MyClass1 myClass1;  // gets instantiated by update("one") call

@Autowired(phase = "two")
private MyClass2 myClass2;  // gets instantiated by update("two") call

@Autowired
private MyClass3 myClass3;  // gets instantiated by update() call
// . . .
Be careful when calling update() method

Calling ctx.update(null) is the same as calling ctx.update(). So if you’re passing in a string variable that contains the phase name, and it happens to be null by mistake, then ALL beans will be autowired, and the autowiring phase will finish prematurely.

getBean() methods

There are four methods that allow you to retrieve a desired bean from the context. These can be used at any time during program operation. Typically, these are used after the autowiring process.

In all cases, if the specified bean is not found, then null is returned.

ctx.getBean(clazz)

The following example shows how to retrieve a bean from the context. It searches your application’s context AND its parent contexts (explained elsewhere TBD).

// Retrieve the MyClass1 instance:
MyClass1 myClass1 = ctx.getBean(MyClass1.class);
if (myClass1 != null) {
    // The MyClass1 bean WAS found in the context.
    // Use it as desired.
} else {
    // The MyClass1 bean was NOT found in the context.
    // Perform alternate tasks or throw an exception.
}

ctx.getBean(clazz, inherit)

The following example shows how to retrieve a bean from the context, identical to the above example EXCEPT that the parent context is not searched for a potential matching bean.

// Retrieve the MyClass1 instance,
//  but do not check any parent contexts:
MyClass1 myClass1 = ctx.getBean(MyClass1.class, false);

ctx.getBean(name, clazz)

For beans that are named (see the ctx.add(name, bean) method), this method allows you to specify that name.

// Retrieve the MyClass1 bean named "primary":
MyClass1 myClass1 = ctx.getBean("primary", MyClass1.class);

ctx.getBean(name, clazz, inherit)

The final getBean() method variation is just like the previous call, except you can choose not to search the parent contexts for a matching bean.

// Retrieve the MyClass1 bean named "primary",
//  but do not check any parent contexts:
MyClass1 myClass1 = ctx.getBean("primary", MyClass1.class, false);

getBeans() methods

There are two methods that let you retrieve a list of beans that are of the same type.

In both cases, if no matching beans are found, and empty list is returned.

ctx.getBeans(clazz)

The following example asks for all beans of type MyClass1.

// Retrieve list of all objects of type MyClass1:
List<MyClass1> myClass1List = ctx.getBeans(MyClass1.class);

// Iterate through these:
for (MyClass1 myClass1: myClass1List) {
    // Perform desired operations on each myClass1.
}

ctx.getBeans(clazz, inherit)

The second getBeans() method is identical to the first except that you can choose to not search the parent contexts.

// Retrieve list of all objects of type MyClass1,
//  but do not check any parent contexts:
List<MyClass1> myClass1List = ctx.getBeans(MyClass1.class, false);

connect() method

There is a single connect() method.

ctx.connect(bean)

This method connects a bean to the context. It is used after the final update() method has been called, and is therefore useful for dynamically created beans that want access to some beans in the BeanContext, without having to manually look up every bean.

This will resolve the autowired fields and attach the bean to any ListenerList objects that line up with the interfaces provided by this bean. This does not add the bean to the context, and it will not receive any lifecycle events that context beans receive since this API will either return successful or throw an exception if there’s anything that can’t be autowired.

ctx.update();       // the final update() is called
// . . .

// Create a new bean:
MyNewClass myNewClass = new MyNewClass();

// Autowire this new bean:
ctx.connect(myNewClass);

Summary

We now have a basic understanding of the BeanContext, and how it is used to connect all of your application’s objects together. We now look at how our code can be informed of when a bean or set of beans have been injected, using the CtxEventListener interface.

Previous
Next
On this page
Java Development
Seamlessly transition from Legacy+ systems to Freestyle microdosing and advanced distributed dispense systems.
UI Development
Using KOS SDKs, integrating Consumer and Non-consumer facing UIs becomes seamless, giving you less hassle and more time to create.
Video Library
Meet some of our development team, as they lead you through the tools, features, and tips and tricks of various KOS tools.
Resources
Familiarize yourself with KOS terminology, our reference materials, and explore additional resources that complement your KOS journey.
Copyright © 2024 TCCC. All rights reserved.