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


1. 案例简单描述

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


2.环境准备

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

  2.1 数据库

USE [sampledb]
GO

CREATE 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]

GO

CREATE 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注解定义一个DAO
public class UserDao {

	@Autowired  //自动注入JdbcTemplate
	private 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

@Repository
public class LoginLogDao {
	@Autowired
	private 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标注为一个服务层的Bean
public class UserService {
    
	@Autowired   //注入userDao和loginLogDao两个DAO层的bean
	private UserDao userDao;
	
	@Autowired
	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);
	}
	
	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
	@Autowired
	private UserService userService;

	@Test
	public void test() {
		Boolean b1 = userService.hasMatchUser("JY", "123");
		Boolean b2 = userService.hasMatchUser("ST", "1234");

		assertTrue(b1);
		assertTrue(!b2);
		System.out.println(b1);

	}

	@Test
	public void findUserByUserName(){
		User user = userService.findUserByUserName("JY");
		assertEquals(user.getUserName(), "JY");
	}

	@Test
	public 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之间要有空格!!!


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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值