这段时间,看了一些Spring文档和资料,对其有了一个基本的了解。Spring的核心技术由两大部分组成:IoC和AOP,下面我们就分别对它们进行介绍。
1 IoC技术
1.1 预备知识
IoC即Inversion of Control(控制反转)的简写,它是一种设计模式,Spring只不过是实现了该模式。IoC是工厂模式的升华,同时涉及到了反射的概念。所以,在正式介绍IoC之前,首先介绍一下几个基本的概念及技术:接口、工厂模式、反射。
1.1.1 接口
作为面向对象的语言,和C++不同的是,JAVA不支持多重继承,即一个子类只能继承自一个父类,像Son extends FatherA,FatherB 是错误的。于是产生了接口这个概念,即JAVA可以实现多个接口,比如:Son extends FatherA implements FatherB, FatherC是允许的。接口的主要特征包括:
A、其中的方法均没有实体(即只声名未实现),就这一点而言就相当于abstact class,如:
interface ITF
{
void func(int i);
}
上例中,ITF是一个接口,它仅仅声明了一个方法func,而没有具体实现它。
B、一个类欲实现某接口,则必须实现该接口内的所有方法。例如: 字串5
class aclass implements ITF
{
public void func (int i)
{
//在这里你可以不作任何处理,但是必须实现该方法
}
}
C、一个类可以实现多个接口。
D、接口没有实例变量。
E、接口中所有变量都必须被定义为final static。
F、接口可以继承多个接口。
以上只是接口的几个典型特征,与其相关的内容还有很多,如果您想对其有更深的理解,请访问其他相关资源。
1.1.2 工厂模式
工厂模式是最常用的设计模式之一(我对所谓的设计模式没有仔细研究过,该模式是我看到次数最多的一个,所以才这么说,呵呵)。今天看了一些例子,觉得工厂模式这个名字起得相当有创意,这是因为在该模式中,"工厂"、"车间"、"原料"、"加工设备"、"原型产品"、"产品"等概念样样俱全。下面,我们在一个经典例子的基础上,引用上述概念,对工厂模式进行较为形象的解释。
现在我们模拟一个火腿(Ham)加工厂,该工厂可以生产若干种类型的Ham,在该厂中,上述概念可依次描述如下:
字串8
A、"原型产品":即"产品"的模型,该模型定义(但未实现)了各类"产品"必须要具备的功能,各类"产品"对应的"加工设备"可根据该"产品"的特点实现这些功能。所以,在工厂模式中,"原型产品"可以用一个接口来表示:
1. interface
2. {
3. void show();//由Ham"加工设备"生产出的各种Ham将有show()的功能
4. }
interface Ham
{
void show();//由Ham"加工设备"生产出的各种Ham将有show()的功能
}
B、"工厂":即包含了"车间"、加工所需的"原料"、"加工设备"、最终加工的"产品"等实体的复合型实体。在工厂模式中,"工厂"可以用一个类来表示,"车间"可以用该类中的函数来表示,"原料"可以用该函数中的实参来表示,最终的"产品"可以用该函数的返回值来表示。在这里,"车间"远程调用了"加工设备"(即每种"产品"的构造函数)。下面是"工厂"类的具体实现代码:
1. public class
2. //"工厂"类,用于生产不同种类的Ham
3. {
4.
5.
6.
7. public Ham getHam(String HamType) throws
8. //"工厂"中加工Ham的"车间";HamType为该"车间"中的"原料"
9. {
10. if (HamType.equals("HamA"))
11. {
12. return new HamA();//根据不同的"原料"得到不同的"产品",下同
13. }
14. if (HamType.equals("HamB"))
15. {
16. return new
17. }
18. if (HamType.equals("HamC"))
19. 8
20. return new
21. }
22. else
23. throw new
24. }
25. public static void
26. //测试函数
27. {
28. new
29. try
30. {
31. 0]);
32. //实际上调用的是args[0]所对应某类具体产品的show()
33. }
34. catch
35. {
36. ex.printStackTrace();
37. }
38. }
39. }
public class FatoryModule
//"工厂"类,用于生产不同种类的Ham
{
public Ham getHam(String HamType) throws Exception
//"工厂"中加工Ham的"车间";HamType为该"车间"中的"原料"
{
if (HamType.equals("HamA"))
{
return new HamA();//根据不同的"原料"得到不同的"产品",下同
}
if (HamType.equals("HamB"))
{
return new HamB();
}
if (HamType.equals("HamC"))
{ 字串8
return new HamC();
}
else
throw new Exception();
}
public static void main(String[] args)
//测试函数
{
FatoryModule fatorymodule = new FatoryModule();
try
{
Ham myHam = fatorymodule.getHam(args[0]);
myHam.show();//实际上调用的是args[0]所对应某类具体产品的show()
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
C、"产品":即"原型产品"的模型实现实体。"车间"根据具体的"原料"调用对应的"加工设备"来实现"原型产品"定义的所有功能,最后得到所需的"产品"。所以,"产品"可以用一个类来表示,具体代码如下:
1. class HamA implements
2. // "产品"HamA
3. {
4. public void
5. {
6. "You got a HamA.");
7. }
8. }
9. class HamB implements
10. // "产品"HamB
11. {
12. public void
13. {
14. "You got a HamB.");
15. }
16. }
17. class HamC implements
18. // "产品"HamC
19. {
20. public void
21. {
22. "You got a HamC.");
23. }
24. }
class HamA implements Ham
// "产品"HamA
{
public void show()
{
System.out.println("You got a HamA.");
}
}
class HamB implements Ham
// "产品"HamB
{
public void show()
{
System.out.println("You got a HamB.");
}
}
class HamC implements Ham
// "产品"HamC
{
public void show()
{
System.out.println("You got a HamC.");
}
}
小结:总体感觉工厂模式是接口的典型应用。该模式最大特点是方便我们"加工"指定参数条件下的产品。以上述情况为例,假如我们不采用该模式的话,要得到一种产品就必须new一个该产品类的实例,而且在程序中不同产品类的实例变量不能采用同一个变量名。假如有一天我们想更换某一类产品的名字,考虑到程序的可读性,该类产品的实例变量名也需要修改,这个工作量也需会很大(所有涉及到该变量名的地方都需要修改)。从这一点来讲,工厂模式能够减轻我们代码维护量。
1.1.3 反射(主要应用于Spring的四种Bean封装机制中的Bean Wrapper机制)
反射是java的一种非常有趣且有用的特征,而且也是Java被视为动态(或准动态)语言的一个非常关键的性质。该机制允许java程序在运行时通过Reflection APIs(java.lang.reflect.*)取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等等)、superclass(如Object等)、所实现的interfaces(如Cloneable等),以及fields和methods的所有信息,并可在程序运行时改变fields的内容或唤起methods。其实,反射的最重要作用就是允许我们通过类(方法)名调用类(方法)。
更具体的内容请看候捷先生的文章:"Java反射机制",具体网址为:http://www.j2medev.com/Article/Class3/Class7/200604/1995.html。如果您想在短时间内对java的反射机制有一个大体的了解,请看一个网友的一篇博客:"java的reflect机制(反射)",网址为:http://jackleliu.spaces.live.com/blog/cns!426307897fa1054e!125.entry。
1.2 正式进入IoC
IoC实际上是一个高层次的概念,是一种思想或者设计模式,Spring等J2EE框架或者技术只不过是该思想的实现产品。简单的来讲,IoC要求我们利用容器,而不是我们自己编写的代码去控制系统中应用程序之间的关系。即对应用程序之间的关系由原来的手工编码控制转换为利用容器控制。所以,我们把这种思想叫做控制反转(IoC)。
IoC有多种实现方法,其中,Spring是通过一种名为DI(Dependency Injection,即依赖注入)的方法实现的。用夏昕先生的话来讲,"所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。"在Spring中,上面所说的容器一般是一种配置文件(.Xml等类型的文件)。
通过使用DI,当组件之间关系发生变化时,我们只需要修改系统中相关的配置文件,不需要改动我们的代码,这既减少了我们的程序维护量,又提高了我们程序的可重用性。
DI主要有三种实现方式:接口注入、设值注入、构造子注入,下面分别加以介绍:
1.2.1 接口注入
该方式利用接口将调用组件和实现组件相分离。请看下面的代码:
1. public class
2. {
3. private
4. public
5. {
6. clzB = b;
7. return
8. }
9. ………
10. }
public class ClassA
{
private InterfaceB clzB;
public Object doSomething(InterfaceB b)
{
clzB = b;
return clzB.doIt();
}
………
}
上面的实参b的值由容器(spring中的相关配置文件)定义,并在运行期间由该容器提供。相关配置文件的内容我们以后会专门加以介绍。
1.2.2 设值注入
设值注入是spring中应用最广泛的DI模式,与其它模式相比,该模式更为直观、自然。该模式主要通过类的setter方法来完成依赖关系的设置。请看下面的代码:
1. public class
2. {
3. private final
4. private final
5. ………
6. public
7. {
8. this.dataSource = ds;
9. }
10. public
11. {
12. this.message = msg;
13. }
14. public
15. {
16. return this.dataSource;
17. }
18. public
19. {
20. return this.message;
21. }
22. ………
23. }
public class DIByConstructor
{
private final DataSource dataSource;
private final String message;
………
public setDataSource(DataSource ds)
{
this.dataSource = ds;
}
public setmessage(String msg)
{
this.message = msg;
}
public getDataSource()
{
return this.dataSource;
}
public getmessage()
{
return this.message;
}
………
}
上面的实参ds和msg的值由容器(spring中的相关配置文件)定义,并在运行期间由该容器提供。相关配置文件的内容我们以后会专门加以介绍。
1.2.3 构造子注入
构造子注入,即通过构造函数完成依赖关系的设定,请看下面的代码:
1. public class
2. {
3. private final
4. private final
5. public
6. {
7. this.dataSource = ds;
8. this.message = msg;
9. }
10.
11. }
public class DIByConstructor
{
private final DataSource dataSource;
private final String message;
public DIByConstructor(DataSource ds, String msg)
{
this.dataSource = ds;
this.message = msg;
}
}
可以看到,在该模式中,依赖关系是通过类构造函数建立的,容器通过调用类的构造方法,将其所需的依赖关系注入其中。其中,构造函数的实参ds和msg的值由容器(spring中的相关配置文件)定义,并在运行期间由该容器提供。相关配置文件的内容我们以后会专门加以介绍。
1.2.4 几种注入模式的比较
个人觉得,这部分属于理论研究的范畴,所以在此不予介绍。如果您对此感兴趣的话,请参考相关资料。
2 AOP技术
2.1 AOP基本概念
AOP(Aspect-Oriented Programming,面向切面编程)是一种程序设计思想,该思想的主要目标是将系统分为两部分:一部分封装了系统中各组件的通用功能(罗辑或者责任),形成一个切面,该切面被称为应用系统的"横切关注点",此部分包含了与系统核心商业逻辑关系不大的部分;系统的其余部分,即核心商业逻辑部分,被称为系统的"核心关注点",此部分包含了系统中业务处理的主要流程。其中,"横切关注点"经常出现在"核心关注点"的周围,但无论出现在何处,它们的基本功能是相似的,比如权限认证、日志、事务处理等。应用中采用这种思想的好处在于:一方面可以使我们在系统开发过程中的责任分工更为明确(比如,可以让高级工程师负责"横切关注点"的开发,初级工程师负责"核心关注点"的开发,配置工程师负责将以上两部分搭建成一个完整的应用系统);另一方面,清晰的系统结构将使我们以后的系统维护工作变得更为轻松。
字串3
下面是AOP的几个基本概念(理解它们极为重要):
2.1.1 Join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个Join point。
2.1.2 Point cut(切入点):本质上是一个捕获连接点的结构(或称连接点的集合)。在AOP中,可以定义一个Point cut,来捕获相关方法(通知中的逻辑)的调用。
2.1.3 Advice(通知):Point cut的执行代码,它是执行"切面"的具体逻辑。
2.1.4 Aspect(切面):Point cut和Advice的组合,它类似于OOP中定义的一个类,但它代表的更多的是对象间横向的关系。
2.1.5 Introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的(此概念不是很理解)。
2.1.6 Target Object(目标对象):包含Join point的对象,它是被Advice的类,也是商务逻辑对象。这个对象永远是一个被代理的对象。
2.1.7 AOP Proxy(AOP代理):由AOP框架创建的对象,它是真正执行Advice的实体。
2.1.8 Weaving(织入):将Aspec模块与核心商务逻辑(即目标对象)进行关联的过程,比如,将事务处理模块和银行柜员机程序通过配置结合起来,决定什么情况下事务处理模块被通知调用。 字串3
2.2 AOP in Spring2.0应用程序开发基本步骤
在2.0版本以前,Spring所提供的内置AOP支持是基于Java Dynamic Proxy和CGLib实现的,是一种动态的AOP机制。Spring 2.0版本之后,AOP的使用变得更简单,同时功能也更为强大。通过与AspectJ 5的整合, Spring 2.0提供了更完整的AOP。在Spring 2.0中,AOP主要有三种实现方式:基于AspectJ语言的实现方式;基于模式(schema-based)的实现方式;基于 Dynamic Proxy和CGLib(即同以前版本的Spring是兼容的)的实现方式。
下面是一个用Spring2.0的AOP所做的一个例子,该例采用了上面所说的第一种实现方式。限于篇幅,有关AspectJ方面的内容这里不再叙述,您可以参考相关的资料以对其有一个基本的了解。另外,可以通过MyEclipse最新版本实现该例,该版本提供了对Spring2.0的全面支持。
Step1 首先创建一个普通的Java项目:com.longthsoft.learn.spring
Step2 然后编写两个简单的类,代码如下:
1. package
2. public class A //"目标对象"A,AOP框架将自动为该对象创建一个"代理对象"
3. {
4. public void
5. {
6. "Hello, I'm a");
7. }
8. };
package com.longthsoft.learn.spring.models;
public class A //"目标对象"A,AOP框架将自动为该对象创建一个"代理对象"
{
public void sayHello()
{
System.out.println("Hello, I'm a");
}
};
1. package
2. public class B //"目标对象"B,AOP框架将自动为该对象创建一个"代理对象"
3. {
4. public void
5. {
6. "Hi, I'm b"); 字串1
7. }
8. }
package com.longthsoft.learn.spring.models;
public class B //"目标对象"B,AOP框架将自动为该对象创建一个"代理对象"
{
public void sayHi()
{
System.out.println("Hi, I'm b"); 字串1
}
}
Step3 接下来编写AOP中的"切面"类,代码如下:
1. package
2.
3. import
4. import
5. import
6.
7. @Aspect //利用AspectJ语法创建一个"切面"
8. public class
9. {
10. @Pointcut("execution(* com.longthsoft.learn.spring.models.*.say*())")
11. //创建"切入点",该"切入点"捕获了指定项目中相关类的方法("连接点")
12. public void
13.
14. @AfterReturning(pointcut="simplePointcut()")
15. 4
16.
17. //创建"通知",该"通知"绑定了具体的"切入点"
18. public void simpleAdvice() //"切入点"的执行代码,将被"目标对象"的"代理对象"执行
19. {
20. "Merry Christmas");
21. }
22. }
package com.longthsoft.learn.spring;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect //利用AspectJ语法创建一个"切面"
public class SimpleAspect
{
@Pointcut("execution(* com.longthsoft.learn.spring.models.*.say*())")
//创建"切入点",该"切入点"捕获了指定项目中相关类的方法("连接点")
public void simplePointcut() { }
@AfterReturning(pointcut="simplePointcut()")
字串4
//创建"通知",该"通知"绑定了具体的"切入点"
public void simpleAdvice() //"切入点"的执行代码,将被"目标对象"的"代理对象"执行
{
System.out.println("Merry Christmas");
}
}
Step4 编写配置文件(applicationContext.xml),内容如下:
1. <?xml version="1.0" encoding="GBK"?>
2. "http://www.springframework.org/schema/beans"
3. "http://www.w3.org/2001/XMLSchema-instance"
4. "http://www.springframework.org/schema/aop"
5. "http://www.springframework.org/schema/tx"
6. //www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
7. //www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
8. //www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
9.
10. //织入"目标对象"和"切面"之间的关联关系
11.
12. "a" class="com.longthsoft.learn.spring.models.A"
13. "b" class="com.longthsoft.learn.spring.models.B"
14.
15. "simpleAspect" class="com.longthsoft.learn.spring.SimpleAspect"
16. </beans>
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<aop:aspectj-autoproxy /> //织入"目标对象"和"切面"之间的关联关系
<bean id="a" class="com.longthsoft.learn.spring.models.A" />
<bean id="b" class="com.longthsoft.learn.spring.models.B" />
<bean id="simpleAspect" class="com.longthsoft.learn.spring.SimpleAspect" />
</beans>
Step5 编写测试程序,代码如下:
1. package
2.
3. import
4. import
5.
6. import
7. import
8.
9. public final class
10. {
11. public static void
12. {
13. new ClassPathXmlApplicationContext("applicationContext.xml");
14. "a");
15. a.sayHello();
16.
17. "b");
18. b.sayHi();
19. }
20. }
package com.longthsoft.learn.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.longthsoft.learn.spring.models.A;
import com.longthsoft.learn.spring.models.B;
public final class Boot
{
public static void main(String[] args)
{
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = (A) ctx.getBean("a");
a.sayHello();
B b = (B) ctx.getBean("b");
b.sayHi();
}
}
最后运行结果如下:
Hello, I'm a
Merry Christmas
Hi, I'm b
Merry Christmas
3 引用资源
3.1 候捷 《Java反射机制》
3.2 夏昕 《Spring开发指南 V0.8》
3.3 佚名 《AOP技术介绍--(AOP技术基础)》