九零后大叔
技术人的点点滴滴

springmvc篇-(七)SpringMVC&Spring配置父子容器启动服务

前一章节回顾: 通过前一个章节的了解,我们基本上了解了从tomcat解析web.xml到整个spring容器启动的整个流程环节。但是上一章节我们留下了一个疑问,就是spring容器启动过程居然包含父子容器这种启动方式。那么怎么样配置父子容器的启动方式呢?这种配置方式的原理是什么呢?有哪些需要注意的一些坑点呢?本章节将重点探讨这些问题。


1. 父子容器方式启动spring容器基本配置

第一步:配置pom.xml文件

    <dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>

    <!--1.spring框架相关包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>com.github.stefanbirkner</groupId>
        <artifactId>system-rules</artifactId>
        <version>1.16.1</version>
        <scope>test</scope>
    </dependency>
    <!--其他需要的包-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.4</version>
    </dependency>
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.2.6.RELEASE</version>
    </dependency>


    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>9.0.20</version>
    </dependency>

    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-servlet-api</artifactId>
        <version>9.0.24</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.0</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
    </dependency>

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <!--日志-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.10</version>
    </dependency>

    <!--mysql驱动包-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.35</version>
    </dependency>

    <!--切面相关包-->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>1.8.5</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.9</version>
    </dependency>
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>

    <!--json日志相关包-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.module</groupId>
        <artifactId>jackson-module-jaxb-annotations</artifactId>
        <version>2.9.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.5</version>
    </dependency>

    <!-- 9.fastjson支持 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.31</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
        <scope>provided</scope>
    </dependency>

</dependencies>

第二步:配置web.xml文件

<!DOCTYPE web-app PUBLIC
          "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
          "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>

    <display-name>Archetype Created Web Application</display-name>

    <!-- 配置spring父容器的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>

    <!-- 配置字符过滤器 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- 配置springmvc的dispatcherServlet-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置spring子容器的配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--配置home页面 -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>default.html</welcome-file>
        <welcome-file>default.htm</welcome-file>
        <welcome-file>default.jsp</welcome-file>
    </welcome-file-list>

</web-app>

第三步:因为是父子容器的方式启动spring,所以一定也存在父容器所需要的配置文件和子容器需要的配置文件。

配置spring父容器的配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- spring父容器会扫描除了controller包外的所有业务类,并将这些bean注入到父容器中 -->
    <context:component-scan base-package="com.minesoft.tutorial">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

</beans>

配置spring子容器的配置文件applicationContext-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- spring子容器只会扫描除了controller包的所有业务类,并将这些bean注入到子容器中 -->
    <context:component-scan base-package="com.minesoft.tutorial">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 静态资源(js、image等)的访问 -->
    <mvc:default-servlet-handler/>
    <!-- 开启注解 -->
    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json; charset=UTF-8</value>
                        <value>application/x-www-form-urlencoded; charset=UTF-8</value>
                    </list>
                </property>
                <!--配置 objectMapper 为我们自定义扩展后的 CustomMapper -->
                <property name="objectMapper">
                    <bean class="com.minesoft.tutorial.model.CommonMapper">
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!--配置静态资源访问路径-->
    <mvc:resources location="/WEB-INF/js/" mapping="/js/**"/>
    <mvc:resources location="/WEB-INF/css/" mapping="/css/**"/>

    <!-- 完成请求和注解POJO的映射 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
    <!-- HandlerMapping -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <!-- HandlerAdapter -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- ViewResolver -->
    <bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

第四步:配置controller,service,model等业务类

@RestController
@RequestMapping("/{version}/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/")
    public ResultData createUser(@RequestBody User user) {
        ResultData resultData = new ResultData();
        return resultData;
    }

    @RequestMapping(value = "", method = RequestMethod.GET)
    public ResultData<User> findUserById(@RequestParam(value = "id") Long id) {
        ResultData resultData = new ResultData();
        User user = userService.findUserById(id);
        resultData.handlerSuccess(user);
        return resultData;
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public ResultData updateUser(@RequestBody User user) {
        ResultData resultData = new ResultData();
        return resultData;
    }

    @RequestMapping(value = "", method = RequestMethod.DELETE)
    public ResultData deleteUserById() {
        ResultData resultData = new ResultData();
        return resultData;
    }
}

public interface UserService {
        User findUserById(Long id);
}

@Service
public class UserServiceImpl implements UserService {

    static Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Override
    public User findUserById(Long id) {

        User user = new User();
        user.setId(id);
        user.setName("张山");
        user.setIdentity("张山");
        user.setBankcard("36457736355363");
        user.setMobile("16752652625");
        user.setGender(2);
        user.setAge(18);

        return user;
    }
}

@Data
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class User implements Serializable {

    private static final long serialVersionUID = 4800166777883697833L;

    private Long id;
    private String name;
    private String identity;
    private String mobile;
    private String bankcard;
    private Integer age;
    private Integer gender;

}

经过上面的四步的配置,那么我们启动这个服务。启动服务的过程中,我们会看到下面的启动服务日志,明显看到父子容器不同的启动日志内容。


2. 父子容器方式的核心原理介绍

1.单容器启动spring:所有的bean都存放再一个spring容器中,任何一个bean都能引用另外一个bean。controller引用serveice,service引用mapper都可以。反之也可以,mapper引用service,service引用controller(一般不作么做,这种反向依赖的注入方式是违反开发规范的)。前面的系列文章的介绍都是单容器,大家可能比较能理解。

2.父子容器启动spring:在上面的配置过程中,所有的非controller的业务类全部都注入到父容器里面了,而所有的controller业务类全部都注入到子容器里面了。因为父容器先创建,子容器后创建,子容器(applicationContext对象)持有父容器(applicationContext对象),父容器并不持有子容器,所以子容器的bean是能够使用父容器的bean的,而父容器的bean是不能使用子容器的bean的。父子容器的概念其实和父子类的概念非常的相似,父类只能使用父类的属性不能使用子类的属性(可以用反射方式做到,但是没有任何的意思)而子类却可以使用父类的属性。同样的,spring框架的父容器的bean只能引用父容器的bean而不能引用子容器的bean;子容器的bean既能引用子容器的bean,也能引用父容器的bean。
需要注意的是,父子容器并不是一个继承关系,而是持有关系。在启动的过程中,先创建出父容器对象,然后创建子容器对象,然后将父容器对象设置为子容器对象的一个属性,父子容器一起构成了spring容器。


3. 父子容器方式环境下需要注意的一些点

1.使用父子容器启动spring环境的时候,会发现一个问题:如果直接在controller中注入service,然后希望在service方法的地方进行切面处理,我们会发现一个问题,就是不生效。如果想要生效,要记得在applicationContext-mvc.xml文件中添加

2.由1的问题,其实我们可以引申的发现第二个问题,就是如果我们以同样的方式使用事务的话,该事务同样也不会生效。
同样的道理,如果希望事务生效,补上上面的配置即可。

更多知识请关注公众号

赞(0) 赏一下
未经允许不得转载:九零后大叔的技术博客 » springmvc篇-(七)SpringMVC&Spring配置父子容器启动服务
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

九零后大叔的技术博客

联系我们联系我们

感谢您的支持

支付宝扫一扫打赏

微信扫一扫打赏