Spring入门案例 注解方式配置(一)

来源:互联网 发布:淘宝生活用品沱茶代理 编辑:程序博客网 时间:2024/06/06 05:12


1. 案例简单描述

  本例实现一个简单的登录验证模块,登录页面提供一个带用户名/密码的输入表单,用户填写并提交,服务器端检查是否匹配,匹配成功则进入主页面,记录登录日志,信用+5分,否则提示登录失败。


2.环境准备

  案例开发环境:MyEclipse2015+Spring 4.x +SQL Server2008

  2.1 数据库

USE [sampledb]GOCREATE TABLE [dbo].[t_user]([user_id] [int] IDENTITY(1,1) NOT NULL,[user_name] [nvarchar](50) NULL,[credits] [int] NULL,[password] [nvarchar](50) NULL,[last_visit] [datetime] NULL,[last_ip] [nvarchar](50) NULL) ON [PRIMARY]GOCREATE TABLE [dbo].[t_login_log](    [login_log_id] [int] IDENTITY(1,1) NOT NULL,    [user_id] [int] NULL,    [ip] [nvarchar](50) NULL,    [login_datetime] [datetime] NULL) ON [PRIMARY]GO
  一共两张表,用户信息t_user和登录历史记录t_login_log

  2.2 类包划分

  类包以分层的方式进行组织,共划分为4个包,领域对象严格意义上讲属于业务包,但是由于领域对象可能被持久层和展现层共享,所有一般将其单独划分到一个包中。如图所示:

  单元测试的包和程序的类包对应<我这里写了是servicetest.感觉没啥影响,可能是单元测试这样方便知道测试哪个类?具体后面看测试的时候再看了先丢个黑人问号了..??>,但是放在不同的文件夹下,本示例仅对业务层的业务类进行单元测试。


3.持久层

      持久层负责数据的访问和操作,DAO类被上层的业务类调用,这里选用JDBC作为持久层的实现技术。

  3.1 建立领域对象

  领域对象(Domain Object)也成为实体类,它代表业务的状态,一般来说,它属于业务层,但是它贯穿展现层、业务层和持久层,并最终被持久化到数据库中,领域对象不一定等同于数据库表,不过对于简单的应用来说,往往领域对象都拥有对应的数据库表。

  持久层的主要工作是从数据库表中加载数据并实例化领域对象,或将领域对象持久化到数据表中,由于持久层需要用到领域对象,所以我们将本属于业务层的领域对象提前到持久层来。下述共两个领域对象User和LoginLog,分别对应数据库表中的t_user和t_login_log


1. 用户对象领域

package com.baobaotao.domain;import java.io.Serializable;import java.util.Date;public class User implements Serializable{private int userId;private String userName;private String password;private int credits;private String lastIp;private Date lastVisit;     public Date getLastVisit() {        return (Date) lastVisit.clone();     } }
  此处把一些get和set方法省略了,但是有一点注意要注意的是对Date对象进行判断时需要用到clone,书上没有写,这里给加上了~~实现Serializable接口,可以方便序列化,书上注释为<领域对象一般要实现此接口,方便序列化>,同理,另一个领域对象为:

2.登录日志领域

package com.baobaotao.domain;import java.io.Serializable;import java.util.Date;public class LoginLog implements Serializable{private int loginLogId;private int userId;private String ip;private Date loginDate;}
3.UserDao
@Repository  //注解:通过Spring注解定义一个DAOpublic class UserDao {@Autowired  //自动注入JdbcTemplateprivate JdbcTemplate jdbcTemplate;/** * 根据用户名或密码获取匹配的用户数量 * @param userName * @param password * @return 等于1表示正确,0表示错误 */public int getMatchCount(String userName, String password) {String sqlStr = " SELECT count(*) FROM t_user "+ " WHERE user_name =? and password=? ";return jdbcTemplate.queryForObject(sqlStr,new Object[]{userName,password},Integer.class);//在Spring4.x中取消了jdbcTemplate.queryForInt(..)方法,改用jdbcTemplate.queryForObject}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 },                                <span style="font-family:SimSun;">//匿名内部类实现回调</span><span style="font-family:SimSun;">                              </span> 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) {String sqlStr = " UPDATE t_user SET last_visit=?,last_ip=?,credits=? "+ " WHERE user_id =?";jdbcTemplate.update(sqlStr, new Object[] { user.getLastVisit(),user.getLastIp(),user.getCredits(),user.getUserId()});}}
需要注意的点有:

  • 一定要添加注解!!!否则后续配置扫描识别不出来
  • 在Spring4.x中取消了jdbcTemplate.queryForInt(..)方法,改用jdbcTemplate.queryForObject
  • SQL语句较长的时候通常换行,但是换行时一定要注意前后不能连起来如:"select ...."+"from..",这样from就会和select中的内容连起来,导致数据库语句错误,常用避免错误方式为在每每一行SQL语句的句前和句尾都加一个空格。
  • RowCallbackHandler:查询结果的处理回调接口,该接口有一个processRow(ResultSet rs),负责把查询的结果集从ResultSet装载到类似领域对象的对象实例中

4. LoginLogDao

@Repositorypublic class LoginLogDao {@Autowiredprivate JdbcTemplate jdbcTemplate;public void insertLoginLog(LoginLog loginLog) {String sqlStr = "INSERT INTO t_login_log(user_id,ip,login_datetime) "+ "VALUES(?,?,?)";Object[] args = { loginLog.getUserId(), loginLog.getIp(),          loginLog.getLoginDate() };jdbcTemplate.update(sqlStr, args);}}

  注意事项同上。

4. 业务层

  此例中只有一个业务类,UserService,负责将持久层的UserDao和LogLoginDao组织起来完成认证、登录日志记录等操作。

代码如下:

@Service  //将UserService标注为一个服务层的Beanpublic class UserService {    @Autowired   //注入userDao和loginLogDao两个DAO层的beanprivate UserDao userDao;@Autowiredprivate 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);}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);}}

5. 在Spring中装配DAO和Service

 5.1 装配DAO

  在上述的两个DAO的实现中都没有打开或者释放Connection的代码,那DAO怎么去访问数据库呢?其实是样板式的操作都被JdbcTemplate封装起来了,JdbcTemplate本身需要一个DataSource,这样它就可以从DataSource中获取或返回连接,UserDao和ServiceDao都提供了一个带@Autowired注解的JdbcTemplate变量,所以我们首先必须事先声明一个数据源,然后定义一个JdbcTemplate Bean,通过Spring容器的上下文自动绑定机制进行Bean的注入。

添加引用Spring的多个Schema空间格式定义文件了..

                <!-- 1.扫描类包,将标注Spring注解的类自动转化为Bean,同时完成Bean的注入 --><context:component-scan base-package="/com.baobaotao.dao"></context:component-scan><!-- 2.定义一个使用JDBC实现的数据源 -->               <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" /><property name="url"value="jdbc:sqlserver://localhost:1433;database=sampledb;integratedSecurity=false" /><property name="username" value="sa" /><property name="password" value="..." /></bean><!-- 3. 定义jdbc模板Bean --><bean id="jdbcTem" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" />

DAO Bean的配置大概就是以上3步了。其中

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

2. 定义数据源,此处要写清楚自己数据库的连接字符串,包括数据库名称、登录用户名和密码,此处为SQL server,数据库:sampledb

3. 配置JdbcTemplate Bean,把2中生命的DataSource导入到JdbcTemplate中。

  5.2 装配Service

  事务管理的代码虽然不用出现在程序中,但我们必须以某种方式告诉Spring哪些业务类需要工作于事务环境下以及事务的规则内容等,一遍Spring根据这些信息自动为目标业务类添加事务管理功能。

还是先引入aop以及tx命名空间对应的schema文件,这样就可以使用aop和tx空间下的配置了


然后:

        <!-- a1扫描service包,应用spring注解 --><context:component-scan base-package="/com.baobaotao.service"></context:component-scan><!-- a2.配置事务管理器 --><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource" /><!-- a3.通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务 --><aop:config proxy-target-class="true"><aop:pointcut id="serviceMethod"expression=" execution(* com.baobaotao.service..*(..))" /><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>
  其中a2处扫描类包,以便service类中的Spring注解生效,a2定义一个机遇DataSourceTransactionManager的事务管理器,该管理器负责声明式事务的管理,需要引用DataSource Bean
  这样,关于applicationContext.xml的配置就完成了。

6.单元测试

  File->New ->Other ..->Java ->JUnit->JUnit Test Case

package com.baobaotao.service;import static org.junit.Assert.*;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.baobaotao.domain.User;import com.baobaotao.service.UserService;@RunWith(SpringJUnit4ClassRunner.class)  //基于Junit4的Spring框架@ContextConfiguration(locations={"/applicationContext.xml"}) //启动Spring容器public class TestUserService {//3.注入Spring容器中的Bean@Autowiredprivate UserService userService;@Testpublic void test() {Boolean b1 = userService.hasMatchUser("JY", "123");Boolean b2 = userService.hasMatchUser("ST", "1234");assertTrue(b1);assertTrue(!b2);System.out.println(b1);}@Testpublic void findUserByUserName(){User user = userService.findUserByUserName("JY");assertEquals(user.getUserName(), "JY");}@Testpublic void addLog(){User u = new User();u.setUserName("JY");u.setPassword("123");userService.loginSuccess(u);}}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.>>>>>>

上面的代码实现倒是很简单,下面是错误的记录,

7. 错误记录

1.配置时报错,The prefix "context" for element"context:component-scan" is not bound.  

原因是,配置标签少了东西,点击上面错误可以看到连接


2. 单元测试时出问题:method initializationerror not found ,原因是单元测试包引用不全,具体连接见:

   http://blog.csdn.net/chenleixing/article/details/44257839点击打开链接

3.

Caused by:org.springframework.beans.factory.BeanCreationException:Error creating beanwith name 'loginLogDao' defined in file[G:\myeclipse_workspace\LoginSpring\WebRoot\WEB-INF\classes\com\baobaotao\dao\LoginLogDao.class]:BeanPostProcessor before instantiation of bean failed; nested exception isorg.springframework.beans.factory.BeanCreationException: Error creating beanwith name'org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor#0':Initialization of bean failed; nested exception isjava.lang.NoClassDefFoundError:org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException


此处截取了部分片段,可以看到关键词,这里提示路径为[G:\myeclipse_workspace\LoginSpring\WebRoot\WEB-INF\classes\com\baobaotao\dao\LoginLogDao.class],但实际上应该在src\com\baobaotao\dao\LoginLogDao.class,看到上面配置时写的是<context:component-scan base-package="/com.baobaotao.dao"/>,查了资料后发现其实就是应该这么写,但是我的路径就是不对,最后发现是一个包的问题,

Aspectjtweaver.jar包,这个是配置aop必须的,引入这个包之后便正常了。


4.



配置时缺少空格 

 

缺少空格:

<aop:pointcutid="serviceMethod"

expression="execution(* com.baobaotao.service..*(..))" />     此execution(* com.baobaotao.service..*(..))* 和com之间要有空格!!!


第一次配置的时候就出现了那么多问题,当时真的想哭的心就有了...辛辛苦苦按书上代码来的,醉了,还好都解决了~~继续努力。

0 0