Spring injection Into Servlet

后端 未结 4 1807
Happy的楠姐
Happy的楠姐 2020-11-29 21:17

So I have seen this question:

Spring dependency injection to other instance

and was wondering if my method will work out.

1) Declare beans in my Spri

相关标签:
4条回答
  • 2020-11-29 21:45

    The answers here so far only worked partly for me. Especially classes with @Configuration annotation were ignored and I did not want to use an xml configuration file. Here is what I have done to get injection working working soley with an Spring (4.3.1) annotation based setup:

    Bootstrap an AnnotationConfigWebApplicationContext in web.xml under web-app. As parameter you need the contextClass and the contextConfigLocation (your annotated config class):

    <context-param>
        <param-name>contextClass</param-name>
        <param-value>
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
      </param-value>
    </context-param>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.example.config.AppConfig</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    Then overwrite the init method in the servlet. I use an abstract class which extends HttpServlet, so I don't have to repeat it in every servlet:

    @Configurable
    public abstract class MySpringEnabledServlet extends HttpServlet
    {
      @Override
      public void init(
          ServletConfig config) throws ServletException
      {
        super.init(config);
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
      }
    [...]
    }
    

    and finally I have my main configuration in the AppConfig class mentioned in web.xml:

    @Configuration
    @ComponentScan(basePackages = "com.example")
    @Import(
    { SomeOtherConfig.class })
    public class AppConfig
    {
    }
    

    The dependend classes are annotated:

    @Component
    public class AnnotatedClassToInject
    

    and injected via autowiring in my servlet:

    @Autowired
    private AnnotatedClassToInject myClass;
    
    0 讨论(0)
  • 2020-11-29 21:47

    What you are trying to do will make every Servlet have its own ApplicationContext instance. Maybe this is what you want, but I doubt it. An ApplicationContext should be unique to an application.

    The appropriate way to do this is to setup your ApplicationContext in a ServletContextListener.

    public class SpringApplicationContextListener implements ServletContextListener {
            @Override
        public void contextInitialized(ServletContextEvent sce) {
            ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            sce.getServletContext().setAttribute("applicationContext", ac);            
        }
        ... // contextDestroyed
    }
    

    Now all your servlets have access to the same ApplicationContext through the ServletContext attributes.

    @Override
    public void init(ServletConfig config) throws ServletException {
       super.init(config);
    
       ApplicationContext ac = (ApplicationContext) config.getServletContext().getAttribute("applicationContext");
    
       this.apiData = (ApiData)ac.getBean("apiData");
       this.apiLogger = (ApiLogger)ac.getBean("apiLogger");
    }
    
    0 讨论(0)
  • 2020-11-29 21:57

    I wanted to leverage on the solution provided by Sotirios Delimanolis but adding transparent autowiring to the mix. The idea is to turn plain servlets into autowire-aware objects.

    So I created a parent abstract servlet class that retrieves the Spring context, gets and autowiring-capable factory and uses that factory to autowire the servlet instances (the subclasess, actually). I also store the factory as an instance variable in case the subclasses need it.

    So the parent abstract servlet looks like this:

    public abstract class AbstractServlet extends HttpServlet {
    
        protected AutowireCapableBeanFactory ctx;
    
        @Override
        public void init() throws ServletException {
            super.init();
            ctx = ((ApplicationContext) getServletContext().getAttribute(
                    "applicationContext")).getAutowireCapableBeanFactory();
            //The following line does the magic
            ctx.autowireBean(this);
        }
    }
    

    And a sevlet subclass looks like this:

    public class EchoServlet extends AbstractServlet {
    
        @Autowired
        private MyService service;
    
        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
            response.getWriter().println("Hello! "+ service.getMyParam());
        }
    }
    

    Notice the only thing EchoServlet needs to do is to declare a bean just in common Spring practice. The magic is done in the init() method of the superclass.

    I haven't tested it thoroughly. But it worked with a simple bean MyService that also gets a property autowired from a Spring-managed properties file.

    Enjoy!


    Note:

    It's best to load the application context with Spring's own context listener like this:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

    Then retrieve it like this:

    WebApplicationContext context = WebApplicationContextUtils
        .getWebApplicationContext(getServletContext());
    ctx = context.getAutowireCapableBeanFactory();
    ctx.autowireBean(this);
    

    Only spring-web library needs to be imported, not spring-mvc.

    0 讨论(0)
  • 2020-11-29 22:02

    Spring is independent of Servlet startup. Right after spring reads the bean xml it will be ready to deliver the beans. So right after below statement, beans are already available

    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    

    Also as pointed by @LuiggiMendoza each ApplicationContext will create/maintain their own beans so its always good to create ApplicationContext once and reuse it from different servlets (as opposed to creating them inside the init() method of a Servlet)

    0 讨论(0)
提交回复
热议问题