天天看点

从javaweb入门到spring+mvc框架(二)——环境配置先用后学:搭建spring+mvc环境及配置解析

先用后学:搭建spring+mvc环境及配置解析

javaweb跟框架的不同

通常学习java的路线是,先学基础的类型、集合框架、泛型、异常、io流、多线程等,然后是javaee部分的servlet、jdbc、xml、filter和listener等。

在javaweb框架中,服务器接收到http请求参数后封装为到request中,然后从服务器经过过滤器一直传递进来到servlet中,我的每个请求都对应一个servlet,通过重写doGet或者doPost方法处理,同时每个servlet都需要在web.xml中配置servlet或者使用@WebServlet注解。

其实一个servlet类就能处理全部内容,比如:

@WebServlet({ "/login" })
public class LoginServlet extends HttpServlet {
    //--1--
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //--2--
        if("".equals(username) || "".equals(password)){
            return;
        }
        //--3--
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection("url", "root", "******");

            String psql = "select username,password,age from user where username='" + username + "'";
            PreparedStatement prepareStatement = connection
                    .prepareStatement("INSERT into user(username,PASSWORD) VALUES('你好','aaaa')");
            prepareStatement.executeUpdate();

            if (connection != null)
                connection.close();
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
           

为了排除代码的臃肿,后来使用三层架构,把servlet业务按类型分开,像下面这样:

//web层
@WebServlet({ "/login" })
public class LoginServlet extends HttpServlet {

    LoginService loginService = new LoginService();

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String username = request.getParameter("username");

        String password = request.getParameter("password");

        loginService.login(username, password);
    }
}
//service层
public class LoginService {

    LoginDao loginDao = new LoginDao();

    public void login(String username, String password) {
        if("".equals(username) || "".equals(password)){
            return;
        } else {
            loginDao.findUser(username, password);
        }
    }
}
//dao层
public class LoginDao {

    public void findUser(String username, String password) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection(
                    "url", "root", "******");

            String psql = "select username,password,age from user where username='" + username + "'";
            PreparedStatement prepareStatement = connection.prepareStatement("INSERT into user(username,PASSWORD) VALUES('你好','aaaa')");
            prepareStatement.executeUpdate();

            if (connection != null)
                connection.close();

        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
           

所谓的三层架构(web、service、dao)其实并没有真正解耦合,因为他们之间只是通过分为三种class,对应分别处理自己的特定内容,web层处理请求转发,service层业务逻辑都放这,dao层数据库逻辑都放这。但是这个三层架构是非常好的思想,跟框架的思想虽然不一样但是目标是一致的。

框架,在web基础上封装和集成,通过实现底层接口继承底层基础类,最后形成自己的一套api框架。

从javaweb到框架,其实是对原本底层基础的代码做了更多的封装了,好比之前是面向具体的类和方法来编程,现在就等于面对框架的组件来开发了,很多代码已经被写好,使用也有针对特定功能。

这里也没有提及框架原理相关知识。

新建springmvc+spring+自带jdbc,完成相关配置

新建web project,结构如下:

从javaweb入门到spring+mvc框架(二)——环境配置先用后学:搭建spring+mvc环境及配置解析

src/main/java是放源代码的源文件夹,src/main/resources是放资源的源文件夹,也可以改为src/java和src/resources。

这个结构可以去属性页面修改,把项目的编码统一为UTF-8,jdk为1.x,对应的module为2.5或者3.0或者3.1:

从javaweb入门到spring+mvc框架(二)——环境配置先用后学:搭建spring+mvc环境及配置解析

(区分source folder、package、folder的区别:source folder只会在eclipse中显示目录结构,比如src/main/java/com/lwr/shop发布之后就只有com/lwr/shop目录结构,作为source folder的src/main/java是不带发布的,同时com/lwr/shop目录结构所发布到的位置也是上面那样指定的,一般是发布到classes目录下,变成classes/com/lwr/shop,所以最后变成了“根目录/WEB-INF/classes/com/lwr/shop“,所以所有放在source folder下的内容都会按其目录结构发布到classes,在配置文件路径时候特别注意要按照发布之后的文件路径来配置,”“表示从根目录/开始,”classpath:”表示从classes开始)

上面结构的project,一旦发布到服务器,其目录结构变为以下所示,这就是服务器上的目录:

从javaweb入门到spring+mvc框架(二)——环境配置先用后学:搭建spring+mvc环境及配置解析

因为我们指定了源文件夹src/main/java和src/main/resources的输出目录都是WEB-INF/classes,同时WebRoot下的文件默认输出到app发布目录的根目录下。因此,写在WEB-INF下的web.xml会发布到WEB-INF下,而写在src/main/java和src/main/resources目录下的所有文件原样发布到classes下。

然后添加jar包(纯净简洁版):

从javaweb入门到spring+mvc框架(二)——环境配置先用后学:搭建spring+mvc环境及配置解析

spring核心包:

core包(依赖common-logging)

beans包(依赖core包)

expression包(依赖core包)

aop包(依赖beans,core包)

context包(依赖core,beans,expression,aop包)

web包(依赖core,beans,context,aop包)

springmvc核心包:

webmvc包:(依赖core,beans,expression,aop,context,web包)

事务管理核心包:

tx包

数据库核心包:

jdbc包(依赖tx包)

web.xml配置

web.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

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

    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.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>

    <filter>
        <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

</web-app>
           

web.xml配置解析:

1、注意

<web-app >

中属性值的配置,复制即可,除了版本号其他一样的。

2、上面所配置的监听器ContextLoaderListener是用来引导spring容器初始化工作的,它是个实现类,与spring架构中的很多类有着错综复杂的关系,applicationContext.xml文件的加载和applicationContext.xml中的具体配置就是在这种复杂的关系调用中被初始化的。

3、在xml文件下直接写的

<context-param></context-param>

一般用来配置applicationContext.xml路径,ContextLoaderListener起作用之后的一系列动作,就包括从web.xml中获取到这个applicationContext.xml路径,上面路径

classpath:spring/applicationContext.xml

表示applicationContext.xml在服务器的classes/spring/下,对应到web project的相应位置。当然路径可以改。如果不写

<context-param></context-param>

配置,则默认在web.xml同目录下寻找这个applicationContext.xml(默认名称)。

4、配置org.springframework.web.servlet.DispatcherServlet,servlet-name自定义,

<load-on-startup>1</load-on-startup>

表示spring容器启动后首先加载(按正数从小到大顺序加载)。注意到这里的配置参数

<init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
           

也有contextConfigLocation这个,它是配置DispatcherServlet初始化的配置文件位置contextConfigLocation的,缺省值的路径是跟web.xml同目录,配置文件名是

<servlet-name>-servlet.xml

,比如这里的缺省文件名是dispatcherServlet-servlet.xml。如果写了,就按照自定义的路径查找springmvc配置文件。

5、org.springframework.web.filter.CharacterEncodingFilter顾名思义就是字符编码过滤,原理请搜索网络。

6、web.xml配置完后,框架就会加载其中的applicationContext.xml和dispatcherServlet-servlet.xml,找到对应路径,建立xml文件,接下来是这两个配置文件。

dispatcherServlet-servlet.xmll配置

dispatcherServlet-servlet.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:p="http://www.springframework.org/schema/p"
    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-4.3.xsd   
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd  
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

    <!--检测注解 -->
    <!-- springmvc-servlet配置需要排除Service注解,因为springmvc的注解实例会先于spring的注解实例被spring容器使用,但是springmvc的实例无法用于事务,一旦spring使用这里的注解实例,会导致事务管理失效 -->
    <context:component-scan base-package="cn.lwr.shop"
        use-default-filters="false">
        <context:include-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 启动注解驱动的Spring MVC功能 -->
    <mvc:annotation-driven>
        <!-- 这里配置需要的编码转换器Converter -->
        <mvc:message-converters>
            <!-- 配置上Jackjson的依赖jar,使用MappingJacksonHttpMessageConverter -->
            <bean
                class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <constructor-arg value="UTF-8" index="0" />
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/plain;charset=utf-8</value>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 对根目录下静态资源文件的访问,如果是WEB-INF下资源,需要用resource -->
    <mvc:default-servlet-handler />

    <!-- 配置视图解析器 如何把handler 方法返回值解析为实际的物理视图 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>
           

dispatcherServlet-servlet.xml配置解析和注意地方:

这部分主要配置1、注解的package扫描,2、注解驱动启动,3、对静态资源的访问,4、视图解析器

开头导入xsd文件是对xml标签格式的定义文件,需要用到哪些包下的类就导入哪些xsd。

1、在springmvc中扫package,如果@Controller注解都是放在同一个包中,可以直接扫描这个包。或者像上面那样扫描全部,然后用

context:include-filter

过滤@Controller注解。springmvc框架的注解注入只需要Controller控制器即可,其他的注入留给spring来做,请看上面注释。

2、注解驱动启动直接用

<mvc:annotation-driven />

,如果像上面带有参数就按开闭标签写,上面参数是配置编码类型转换器。引用网络上的内容,其实

<mvc:annotation-driven />

帮我们做了这些事情:注册以下HandlerMapping(处理器映射器),HandlerAdapter(处理器适配器),Resolver。这样就不用手动配置控制器了。

RequestMappingHandlerMapping 

BeanNameUrlHandlerMapping

RequestMappingHandlerAdapter

HttpRequestHandlerAdapter

SimpleControllerHandlerAdapter

ExceptionHandlerExceptionResolver 

ResponseStatusExceptionResolver 

DefaultHandlerExceptionResolver 
           

大概原理是这样:一开始是通过配置好那个访问路径对应哪个控制器的,后来改为配置注解类,通过注解来配置路径。最后用一个mvc注解驱动注册所有注解类。

  • 在xml中配置路径,springmvc容器会通过读取配置文件,mapping路径和类及方法。
  • 在xml中配置注解,springmvc容器通过反射调用

    getAnnocation()

    等方法获取到类和方法上的注解,然后执行该类型注解对应的内容,最后把类、方法mapping路径。

    3、

    <mvc:default-servlet-handler />

    使得请求能够访问app根目录下除了WEB-INF的文件内容。如果访问路径在WEB-INF中,使用

    <mvc:resources location="src路径" mapping="url"/>

    4、在控制器中返回的是相对路径,视图解析器就是为这个相对路径加上前缀和后缀。

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"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context-4.3.xsd 
                        http://www.springframework.org/schema/aop 
                        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd   
                        http://www.springframework.org/schema/tx 
                        http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

    <!-- @Required, @Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext 
        and @PersistenceUnit 等注解需要配置相应的bean才可以使用,但是context:annotation-config就能自动注册它们 -->
    <context:annotation-config />

    <!-- applicationContext是spring的配置文件,里面配置的注解对springmvc可见,可以被其使用 -->
    <!-- 作为父容器的spring容器 不需要controller注解,这个注解的实例应该由springmvc提供并使用 -->
    <!-- context:component-scan配置能够使@Component, @Repository, @Service, @Controller, 
        @RestController, @ControllerAdvice, and @Configuration 等原型注入spring bean。 
        同时context:annotation-config配置的功能也被启用,无需再配置。 -->
    <context:component-scan base-package="cn.lwr.shop">
        <context:exclude-filter type="annotation"
            expression="org.springframework.stereotype.Controller" />
    </context:component-scan>

    <!-- 配置读取外部配置文件 -->
    <context:property-placeholder location="classpath:spring/jdbc.properties" />

    <!-- spring自带数据源配置 destroy-method="close" -->
    <!-- 第一种配置写法 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.url}"
        p:username="${jdbc.username}" p:password="${jdbc.password}" />
    <!-- 第二种写法 -->
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName">
            <value>${jdbc.driverClassName}</value>
        </property>
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>

    <!-- 此行语句使得resource autowired 等四个注解可以使用 实例化jdbcTemplate,同时注入数据源 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />

    <!-- 配置事务管理器 -->
    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 事务扫描开始(开启@Tranctional) -->
    <tx:annotation-driven transaction-manager="txManager" />

</beans>
           

applicationContext.xml配置解析:

关于spring容器的配置都是在这个xml文件中。

注意的点:

1、

<context:component-scan

扫描包要把controller注解排除。

2、用了

<context:component-scan

,就已经包括

<context:annotation-config />

的所有功能,注解能用了。

3、配置properties文件,获取${…}的值,使用

<context:property-placeholder

来引入properties文件

4、${…}好像是EL语言,spring-expression.jar就是使用这个语言的基础。

5、这里配置的bean都会被spring容器生成实例,而bean的属性ref=”bean_id”表示在在这个实例里对于id为bean_id的bean可以直接引用,容器来注入实例,等同注解@autowried。

6、配置文件中的所有配置在容器中都是有根据的,也是有规律的,可以从原理和源码来分析。