开发者

How to automatically create and inject a proxy of a missing dependency?

开发者 https://www.devze.com 2022-12-08 18:09 出处:网络
Given the following classes: package com.acme; public class Foo { private Bar bar; public void setBar(Bar newBar) {

Given the following classes:

package com.acme;

public class Foo {
  private Bar bar;
  public void setBar(Bar newBar) {
    this.bar = newBar;
  }
}

@AcmeService
public interface Bar {}

and a Spring configuration file of:

<beans>
  <bean id="foo" class="com.acme.Foo">
    <property name="bar" ref="bar" />
  </bean>
</beans>

when Spring's configuration phase runs, is it possible to do the following?

  1. Catch the missing dependency exception (or detect that there is going to be one)
  2. Discover that the missing dependency "bar" of bean "foo" is of type com.acme.Bar
  3. Notice that Bar is an @AcmeService, and so we're going to handle it. Otherwise, the error can be considered fatal
  4. Generate a proxy of type com.acme.Bar
  5. Inject the Bar proxy into Foo, ensuring that method calls on Bar can be advised with Spring AOP just as if I had explicitly created the Bar proxy in the Spring config file and injected the proxy into the Foo bean.

I was initially drawn to BeanPostProcessor and BeanFactoryPostProcessor as a means to implement this, although upon reading the documentation I did not see an obvious way of doing precisely what I want.

In case you're wondering, the underlying use case is that I want to:

  • Replace all Spring context files with my service-layer bean definitions with a single context file that generates proxies of @AcmeService bean interfaces.
  • Advise invocations against those proxies to instead be invoked on interface mocks of the service layer bean proxi开发者_如何学JAVAes. (The kicker is that the mocks are defined at runtime by a remote system and are uploaded to this app for integration testing purposes. For my regular unit tests, I do the more sane thing of simply creating and injecting mocks as needed).

I have all of that working except for the whole "dynamically generate proxies" bit. Currently I have empty implementations of my service interfaces defined in my context file just so they can be injected and advised - but I'd rather not have so many empty interface classes generated and checked in. And lastly, I assume I need to generate these proxies as I do not know how to advise attempted invocations on null dependencies.


OK, there's a number of things in here to talk about.

Firstly, I don't think you'll be able to do exactly what you're asking. In particular, if you have <property name="bar" ref="bar"/> and there is no bean definition for bar, the bean initialization will fail, and I don't think you can intercept that easily. You could, of course, write your own subclass of the BeanFactory itself, but that's not something to be taken lightly.

The closest existing mechanism to what you're asking for is auto-wiring. Take a look at AutowiredAnnotationBeanPostProcessor. You could use this as a guide for writing your own BeanPostProcessor which looks at every bean as it's instantiated by the container, looking for properties which are of types which in turn have the @AcmeService annotation. When it finds one, it can generate a proxy for that type and inject it.

That brings us on to the proxy generation itself. The place to look for that is ProxyFactory. This allows you to generate objects at runtime that implement any interface or extend any class, whilst attaching advisors/interceptors. These interceptors can do all the work - the proxy doesn't have to have anything "behind" it if you don't want to.

With this approach, you wouldn't need the "hanging" <property name="bar" ref="bar"/> entry in your config - the presence of the property in Foo would be enough to trigger the process.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号