文档库 最新最全的文档下载
当前位置:文档库 › 51CTO下载-大型门户网站是这样炼成+这个只是spring+部分

51CTO下载-大型门户网站是这样炼成+这个只是spring+部分

51CTO下载-大型门户网站是这样炼成+这个只是spring+部分
51CTO下载-大型门户网站是这样炼成+这个只是spring+部分

这个只是spring 部分

6.1.3 Spring 2.5拿手戏--控制反转与依赖注入

人类社会由数以亿计的拥有不同本领的人组成,通过这些人的不同分工协作完成了许多不可思议的惊天杰作,从而呈现出一幅又一幅美妙的生活全景图。我们习惯将这种组合称为团队,团队讲求的是分工与协作,谁也离不开谁。在Java EE的世界里,充满了身怀绝技的Java类,通过这云云Java类的分工协作,造就了五彩缤纷的Java EE 应用王国。

当我们需要别人的帮助才能完成某项工作时,毫不犹豫地就会想到"请",这种"请"无疑是一种盛情的、主动的"请求"。沿用这种处事风格,我们的Java EE应用开发走过了漫长的"请"的岁月。例如:

1./** 负责处理用户管理底层数据访问 */

2.public class UserDao{

3.//保存用户数据

4.public void saveUser(User user){

5.//进行数据的保存工作,省略代码

6. }

7. ......

8.}

9.

10./** 负责处理用户管理业务逻辑 */

11.public class UserService{

12.//主动创建数据访问组件

13. UserDao dao = new UserDao();

14.//处理新增用户业务逻辑

15.public void addUser(String userName,String userPwd){

16. User user = new User();

17. user.setUserName(userName);

18. user.setUserPwd(userPwd);

19.//调用UserDao的saveUser方法进行数据保存

20. dao.saveUser(user);

21. }

22. ......

23.}

24.

25./** 负责处理用户管理业务请求 */

26.public class UserAction{

27.//主动创建业务逻辑处理组件

28. UserService service = new UserService();

29.//处理新增用户请求

30.public void addUser(){

31. String userName = "liubin";

32. String userPwd = "123456";

33.//调用UserService的addUser方法进行业务逻辑处理

34. service.addUser(userName,userPwd);

35. }

36. ......

37.}

上面这种风格(见图6-2)的代码是那么眼熟,又是那么亲切,毕竟用了那么多年,不知是习惯了还是麻木了。在一个类(如UserAction类)中需要用到另一个类(如UserService类),也就是一个类依赖另一个类时,顺手就是一个"new",当应用变得庞大,依赖关系网也随之复杂,为测试与维护带来的困难呈指数上升。

那能不能做到"不请自来"呢?答案是可以的。想象一下,如果有一座工厂专门负责生产Bean实例的话,"new"的工作就可以交给它了,倘若还能"送货上门"的话,自然也就"不请自来"。这就是Spring最初的动机,自Spring呱呱坠地之日起,理想变为现实,从此Java EE应用开发步入了"不请自来"的时代。例如:

1./** 负责处理用户管理底层数据访问 */

2.public class UserDao{

3.//保存用户数据

4.public void saveUser(User user){

5.//进行数据的保存工作,省略代码

6. }

7. ......

8.}

9.

10./** 负责处理用户管理业务逻辑 */

11.public class UserService{

12.//仅声明数据访问组件的引用

13. UserDao dao;

14.//处理新增用户业务逻辑

15.public void addUser(String userName,String userPwd){

16. User user = new User();

17. user.setUserName(userName);

18. user.setUserPwd(userPwd);

19.//调用UserDao的saveUser方法进行数据保存

20. dao.saveUser(user);

21. }

22.//提供一条UserDao对象的注入通道

23.public void setDao(UserDao dao){

24.this.dao=dao;

25. }

26. ......

27.}

28.

29./** 负责生产与分发Bean的小作坊 */

30.public class BeanFactory{

31.//生产一个数据访问组件

32. UserDao dao = new UserDao();

33.//再生产一个业务逻辑处理组件

34. UserService service = new UserService();

35.//将数据访问组件装配到业务逻辑处理组件中

36. service.setDao(dao);

37.//分发业务逻辑处理组件

38.public UserService getUserService(){

39.return service

40. }

41.}

42.

43./** 负责处理用户管理业务请求 */

44.public class UserAction{

45.//实例化Bean小作坊

46. BeanFactory beanFactory=new BeanFactory();

47.//从Bean小作坊中取出业务逻辑处理组件实例

48. UserService service=beanFactory.getUserService();

49.//处理新增用户请求

50.public void addUser(){

51. String userName = "liubin";

52. String userPwd = "123456";

53.//调用UserService的addUser方法进行业务逻辑处理

54. service.addUser(userName,userPwd);

55. }

56. ......

57.}

上述代码中,BeanFactory是我们特意设计的一个Bean小作坊,用于生产、装配与分发Bean实例。大家可以看到,业务控制器UserAction中从Bean小作坊取回的业务逻辑组件是已经完全装配好的成品Bean,在业务逻辑组件UserService中再也看不到往昔"new"的身影了,这种基于Bean容器的设计模式,使控制层、逻辑层及数据层实现充分解耦,如图6-3所示,可以提高可测试性与可维护性

于是很多保守与固执的"老资历"Java EE工程师开始呐喊:"反了!反了!!"。没错,是反了,是控制权发生了反转,应用本身不再负责依赖对象的创建及维护,依赖对象的创建及维护交由外部容器负责。这样控制权就由应用转移到了外部容器,这种控制权的转移就是所谓的控制反转或反转控制。

所谓依赖注入就是指程序在运行期,由外部容器动态地将依赖对象注入到组件中。这种依赖注入的过程就如同生产车间将零件装配到机器上一样,注入的过程其实就是一种装配的过程。

正是基于这种思想,Spring才推出了功能强大的Bean工厂与控制反转IoC容器,在IoC容器里通过简单地装配完成Bean实例之间的依赖注入

6.1.4 何为"面向切面编程AOP"

在传统的编写业务逻辑处理代码时,我们通常会习惯性地做几件事情:

日志记录、事务控制及权限控制等,然后才是编写核心的业务逻辑处理代

码。当代码编写完成回头再看时,不禁发现,扬扬洒洒上百行代码中,真

下面我们以用户管理业务逻辑组件UserService的AOP实现过程(见图6-6)为例,深度剖析一下AOP技术的实现原理。AOP技术是建立在Java语言的反射机制与动态代理机制之上的。业务逻辑组件在运行过程中,AOP容器会动态创建一个代理对象供使用者调用,该代理对象已经按Java EE程序员的意图将切面成功切入到目标方法的连接点上,从而使切面的功能与业务逻辑的功能同时得以执行。从原理上讲,调用者直接调用的其实是AOP容器动态生成的代理对象,再由代理对象调用目标对象完成原始的业务逻辑处理,而代理对象则已经将切面与业务逻辑方法进行了合成。

现将图6-6中涉及到的一些概念解释如下。

切面(Aspect):其实就是共有功能的实现。如日志切面、权限切面、事务切面等。在实际应用中通常是一个存放共有功能实现的普通Java类,之所以能被AOP容器识别成切面,是在配置中指定的。

通知(Advice):是切面的具体实现。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际应用中通常是切面类中的一个方法,具体属于哪类通知,同样是在配置中指定的。

连接点(Joinpoint):就是程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出或字段修改等,但Spring只支持方法级的连接点。

切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。

目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象。这些对象中已经只剩下干干净净的核心业务逻辑代码了,所有的共有功能代码等待AOP容器的切入。

代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。

织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种AOP实现的特殊编译器;发生在类装载期,就要求有一个支持AOP实现的特殊类装载器;只有发生在运行期,则可直接通过Java语言的反射机制与动态代理机制来动态实现。

6.1.6 开始Spring 2.5旅程--Hello World6

在对Spring 2.5有了一个初步的了解之后,下面我们将以Spring 2.5在Java App中的应用SpringHelloWorld项目为例,全面演示一下Spring的基本使用步骤,请大家重点注意一下面向接口编程的实际应用及applicationContext.xml的具体配置。

(1)创建一个"Java Project",命名为"SpringHelloWorld"。

(2)创建一个文件夹"lib"用于存放Spring 2.5框架的Jar包。

(3)将Spring 2.5所需的spring-beans.jar、spring-context.jar、spring-core.jar、commons- attributes-compiler.jar、commons-attributes-api.jar、commons-logging.jar及log4j-1.2.15.jar 复制到lib文件夹下,将这些Jar包加入到项目的Classpath中。

(4)创建以下几个包,用于存放项目的类或接口:test.spring.dao包(存放数据访问接口)、test.spring.dao.impl包(存放数据访问接口实现)、test.spring.service包(存放业务逻辑接口)、test.spring.service.impl包(存放业务逻辑接口实现)、test.spring.action 包(存放业务控制组件)、test.spring.bean包(存放ORM对象)、test.spring.junit包(存放Junit测试用例)。

(5)编写用户ORM类User,代码如下:

1.package test.spring.bean;

2./** 用户持久化类 */

3.public class User {

4.Integer id;//自然ID号

5.String userName; //用户名

6.String userPwd;//用户密码

7.//默认构造方法

8.public User(){}

9.//自定义构造方法

10.public User(Integer id, String userName, String userPwd)

{

11.this.id = id;

https://www.wendangku.net/doc/8712610463.html,erName = userName;

https://www.wendangku.net/doc/8712610463.html,erPwd = userPwd;

14.}

15.//省略属性的get/set方法对

16.}

(6)编写数据访问接口UserDao,代码如下:

1.package test.spring.dao;

2.import https://www.wendangku.net/doc/8712610463.html,er;

3./** 用户管理底层数据访问接口 */

4.public interface UserDao {

5.//处理新增用户业务逻辑

6.public void addUser(User user);

7.//处理装载用户业务逻辑

8.public User loadUser(Integer id);

9.//处理修改用户业务逻辑

10.public void modiUser(User user);

11.//处理删除用户业务逻辑

12.public void delUser(Integer id);

13.}

(7)编写数据访问接口实现类UserDaoImpl,代码如下:

1.package test.spring.dao.impl;

2.import https://www.wendangku.net/doc/8712610463.html,er;

3.import https://www.wendangku.net/doc/8712610463.html,erDao;

4./** 用户管理底层数据访问接口实现 */

5.public class UserDaoImpl implements UserDao {

6.//处理新增用户业务逻辑

7.public void addUser(User user) {

8.//由于该实例未实现数据库访问,省略插入数据库记录的代码

9.System.out.println("用户名为"+user.getUserName()+"的用户新

增成功!");

10.}

11.//处理删除用户业务逻辑

12.public void delUser(Integer id) {

13.//由于该实例未实现数据库访问,省略删除数据库记录的代码

14.System.out.println("ID号为"+id+"的用户删除成功!");

15.}

16.//处理装载用户业务逻辑

17.public User loadUser(Integer id) {

18.//由于该实例未实现数据库访问,省略读取数据库记录的代码

19.return new User(1,"liubin","123456");

20.}

21.//处理修改用户业务逻辑

22.public void modiUser(User user) {

23.//由于该实例未实现数据库访问,省略更新数据库记录的代码

24.System.out.println("ID号为"+user.getId()+"的用户修改成

功!");

25.}

26.}

(8)编写业务逻辑接口UserService,代码如下:

1.package test.spring.service;

2.import https://www.wendangku.net/doc/8712610463.html,er;

3./** 用户管理业务逻辑接口 */

4.public interface UserService {

5.//处理新增用户业务逻辑

6.public void addUser(String userName,String userPwd);

7.//处理装载用户业务逻辑

8.public User loadUser(Integer id);

9.//处理修改用户业务逻辑

10.public void modiUser(Integer id,String userName,String u

serPwd);

11.//处理删除用户业务逻辑

12.public void delUser(Integer id);

13.}

(9)编写业务逻辑接口实现类UserServiceImpl,代码如下:

1.package test.spring.service.impl;

2.import https://www.wendangku.net/doc/8712610463.html,er;

3.import https://www.wendangku.net/doc/8712610463.html,erDao;

4.import https://www.wendangku.net/doc/8712610463.html,erService;

5./** 用户管理业务逻辑接口实现 */

6.public class UserServiceImpl implements UserService {

7.//仅声明数据访问组件的引用

https://www.wendangku.net/doc/8712610463.html,erDao dao;

9.//处理新增用户业务逻辑

10.public void addUser(String userName, String userPwd) {

https://www.wendangku.net/doc/8712610463.html,er user = new User();

https://www.wendangku.net/doc/8712610463.html,er.setUserName(userName);

https://www.wendangku.net/doc/8712610463.html,er.setUserPwd(userPwd);

14.dao.addUser(user);

15.}

16.//处理删除用户业务逻辑

17.public void delUser(Integer id) {

18.dao.delUser(id);

19.}

20.//处理装载用户业务逻辑

21.public User loadUser(Integer id) {

22.return dao.loadUser(id);

23.}

24.//处理修改用户业务逻辑

25.public void modiUser(Integer id, String userName, String

userPwd) {

https://www.wendangku.net/doc/8712610463.html,er user = dao.loadUser(id);

https://www.wendangku.net/doc/8712610463.html,er.setUserName(userName);

https://www.wendangku.net/doc/8712610463.html,er.setUserPwd(userPwd);

29.dao.modiUser(user);

30.}

31.//提供一条UserDao对象的注入通道

32.public void setDao(UserDao dao){

33.this.dao=dao;

34.}

35.}

(10)编写业务控制器UserAction,代码如下:

1.package test.spring.action;

2.import https://www.wendangku.net/doc/8712610463.html,er;

3.import https://www.wendangku.net/doc/8712610463.html,erService;

4./** 用户管理业务控制器 */

5.public class UserAction {

6.//仅声明业务逻辑组件的引用

https://www.wendangku.net/doc/8712610463.html,erService service;

8.//处理新增用户请求

9.public void addUser() {

10.String userName="liubin";

11.String userPwd="123456";

12.service.addUser(userName,userPwd);

13.}

14.//处理删除用户请求

15.public void delUser() {

16.service.delUser(1);

17.}

18.//处理装载用户请求

19.public void loadUser() {

https://www.wendangku.net/doc/8712610463.html,er user = service.loadUser(1);

21.System.out.println("用户名="+user.getUserName());

22.}

23.//处理修改用户请求

24.public void modiUser() {

25.Integer id = 1;

26.String userName="liujunyu";

27.String userPwd="123456";

28.service.modiUser(id, userName, userPwd);

29.}

30.//提供一条UserService对象的注入通道

31.public void setService(UserService service) {

32.this.service = service;

33.}

34.}

(11)创建Spring 2.5的配置文件applicationContext.xml,并在该配置文件中配置数据访问实现类UserDaoImpl、业务逻辑实现类UserServiceImpl、业务控制器UserAction,代码如下:

1.

2.

3.xmlns="https://www.wendangku.net/doc/8712610463.html,/schema/beans"

4.xmlns:xsi="https://www.wendangku.net/doc/8712610463.html,/2001/XMLSchema-instance"

5.xsi:schemaLocation="https://www.wendangku.net/doc/8712610463.html,/schem

a/beans

6.https://www.wendangku.net/doc/8712610463.html,/schema/beans/spring-beans

-2.5.xsd">

7.

8.

>

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

(12)为UserAction创建一个Junit的测试testUserAction,分别对其中的方法进行测试,代码如下:

1.package test.spring.junit;

2.import org.junit.BeforeClass;

3.import org.junit.Test;

4.import org.springframework.context.ApplicationContext;

5.import org.springframework.context.support.

6.ClassPathXmlApplicationContext;

7.import https://www.wendangku.net/doc/8712610463.html,erAction;

8./** 用户管理业务控制器的测试用例 */

9.public class testUserAction {

10.static ApplicationContext cxt;

11.static UserAction userAction;

12.//初始化ApplicationContext容器

13.@BeforeClass

14.public static void setUpBeforeClass() throws Exception {

15.//使用ClassPathXmlApplicationContext方式初始化

ApplicationContext容器

16.cxt = new ClassPathXmlApplicationContext("applicationCon

text.xml");

17.//从Bean工厂容器中获取名为"userAction"的UserAction实例

https://www.wendangku.net/doc/8712610463.html,erAction = (UserAction)cxt.getBean("userAction");

19.}

20.//测试UserAction的AddUser方法

21.@Test

22.public void testAddUser() {

https://www.wendangku.net/doc/8712610463.html,erAction.addUser();

24.}

25.//测试UserAction的DelUser方法

26.@Test

27.public void testDelUser() {

https://www.wendangku.net/doc/8712610463.html,erAction.delUser();

29.}

30.//测试UserAction的LoadUser方法

31.@Test

32.public void testLoadUser() {

https://www.wendangku.net/doc/8712610463.html,erAction.loadUser();

34.}

35.//测试UserAction的ModiUser方法

36.@Test

37.public void testModiUser() {

https://www.wendangku.net/doc/8712610463.html,erAction.modiUser();

39.}

40.}

运行测试用例,控制台打印出预想的提示信息,说明Spring 2.5在SpringHelloWorld项目被成功应用。该实例各组件之间的关系如图6-7所示。

6.2 Spring 2.5核心技术

通过6.1节的学习,大家对Spring 2.5从组成到功能及一些基本概念等应该有了一个初步的了解了。这些功能到底是如何实现的呢?在实际的项目中又该如何应用呢?带着这些疑问,下面我们一起来探究一下Spring 2.5的技术内幕。

6.2.1 Bean工厂之BeanFactory介绍

BeanFactory顾名思义是Bean工厂的意思,采用的是工厂设计模式。Bean工厂的神圣职责就是负责Bean实例的创建、Bean实例之间依赖关系的装配及Bean实例的分发。这是Spring的核心技术之一,作为工厂,"看图生产"是必不可少的,同理,我们必须为Bean工厂提供一份"生产图纸"用于指导生产,这就是前面提到的Spring的配置文件(如applicationContext.xml),Spring并未规定配置文件的命名及数量,起什么名字都行,多个配置文件也可以。Spring容器在实例化时就会装载这些配置文件,并进行"看图生产"。

基于"面向接口编程"的指导思想,BeanFactory被定义为接口,XmlBeanFactory 是BeanFactory接口最常用的实现类。XmlBeanFactory通过装载指定的XML配置文件实例化BeanFactory容器。例如:

1.//通过装载指定的XML配置文件实例化BeanFactory容器

2.Resource res = new FileSystemResource("c:/applicationCon

text.xml");

3.BeanFactory factory = new XmlBeanFactory(res);

4.//从Bean工厂容器中获取名为"userAction"的UserAction实例

https://www.wendangku.net/doc/8712610463.html,erAction = (UserAction)factory.getBean("userAction");

BeanFactory容器实例化之后,我们就可以使用getBean方法从BeanFactory容器中获取装配好的Bean实例了。

Spring开发团队在设计Spring时,考虑到一些受限资源的应用场合,譬如PDA设备上的Java应用等,故特意将BeanFactory容器尽可能地精简,以减少资源的消耗。但对于那些需要国际化支持、Bean事件发布与监听的应用来讲,BeanFactory显然有点力不从心了。于是通过继承BeanFactory接口,又创建了ApplicationContext与WebApplicationContext等子接口,用以满足Java EE应用的开发。

6.2.2 实用的Bean工厂ApplicationContext

ApplicationContext的中文意思是"应用上下文",它继承自BeanFactory接口,除了包含BeanFactory的所有功能之外,在国际化支持、资源访问(如URL和文件)、事件传播等方面进行了良好的支持,被推荐为Java EE应用之首选,可应用在Java APP 与Java Web中。

在ApplicationContext接口的众多实现类中,有3个是我们经常用到的(见表6-1),并且使用这3个实现类也基本能满足我们Java EE应用开发中的绝大部分需求。

表6-1 ApplicationContext接口的常用实现类介绍

这些实现类的主要区别就是装载Spring配置文件实例化ApplicationContext容器的方式不同,在ApplicationContext实例化后,同样通过getBean方法从ApplicationContext容器中获取装配好的Bean实例以供使用。

与BeanFactory不同的是,ApplicationContext容器实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态。而BeanFactory容器实例化后并不会自动实例化Bean,只有当Bean被使用时BeanFactory容器才会对该Bean 进行实例化与依赖关系的装配。

在Java项目中通过ClassPathXmlApplicationContext类手动实例化ApplicationContext容器通常是不二之选。但对于Web项目就不行了,Web项目的启动是由相应的Web服务器负责的,因此,在Web项目中ApplicationContext容器的实例化工作最好交给Web服务器来完成。

Spring为此提供了两种解决方案,一种是基于ContextLoaderListener实现的(此方案只适用于Servlet 2.4及以上规范的Servlet容器)。例如,在web.xml中加入如下代码:

1.

2.

3.contextConfigLocation

4./WEB-INF/applicationContext.xml

e>

5.

6.

7.

8.org.springframework.web.context.

9.ContextLoaderListener

10.

别一种方案则是基于ContextLoaderServlet实现的。例如,在web.xml中加入如下代码:

1.

2.

3.contextConfigLocation

4./WEB-INF/applicationContext.xml

e>

5.

6.

7.

8.context

9.org.springframework.web.context.

10.ContextLoaderServlet

11.1

12.

相关文档