其他分享
首页 > 其他分享> > 《精通Spring4.x企业应用开发实战》第二章

《精通Spring4.x企业应用开发实战》第二章

作者:互联网

  昨天联系了一下学长,学长说这个项目因为种种原因代码比较混乱,感觉最坏的打算是从头开始写。

  大概询问了一下学长和xianhua学姐的建议,又看了看网上的资料,这个项目开发的技术栈基本就是SpringBoot + vue + D3,SpringBoot做后端的东西,vue写个前端的东西,D3用来做知识图谱那个图比较好。

  数据库的话应该要用Neo4j,应该还要加一个关系数据库。

  

  先花几天时间突击一下Spring,知乎上推荐这个书的比较多,源码先跑起来看看。废话不多说,从第二章开始看。

  第二章主要要求做一个Spring的登录模块,首先要建立相应的数据库和表相关。

drop database if exists sampledb;
create database sampledb default character set utf8;
use sampledb;

#创建用户表
create table t_user (
    user_id int auto_increment primary key,
    user_name varchar(30),
    credits int,
    password varchar(32),
    last_visit datetime, 
    last_ip varchar(23)
)engine = InnoDB;

#创建用户登录日志表
create table t_login_log (
    login_log_id int auto_increment primary key,
    user_id int,
    ip varchar(23),
    login_datatime datetime
)engine=InnoDB;

#插入初始化数据
insert into t_user (user_name, password) values ('admin', '123456');
commit;

 

  为了编码的统一,将IDEA设置成统一的utf8编码。

  

  

  

 

  之后就是配置maven和创建项目的详细内容,由于这个博客的主要目的不在于详细记录每一个步骤(具体步骤可以参考书籍中内容),所以只对我觉得关键的步骤和内容进行笔记。

  类包以分层的方式进行组织,共划分为4个包,分别是dao(持久层)、 domain(领域对象)、 service(业务层)和 web(展现层)。领域对象从严格意义上讲属于业务层,但由于领域对象可能同时被持久层和展现层共享,所以一般将其单独划分到一个包中

  spring-context.xml是配置文件。

2.3持久层

2.3.1 建立领域对象

  持久层负责数据的访问和操作,DAO类被上层的业务类调用。Spring 本身支持多种流行的ORM框架(第14章对此进行专门的讲解),这里使用Spring JDBC作为持久层的实现技术(关于Spring JDBC的详细内容,请参见第13章)。为了方便阅读,我们会对本章涉及的相关知识点进行必要的介绍,所以在不了解Spring JDBC的情况下,相信读者也可以轻松阅读本章的内容。

  领域对象(Domain Object)也被称为实体类,它代表了业务的状态,且贯穿展现层、业务层和持久层,并最终被持久化到数据库中。领域对象使数据库表操作以面向对象的方式进行,为程序扩展带来了更大的灵活性。领域对象不一定等同于数据库表,不过对于简单的应用来说,领域对象往往拥有对应的数据库表。

  持久层的主要工作就是从数据库表中加载数据并实例化领域对象,或将领域对象持久化到数据库表中。论坛登录模块需要涉及两个领域对象: User 和 LoginLog,前者代表用户信息,后者代表日志信息,分别对应t_user和t_login_log 数据库表,领域对象类的包为com.smart.domain。

  

  

 

2.3.2 UserDao

  首先来定义访问User的 DAO,它包括3个方法。

  getMatchCount():根据用户名和密码获取匹配的用户数。等于1表示用户名/密码正确;等于0表示用户名或密码错误(这是最简单的用户身份认证方法,在实际应用中需要采用诸如密码加密等安全策略)。

  findUserByUserName():根据用户名获取User对象。

  updateLoginInfo():更新用户积分、最后登录IP及最后登录时间。下面通过Spring JDBC技术实现这个DAO类,如代码所示。

@Repository
public class UserDao {
    private JdbcTemplate jdbcTemplate;

    private  final static String MATCH_COUNT_SQL = " SELECT count(*) FROM t_user  " +
            " WHERE user_name =? and password=? ";

    public int getMatchCount(String userName, String password) {

        return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[]{userName, password}, Integer.class);
    }

...

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

  这里说明了两个注释的作用@Repository //通过Spring 注解定义一个DAO、@Autowired//自动注入 JdbcTemplate的 Bean .

  传统的JDBC API太底层,即使用户执行一条最简单的数据查询操作,都必须执行如下过程:获取连接→创建Statement→执行数据操作→获取结果→关闭Statement→关闭结果集→关闭连接,除此之外还需要进行异常处理的操作。如果使用传统的JDBC API进行数据访问操作,则可能会产生1/3以上单调乏味的重复性代码。

  Spring JDBC对传统的JDBC API进行了薄层封装,将样板式的代码和那些必不可少的代码进行了分离,用户仅需要编写那些必不可少的代码,剩余的那些单调乏味的重复性工作则交由Spring JDBC框架处理。简单来说,Spring JDBC通过一个模板类org.springframework.jdbc.core.JdbcTemplate封装了样板式的代码,用户通过模板类就可以轻松地完成大部分数据访问操作。

  如getMatchCount()方法,我们仅提供了一个查询SQL语句,直接调用模板的queryForInt()方法就可以获取查询,用户不用担心获取连接、关闭连接、异常处理等烦琐的事务。

@Repository
public class UserDao {
    private JdbcTemplate jdbcTemplate;
    private  final static String UPDATE_LOGIN_INFO_SQL = " UPDATE t_user SET " +
            " last_visit=?,last_ip=?,credits=?  WHERE user_id =?";

    public User findUserByUserName(final String userName) {
        String sqlStr = " SELECT user_id,user_name,credits "
                + " FROM t_user WHERE user_name =? ";
        final User user = new User();
        jdbcTemplate.query(sqlStr, new Object[] { userName },
                new RowCallbackHandler() {
                    public void processRow(ResultSet rs) throws SQLException {
                        user.setUserId(rs.getInt("user_id"));
                        user.setUserName(userName);
                        user.setCredits(rs.getInt("credits"));
                    }
                });
        return user;
    }

    public void updateLoginInfo(User user) {
        jdbcTemplate.update(UPDATE_LOGIN_INFO_SQL, new Object[] { user.getLastVisit(),
                user.getLastIp(),user.getCredits(),user.getUserId()});
    }

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

  

  findUserByUserName()方法稍微复杂一些。这里,我们使用了JdbcTemplate#query()方法,该方法的签名为query(String sql,  Object[]args, RowCallbackHandler rch),它有3个入参。
  sqlStr:查询的SQL语句,允许使用带“?”的参数占位符。
    args: SQL语句中占位符对应的参数数组。
    RowCallbackHandler:查询结果的处理回调接口,该回调接口有一个方法
    processRow(ResultSet rs),负责将查询的结果从ResultSet装载到类似于领域对象的对象实例中。
  findUserByUserName()通过匿名内部类的方式定义了一个RowCallbackHandler回调接口实例,将ResultSet 转换为User对象。

  updateLoginInfo()方法比较简单,主要通过JdbcTemplatc#update(String sql,Object[])进行数据的更新操作。

 

2.3.4在 Spring 中装配DAO
  在编写DAO接口的实现类时,大家也许会有一个问题:在以上两个DAO 实现类中都没有打开/释放Connection的代码,DAO类究竟如何访问数据库呢﹖前面说过,样板式的操作都被JdbcTemplate封装起来了,JdbcTemplate本身需要一个DataSource,这样它就可以根据需要从DataSource中获取或返回连接。UserDao和 LoginLog 都提供了一个带@Autowired注解的JdbcTemplate变量,所以我们必须事先声明一个数据源,然后定义一个JdbcTemplate Bean,通过Spring 的容器上下文自动绑定机制进行Bean的注入。
  在项目工程的src\resources(在Maven工程中,资源文件统一放置在resources文件夹中)目录下创建一个名为smart-context.xml的Spring配置文件,该配置文件的基本结构如下:

<?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: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.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
    ...
</beans>
<?xml version="1.0" encoding="UTF-8" ?>
<beans ...>
    
    <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
    <context:component-scan base-package="com.smart.dao"/>
    <context:component-scan base-package="com.smart.service"/>
    
    <!-- 配置数据源 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" 
        p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://localhost:3306/sampledb"
        p:username="root"
        p:password="123456" />

    <!-- 配置Jdbc模板  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />
        
</beans>

  我们使用Spring 的<context:component-scan>扫描指定类包下的所有类,这样在类中定义的Spring注解(如@Repository、@Autowired等)才能产生作用。

  我们使用Jakarta 的 DBCP开源数据源实现方案定义了一个数据源,数据库驱动器类为com.mysql.jdbc.Driver。

  配置了JdbcTemplate Bean,将声明的dataSource注入JdbcTemplate 中,而这个JdbcTemplate Bean将通过@Autowired自动注入LoginLog和UserDao 的Bean中,可见Spring可以很好地将注解配置和XML配置统一起来。

  这样就完成了登录模块持久层所有的开发工作,接下来将着手业务层的开发和配置工作。我们将对业务层的业务类方法进行单元测试,到时就可以看到DAO的实际运行效果了,现在暂时把这两个DAO放在一边。

  总结一下,访问User的 DAO,它包括3个方法:getMatchCount(),findUserByUserName(),updateLoginInfo(),LoginLogDao负责记录用户的登录日志,它仅有一个insertLoginLog()接口方法。

 

2.4业务层
  在论坛登录实例中,业务层仅有一个业务类,即 UserService。UserService负责将持久层的UserDao和LoginLoginDao组织起来,完成用户/密码认证、登录日志记录等操作。

2.4.1 UserService
  UserService 业务接口有3个业务方法,其中,hasMatchUser()方法用于检查用户名/密码的正确性;findUserByUserName()方法以用户名为条件加载User对象;loginSuccess(方法在用户登录成功后调用,更新用户最后登录时间和P信息,同时记录用户登录日志。
  下面我们来实现这个业务类。UserService的实现类需要调用DAO层的两个DAO完成业务逻辑操作,如代码所示。

@Service
public class UserService {
    private UserDao userDao;
    private LoginLogDao loginLogDao;


    public boolean hasMatchUser(String userName, String password) {
        int matchCount =userDao.getMatchCount(userName, password);
        return matchCount > 0;
    }
    
    public User findUserByUserName(String userName) {
        return userDao.findUserByUserName(userName);
    }

    @Transactional
    public void loginSuccess(User user) {
        user.setCredits( 5 + user.getCredits());
        LoginLog loginLog = new LoginLog();
        loginLog.setUserId(user.getUserId());
        loginLog.setIp(user.getLastIp());
        loginLog.setLoginDate(user.getLastVisit());
        userDao.updateLoginInfo(user);
        loginLogDao.insertLoginLog(loginLog);
    }

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Autowired
    public void setLoginLogDao(LoginLogDao loginLogDao) {
        this.loginLogDao = loginLogDao;
    }
}

  首先通过@Service注解将UserService标注为一个服务层的Bean;然后通过@Autowired注入userDao和 loginLogDao 这两个DAO层的Bean;接着通过 hasMatchUser()和findUserByUserName()业务方法简单地调用DAO 完成对应的功能;最后为loginSuccess()方法标注@Transactional事务注解,让该方法运行在事务环境中(因为我们在 Spring事务管理器拦截切入表达式上加入了@Transactional过滤),否则该方法将在无事务方法中运行。loginSuccess()方法根据入参user对象构造出 LoginLog 对象并将user.credits递增5,即用户每登录一次赚取5个积分,然后调用userDao更新到t_user中,再调用loginLogDao向t_login_log表中添加一条记录。

  loginSuccess()方法将两个DAO组织起来,共同完成一个事务性的数据操作:更新t_user表记录并添加t_login_log表记录。但我们从UserService中却看不出任何事务操作的影子,这正是Spring的高明之处,它让我们从事务操作单调、机械的代码中解脱出来,专注完成那些不可或缺的业务工作。通过Spring声明式事务配置即可让业务类享受EJB声明式事务的好处,下一节我们将了解如何赋予业务类事务管理的能力。

 

2.4.2 在 Spring 中装配Service
  事务管理的代码虽然无须出现在程序代码中,但我们必须以某种方式告诉Spring哪些业务类需要工作在事务环境下及事务的规则等内容,以便Spring根据这些信息自动为目标业务类添加事务管理的功能。
打开原来的smart-context.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"
<!-- 1 -- > xmlns:context="http://www.springframework.org/schema/context" 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.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
  
  <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
  <context:component-scan base-package="com.smart.dao"/>
  <!-- 2 -->
  <context:component-scan base-package="com.smart.service"/>

...
<!-- 配置事务管理器 -->
   <!-- 3 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource" />
  <!-- 4 --> <!-- 通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务 --> <aop:config proxy-target-class="true"> <aop:pointcut id="serviceMethod" expression="(execution(* com.smart.service..*(..))) and (@annotation(org.springframework.transaction.annotation.Transactional))" /> <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> </tx:attributes> </tx:advice> </beans>

  ①处在<beans>的声明处添加 aop和 tx命名空间的Schema定义文件的说明,这样,在配置文件中就可以使用这两个空间下的配置标签了。
  ②处将com.smart.service添加到上下文扫描路径中,以便使service包中类的Spring注解生效。
  ③处定义了一个基于数据源的DataSourceTransactionManager事务管理器,该事务管理器负责声明式事务的管理。该管理器需要引用dataSource Bean。
  ④处通过aop及tx命名空间的语法,以AOP的方式为com.smart.service包下所有类的所有标注@Transactional注解的方法都添加了事务增强,即它们都将工作在事务环境中(关于Spring事务的配置,详见第11章)。
  这样就完成了业务层的程序开发和配置工作,接下来需要对该业务类进行简单的单元测试,以便检验业务方法的正确性。

 

2.4.3单元测试
  TestNG和JUnit 相比有了重大的改进,本书示例所有的单元测试统一采用TestNG框架。请确保已经将TestNG依赖包添加到根模块pom.xml文件中。在 chapter2\srcltest测试目录下创建与UserService一致的包结构,即com.smart.service,并创建UserService对应的测试类UserServiceTest,编写如代码所示的测试代码。

@ContextConfiguration("classpath*:/smart-context.xml")
public class UserServiceTest extends AbstractTransactionalTestNGSpringContextTests {

    @Autowired
    private UserService userService;

    @Test
    public void testHasMatchUser() {
        boolean b1 = userService.hasMatchUser("admin", "123456");
        boolean b2 = userService.hasMatchUser("admin", "1111");
        assertTrue(b1);
        assertTrue(!b2);
    }

    @Test
     public void testFindUserByUserName()throws Exception{
        for(int i =0; i< 100;i++) {
            User user = userService.findUserByUserName("admin");
            assertEquals(user.getUserName(), "admin");
        }

    }


    @Test
    public void testAddLoginLog() {
        User user = userService.findUserByUserName("admin");
        user.setUserId(1);
        user.setUserName("admin");
        user.setLastIp("192.168.12.7");
        user.setLastVisit(new Date());
        userService.loginSuccess(user);
    }
}

 

  Spring 4.0的测试框架很好地整合了TestNG单元测试框架,示例UserServiceTest通过扩展Spring测试框架提供测试基类 AbstractTransactionalTestNGSpringContextTests来启动测试运行器。      @ContextConfiguration也是Spring 提供的注解,用于指定Spring的配置文件。
  可以使用Spring 的@Autowired 将Spring容器中的Bean注入测试类中。在测试方法前通过TestNG的@Test注解即可将方法标注为测试方法
  在 IDEA 中执行当前测试类,通过右键菜单Run ‘UserServiceTest'来运行该测试用例,以检验业务类方法的正确性。

 

2.5展现层

  业务层和持久层的开发任务已经完成,该是为程序提供界面的时候了。Spring4.0对MVC进行了全面增强,支持跨域注解@CrossOrigin 配置,Groovy Web集成,Gson、Jackson、Protobuf的 HttpMessageConverter消息转换器等,Spring MVC的功能更加丰富、强大(读者将在第18章学习到Spring MVC的详细内容)。

2.5.1配置Spring MVC框架
  首先需要对web.xml文件进行配置,以便Web容器启动时能够自动启动Spring容器,如代码所示。

  

 

  

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
    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_2_5.xsd">
  <!-- 1--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:smart-context.xml</param-value> </context-param>

  <!-- 2--> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>   
  
  <!-- 3 --> <servlet> <servlet-name>smart</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>3</load-on-startup> </servlet>   <!-- 4 --> <servlet-mapping> <servlet-name>smart</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>

  然后通过Web容器上下文参数指定Spring 配置文件的地址,如①所示。多个配置文件可用逗号或空格分隔,建议采用逗号分隔的方式。然后在②处指定Spring所提供的ContextLoaderListener的Web容器监听器,该监听器在 Web容器启动时自动运行,它会根据contextConfigLocation Web容器参数获取 Spring配置文件,并启动Spring容器。注意,需要将log4J.propertis日志配置文件放置在类路径下,以便日志引擎自动生效。

  最后需要配置Spring MVC 相关的信息。Spring MVC像 Struts一样,也通过一个Servlet来截获URL 请求,然后再进行相关的处理.

  在3处声明了一个 Servlet, Spring MVC 也拥有一个Spring配置文件(稍后将涉及),该配置文件的文件名和此处定义的Servlet名有一个契约,即使用<Servlet 名>-servlet.xml的形式。在这里,Servlet名为smart,则在/WEB-INF目录下必须提供一个名为smart-servlet.xml 的Spring MVC配置文件,但这个配置文件无须通过web.xml的contextConfigLocation上下文参数进行声明,因为Spring MVC 的 Servlet会自动将smart-servlet.xml文件和Spring 的其他配置文件(smart-dao.xml、smart-service.xml)进行拼装。

  在4处对这个Servlet的URL路径映射进行定义,在这里让所有以.html为后缀的URL都能被smart Servlet截获,进而转由Spring MVC框架进行处理。我们知道,在Struts框架中一般将URL后缀配置为*.do,而在 WebWork 中一般配置为*.action。其实,框架本身和URL模式没有任何关系,用户大可使用喜欢的任何后缀。使用.html后缀,一方面,用户不能通过URL直接知道我们采用了何种服务器端技术;另一方面,.html是静态网页的后缀,可以骗过搜索引擎,增加被收录的概率,所以我们推荐采用这种后缀。对于那些真正无须任何动态处理的静态网页,则可以使用.htm后缀加以区分,以避免被框架截获。

  请求被Spring MVC截获后,首先根据请求的URL查找到目标的处理控制器,并将请求参数封装“命令”对象一起传给控制器处理;然后,控制器调用Spring容器中的业务Bean完成业务处理工作并返回结果视图。

 

 

2.5.2处理登录请求
1. POJO控制器类。

  首先需要编写的是LoginController,它负责处理登录请求,完成登录业务,并根据登录成功与否转向欢迎页面或失败页面。

//1
@Controller public class LoginController{ private UserService userService;
  //2 @RequestMapping(value = "/index.html") public String loginPage(){ return "login"; }
  //3 @RequestMapping(value = "/loginCheck.html") public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){ boolean isValidUser = userService.hasMatchUser(loginCommand.getUserName(), loginCommand.getPassword()); if (!isValidUser) { return new ModelAndView("login", "error", "用户名或密码错误。"); } else { User user = userService.findUserByUserName(loginCommand .getUserName()); user.setLastIp(request.getLocalAddr()); user.setLastVisit(new Date()); userService.loginSuccess(user); request.getSession().setAttribute("user", user); return new ModelAndView("main"); } } @Autowired public void setUserService(UserService userService) { this.userService = userService; } }

 

  在①处通过Spring MVC的@Controller注解可以将任何一个POJO的类标注为Spring MVC的控制器,处理HTTP的请求。当然,标注了@Controller 的类首先会是一个Bean,所以可以使用@Autowired进行 Bean的注入。

  一个控制器可以拥有多个处理映射不同HTTP请求路径的方法,通过@RequestMapping指定方法如何映射请求路径,如②和③处所示。

  请求参数会根据参数名称默认契约自动绑定到相应方法的入参中。例如,在③处的loginCheck(HttpServletRequest request,LoginCommand loginCommand)方法中,请求参数会按名称匹配绑定到loginCommand 的入参中。

  请求响应方法可以返回一个ModelAndView,或直接返回一个字符串,Spring MVC会解析之并转向目标响应页面。ModelAndView对象既包括视图信息,又包括视图渲染所需的模型数据信息。在这里用户仅需要了解它代表一张视图即可,在后面的内容中,读者将了解到Spring MVC如何根据这个对象转向真正的页面。前面用到的LoginCommand对象是一个POJO,没有继承特定的父类或实现特定的接口。LoginCommand 类仅包括用户/密码这两个属性(和请求的用户/密码参数名称一样)。

  在代码的②和③处,控制器根据登录处理结果分别返回 ModelAndView("login" , " error", "用户名或密码错误。")和 ModelAndView("main")。ModelAndView的第一个参数代表视图的逻辑名,第二、第三个参数分别为数据模型名称和数据模型对象,数据模型对象将以数据模型名称为参数名放置到request的属性中。

  编写好LoginCommand后,需要在smart-servlet.xml中声明该控制器,扫描Web路径,指定Spring MVC的视图解析器。

<?xml version="1.0" encoding="UTF-8" ?>
<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="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/p ">
    <!-- 扫描web包,应用Spring的注解 -->
    <context:component-scan base-package="com.smart.web"/>

    <!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 -->
    <bean
            class="org.springframework.web.servlet.view.InternalResourceViewResolver"
            p:viewClass="org.springframework.web.servlet.view.JstlView"
            p:prefix="/WEB-INF/jsp/"
            p:suffix=".jsp"/>

</beans>

 

  Spring MVC为视图名到具体视图的映射提供了许多可供选择的方法。在这里,我们使用InternalResourceViewResolver,它通过为视图逻辑名添加前、后缀的方式进行解析。如视图逻辑名为“login”,将解析为/WEB-INFjsp/login.jsp;视图逻辑名为“main”,将解析为/WEB-INF/jsp/main.jsp。

  

2.5.3JSP视图页面

  论坛登录模块共包括两个JSP页面,分别是登录页面login.jsp和欢迎页面main.jsp,我们将在这节完成这两个页面的开发工作。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
    <head>
        <title>小春论坛登录</title>
    </head>
    <body> 
     <!-- 1 --> <c:if test="${!empty error}"> <font color="red"><c:out value="${error}" /></font> </c:if>
     <!-- 2 --> <form action="<c:url value="loginCheck.html"/>" method="post"> 用户名: <input type="text" name="userName"> <br> 密 码: <input type="password" name="password"> <br> <input type="submit" value="登录" /> <input type="reset" value="重置" /> </form> </body> </html>

   login.jsp页面有两个用处,既作为登录页面,又作为登录失败后的响应页面。所以在①处,使用JSTL标签对登录错误返回的信息进行处理。在JSTL标签中引用了error变量,这个变量正是LoginController中返回的ModelAndView("login" , " error", "用户名或密码错误。")对象所声明的error参数。

  login.jsp的登录表单提交到/loginController.html,如②所示。<c:url value="/loginController.html"/>的JSTL标签会在URL 前自动加上应用部署根目录。假设应用部署在网站的 bbt目录下,则<c:url>标签将输出/bbt/loginController.html。通过<c:url>标签很好地解决了开发和应用部署目录不一致的问题。
  由于login.jsp放置在 WEB-INF/jsp目录下,无法直接通过URL进行调用,所以它由LoginController控制类中标注了@RequestMapping(value= "/index.html")的 loginPage()进行转发.

 

标签:登录,Spring,企业应用,MVC,user,第二章,public,Spring4,smart
来源: https://www.cnblogs.com/hellostranger/p/13947360.html