Announcement Announcement Module
Collapse
No announcement yet.
Dependency injection for a command line utility Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Dependency injection for a command line utility

    Hi! What the best way to have Spring do dependency injection in a pre-existing object? Basically, I have a Java application class (i.e. a class with a "main" method), and I'd like to have Spring inject managed beans into an instance of it even though the class itself is not managed--similar to what DependencyInjectionTestExecutionListener does for integration tests.

    I've seen a couple of threads that suggest using @Configurable to accomplish this, but it's not clear to me exactly how this would work if the object is instantiated before the container. The Spring reference doc (section 6.8.1) seems to give conflicting information. At first it says, "It is also possible to ask a bean factory to configure a pre-existing object..." But then it says, "Instances of @Configurable objects created before the aspect has been configured will result in a warning being issued to the log and no configuration of the object taking place." What am I missing?

    Thanks.

  • #2
    Why not do the following
    Code:
    public class BlaBlaBla {
        private static ApplicationContext ac=new ClasspathXmlApplication("blaBlaBla.xml");
    
        public static void main(String [] args) {
            (BlaBlaBla)ac.getBean("blaBlaBla"),execute();
        } 
    
        public BlaBlaBla(parameters) {
              // Initialize if needed
        }
    
        public void execute() {
             // Move current code from main() here
        }
    
       // Setters
       public void setSomething(Something something) {
       }
    So you do not need @Configurable or other dirty tricks. You just pit "blaBlaBla" bean into context and inject it there.

    BTW, do not mix up class instantiations and object instantiations, Even if you create context in your Configurable (annotated with @Configurable) class e.g. by initializing of the static variable and then create in class main() method object of this class, it will be properly initialized (as far as context is properly configured).

    Regards,
    Oleksandr

    Comment


    • #3
      I want to avoid coupling my application classes to ApplicationContext. In the past, I did this by creating a wrapper around it that exposed type-safe getter methods for the service objects I was interested in. But I'm starting a new project with Spring 2.5 now, and I'm thinking it would be much nicer not to have to deal with the wrapper and just let Spring inject dependencies into my "main" classes--or, more precisely, instances of my "main" classes. Clearly, the Spring team has given this some thought because they built support for just this kind of injection for integration tests. I'm trying to achieve the same thing, but for a command line utility instead of a test.

      I played around with @Configurable, but can't get it to work. Apparently, it requires some AspectJ configuration including a runtime agent that I have to load with the JVM...? Ugh. I don't know much about AspectJ. Here's what I've got:

      Code:
      @Configurable
      public class HelloWorld {
      
        @Autowired	
        private IDataAccessObject m_dao;
      
        public static void main(String[] args) {
      
          // This just creates a singleton app context via 
          // a DefaultLocatorFactory. 
          DependencyInjection.bootstrap();
      
          // Now do some stuff!
          new HelloWorld().doStuff();
      
        }
      
        public void doStuff() {
          // I wish this worked, but it just throws a NPE.
          List<Object> result = m_dao.runSomeQuery();  // NPE!
        }
      }
      I would be just as happy if I could pass in an already-constructed instance of my class to Spring and say, "Okay, here's an object. Now wire it up!"

      Comment


      • #4
        Really, wiht the solutiopn from my previous post you couple to the application context not classes but exactly one class that contains main(), other classes need not to know a tad about Spring. But one class you have to couple anyway as you need to initialize ApplicationContext somewhere. And class with main() is no worse then any other class.

        Really, this class may easily be done quite generic

        Code:
            public static void main(String [] args) throws Exception {
               if (args.length==0) throw new Exception("Please, specify a bean to run");
                ((Runnable)ac.getBean(args[0])).execute();
            }
        So you have a single main class that can start absolutely different applications. It is ever possible to pass one more parameter - context name, if you wish.

        But anyway, if you do not want to KISS then in the attachement you find a simple working example of contigurable. It requires

        Code:
        aspectjweaver.jar
        commons-logging-1.1.jar
        log4j-1.2.13.jar
        spring.jar
        spring-aspects.jar
        in its root directory to run. Just copy jars there, start compile.bat and then run.bat.

        This example was developed more then year ago with Spring 2.0 rc3 but it should run with more recent versions as well.

        Originally posted by rhasselbaum View Post
        I want to avoid coupling my application classes to ApplicationContext. In the past, I did this by creating a wrapper around it that exposed type-safe getter methods for the service objects I was interested in. But I'm starting a new project with Spring 2.5 now, and I'm thinking it would be much nicer not to have to deal with the wrapper and just let Spring inject dependencies into my "main" classes--or, more precisely, instances of my "main" classes. Clearly, the Spring team has given this some thought because they built support for just this kind of injection for integration tests. I'm trying to achieve the same thing, but for a command line utility instead of a test.

        I played around with @Configurable, but can't get it to work. Apparently, it requires some AspectJ configuration including a runtime agent that I have to load with the JVM...? Ugh. I don't know much about AspectJ. Here's what I've got:

        Code:
        @Configurable
        public class HelloWorld {
        
          @Autowired	
          private IDataAccessObject m_dao;
        
          public static void main(String[] args) {
        
            // This just creates a singleton app context via 
            // a DefaultLocatorFactory. 
            DependencyInjection.bootstrap();
        
            // Now do some stuff!
            new HelloWorld().doStuff();
        
          }
        
          public void doStuff() {
            // I wish this worked, but it just throws a NPE.
            List<Object> result = m_dao.runSomeQuery();  // NPE!
          }
        }
        I would be just as happy if I could pass in an already-constructed instance of my class to Spring and say, "Okay, here's an object. Now wire it up!"

        Comment


        • #5
          KISS is precisely why I don't want to be coupled to the ApplicationContext in my command line utility classes. But you're right, I think @Configurable with AspectJ is overkill for this. Fortunately, I was able to achieve exactly what I wanted with a just little extra coding in my bootstrap class. I've attached the source for anyone else who might be interested in doing this.

          Now, when I bootstrap the ApplicationContext in my main method, I can pass in any annotated class and have Spring inject dependencies into a new instance of it. The code looks like this:

          Code:
          HelloWorld helloWorld = DependencyInjection.bootstrap(HelloWorld.class);
          It works by creating a bean definition on the fly for the HelloWorld class in a child context and configures it using the annotation post processors. Kinda neat.

          Comment


          • #6
            In my opinion this solution is much more involved, increase coupling (instead of decreasing it) and has no advantages that I can see. But up to you.

            Or do you believe that including bean definition into application context xml couples this bean to context?

            Regards,
            Oleksandr

            Comment


            • #7
              Originally posted by al0 View Post
              In my opinion this solution is much more involved, increase coupling (instead of decreasing it) and has no advantages that I can see. But up to you.

              Or do you believe that including bean definition into application context xml couples this bean to context?

              Regards,
              Oleksandr
              True, it increases coupling within the bootstrap class, but that's okay with me because it's just one class.

              One point that I didn't make clear in my initial post is that I anticipate having a large number of command line utility classes (console applications) in my project. You're right, if there was only one or two, then using the ApplicationContext directly would be no big deal. The advantage of this from my perspective is that I can create an arbitrary number of console applications with different dependencies and they will be satisfied almost automatically at startup.

              I suppose I could just annotate them all with @Component, which would eliminate the need to create a bean definition programmatically, but this way, the main application context doesn't become cluttered with a bunch of extra objects--only one of which would be needed at runtime.

              Comment


              • #8
                Even so you may escape with single context a without it programmatic building.

                Just include in you context PropertyPlaceholderConfigurer, configured to substitute placeholders from system properties and following bean definition

                Code:
                <bean id="injectionTarget" class="${injectionTargetClass}"/>
                Then specify on command-line
                Code:
                java -DinjectionTargetClass=somepacket.SomeClass Main parameters
                That should do the trick if I understand your needs properly. All that Main.java should do - initialize context, it even need not obtain targetInjection from context if that is specified as singleton.

                Comment


                • #9
                  Ahh, I didn't think of using a placeholder for the class name. Yes, that'd work, too. Although I wouldn't want the user to have to specify the bean as a JVM parameter, so I'd have to set it programmatically anyway.

                  Six of one, half-dozen of the other, I suppose. I've already written the DependencyInjection class and it fills my need quite well. Thanks for your insights, though.

                  Comment


                  • #10
                    Originally posted by rhasselbaum View Post
                    Ahh, I didn't think of using a placeholder for the class name. Yes, that'd work, too. Although I wouldn't want the user to have to specify the bean as a JVM parameter, so I'd have to set it programmatically anyway.
                    Just to keep last word - and he need not, you can take classname as normal parameter of your utility, then call System.setProperty("injectionTarget",args[0]) and only then create context.

                    Comment


                    • #11
                      In order to address this issue, I have created https://jira.springsource.org/browse/SPR-9044. If you like the proposed approach, please vote for it.

                      Comment

                      Working...
                      X