PicoContainer has a rudimentary Aspect Orientated Programing (AOP) capability with its 'Interception' behavior. With respect to the methods of a component, both before and after invocation, control can be handed to an interceptor. You can intercept a method call:

There are some limitations:

public static class BiteReporter implements Apple {
    private Intercepted.Controller controller;

    public BiteReporter(Intercepted.Controller controller) {
        this.controller = controller;

    } 
    public boolean takeBite(int grams) {
        System.out.println("Bite of " + grams + " grams of apple '" + controller.instance().getName() + "'");
        return false; // ignored, but necessary.
    }
}
...
pico = new DefaultPicoContainer(new Intercepting());
pico.addComponent(Apple.class, BraeburnApple.class);
Intercpeted intercepted = pico.getComponentAdapter(Apple.class).findAdapterOfType(Intercpeted.class);
intercepted.pre(Apple.class, new BiteReporter(intercepted.getController()));
// see also Intercpeted.post(...) method.
Apple a1 = pico.getComponent(Apple.class);
a1.takeBite(100); 
// prints Bite of 100 grams of apple 'Braeburn' 
// ... irrespective of what else Braeburn.takeBite(int) does.
  
pico = new DefaultPicoContainer();
pico.as(INTERCEPT).addComponent(Apple.class, BraeburnApple.class);
// etc
pico = new PicoBuilder.withInterception().build();
pico.addComponent(Apple.class, BraeburnApple.class);
// etc
pico = new PicoBuilder.withBehaviors(interception()).build();
pico.addComponent(Apple.class, BraeburnApple.class);
// etc

Fine grained participation in interception
Assuming you're passing in the Interceptor to the classes you're using for interception of a component, you can participate in the fate of the method call. For a 'pre' invocation, you can veto the calling of the 'real' method.

    public boolean takeBite(int grams) {
        if (grams > 50) {
             controller.veto();

        }
        return false; // will be passed back to the caller.
    }

For a 'post' invocation, you can override the return value of the 'real' method.

    public boolean takeBite(int grams) {        

        if (grams > 50) {
             controller.override();
             (Apple) realApple = (Apple) controller.instance();
             realApple.takeBite(-1 * grams); // undo !
        }
        return false; // will be passed back to the caller.
    }

Also for a 'post' invocation, you can access the return value of the 'real' method.

    public boolean takeBite(int grams) {        

        boolean rv = (boolean) controller.getOriginalRetVal();
        if (rv == false) {
             // do something !
        }
        return true; // ignored as no 'override'
    }