计应134(实验班) 韩凯丽
Spring是什么?
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而, Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
目的:解决企业应用开发的复杂性
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能
范围:任何Java应用
一、Spring主要核心是:
(1).控制反转(IOC):以前传统的java开发模式中,当需要一个对象时我们,我们会自己使用new或者getInstance等直接或者间接调用构造方法创建一个对象,而在Spring开发模 式中,Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。实例化一个j java对象有三种方式:使用类构造器,使用静态工厂方法,使用实例工厂方法,当使用spring时我们就不需要关心通过何种方式实例化一个对象,spring通过控制反转机制自动 为我们实例化一个对象。
(2).依赖注入(DI):Spring使用java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。
(3).面向切面编程(AOP):在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对 这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
2.在Spring中,所有管理的对象都是JavaBean对象,而BeanFactory和ApplicationContext就是spring框架的两个IOC容器,现在一般使用ApplicationnContext,其不但包含 了BeanFactory的作用,同时还进行更多的扩展。
spring有两种代理方式:
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
二、Spring的主要特性:
(1)、依赖注入(Inversion of Control, IoC)
依赖注入的三种形式:
1、Type1-接口注入(Interface Injection)
它是在一个接口中定义需要注入的信息,并通过接口完成注入。Apache Avalon是一个较为典型的Type1型IOC容器,WebWork框架的IoC容器也是Type1型。
当然,使用接口注入我们首先要定义一个接口,组件的注入将通过这个接口进行。我们还是以用户注册为例,我们开发一个InjectUserDao接口,它的用途是将一个 UserDao实例注入到实现该接口的类中。InjectUserDao接口代码如下:
public interface InjectUserDao {
public void setUserDao(UserDao userDao);
}
UserRegister需要容器为它注入一个UserDao的实例,则它必须实现InjectUserDao接口。UserRegister部分代码如下:
public class UserRegister implements InjectUserDao{
private UserDao userDao = null;//该对象实例由容器注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// UserRegister的其它业务方法
}
同时,我们需要配置InjectUserDao接口和UserDao的实现类。如果使用WebWork框架则配置文件如下:
<component>
<scope>request</scope>
<class>com.dev.spring.simple.MemoryUserDao</class>
<enabler>com.dev.spring.simple.InjectUserDao</enabler>
</component>
这样,当IoC容器判断出UserRegister组件实现了InjectUserDao接口时,它就将MemoryUserDao实例注入到UserRegister组件中。
2、 Type2-设值方法注入(Setter Injection)
在各种类型的依赖注入模式中,设值注入模式在实际开发中得到了最广泛的应用(其中很大一部分得力于Spring框架的影响)。
基于设置模式的依赖注入机制更加直观、也更加自然。前面的用户注册示例,就是典型的设置注入,即通过类的setter方法完成依赖关系的设置。
3、 Type3-构造子注入(Constructor Injection)
构造子注入,即通过构造函数完成依赖关系的设定。将用户注册示例该为构造子注入,UserRegister代码如下:
public class UserRegister {
private UserDao userDao = null;//由容器通过构造函数注入的实例对象
public UserRegister(UserDao userDao){
this.userDao = userDao;
}
//业务方法
}
(2)、面向切面编程(Aspect Oriented Programming, AOP)
IoC技术是通过Java的反射机制以及JavaBean的自省机制实现的
AOP技术是依赖代理模式实现的,JFC中提供了对代理模式的内在支持,Spring也是通过这种技术实现的。
二、反射机制和内省机制做简单介绍
Java的反射机制允许程序在运行时动态加载对象,并且动态地调用其中的方法。
JFC中的java.lang.reflect包便提供了这种支持,主要是通过java.lang.Class、java.lang.reflect.Method、Field、Constuctor等等类完成这项工作的。
例如,如果有下面的方法:
Object myInvoke(String class,
String methodName,
Objec[] args)
public Object myInvoke(String className, String methodName, Object args[]) {
Object results = null;
try {
Class clazz = Class.forName(className);
Method method = null;
for (int i = 0; i < clazz.getMethods().length; i++) {
method = clazz.getMethods()[i];
if(methodName.equals(method.getName())) {
results = method.invoke(clazz.newInstance(), args);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return results;
}
使用时指明类名和方法名:
class SomeToBeInvoke {
public int calculate(int i) {
return i*i;
}
}
ReflectDemo demo = new ReflectDemo();
Object obj = demo.myInvoke("SomeToBeInvoke",
"calculate",
new Object[]{new Integer(6)});
System.out.println(obj);
class类
java.lang.Class代表一个类,可以通过三种方式得到Class的实例:
1.Object.getClass()
2.Class.forName()
3.直接用对象点class,如:a.class
Class的实例代表的是类,不代表类的实例。即Class c = a.class,c是类而不是实例。如果创建实例,可以调用newInstance()方法。
可以通过Class加载类并了解类的内部结构:
1.获取构造函数:
Constructor
getConstructor(Class[]
parameterTypes)
Constructor[] getConstructors()
2.获取方法:
Method
getMethod(String
name, Class[] parameterTypes)
Method[] getMethods()
3.获取属性:
Field
getField(String
name)
Field[] getFields()
4.检查特性:
Class[]
getInterfaces() 、Package getPackage() 、boolean isArray() 、boolean isInterface()
Field类
java.lang.reflect.Field 代表属性,是java.lang.reflect.AccessibleObject 的子类,故可以调用其setAccessible方法访问private类型的属性。
Field中声明了多个getter和setter方法,可以设置属性的值。
Method类
java.lang.reflect.Method 代表类中的方法,它也是java.lang.reflect.AccessibleObject的子类,可以通过setAccessible访问private类型方法。
可以通过调用Object invoke(Object obj, Object[] args) 方法,间接调用对象方法。
Contructor类
java.lang.reflect.Constructor代表构造函数,是java.lang.reflect.AccessibleObject的子类,可以通过setAccessible访问private类型方法。
可以通过Object newInstance(Object[] initargs方法调用构造函数,创建新实例。
JavaBean自省机制:
JavaBean的属性一般都具有getter方法和setter方法,通过这些方法可以更改或读取JavaBean属性。
JavaBean具有的自省机制可以在不知道JavaBean都有哪些属性的情况下,设置它们的值。
例如:<jsp:setProperty property=“*”/>可以设置JavaBean中与参数同名的属性,这是如何实现的呢?
Introspector
自省机制是使用Introspector实现的。
Introspector的方法大部分都是静态的,可以直接调用,如getBeanInfo(Class beanClass) 可以得到一个JavaBean的BeanInfo实例。
BeanInfo实例包含了一个JavaBean类属性和方法的信息。如:
1.BeanDescriptor getBeanDescriptor()
2.MethodDescriptor[] getMethodDescriptors()
3.PropertyDescriptor[] getPropertyDescriptors
PropertyDescriptor
PropertyDescriptor 代表了属性,可以通过它得到属性的getter方法和setter方法。即:
1.Method getReadMethod()
2.Method getWriteMethod()
通常在不知道JavaBean属性时,如果设置JavaBean属性就采用PropertyDescriptor。
PropertyDescriptor(String
propertyName,
Class beanClass)
代理模式
所谓代理模式即是由代理对象接管对象访问,用户在使用时感觉是在原始对象操作,但实际是通过代理对象访问的原始对象。
Java为代理模式提供了内置的支持,这是通过java.lang.reflect.Proxy以及java.lang.reflect. InvocationHandler 实现的。
BeanFactory
BeanFactory是Spring的“心脏”。它就是Spring IoC容器的真面目。Spring使用BeanFactory来实例化、配置和管理Bean。但是,在大多数情况我们并不直接使用BeanFactory,而是使用ApplicationContext。它也是BeanFactory的一个实现,但是它添加了一系列“框架”的特征,
BeanFactory其实是一个接口-org.springframework.beans.factory.BeanFactory,它可以配置和管理几乎所有的Java类。当然,具体的工作是由实现BeanFactory接口的实现类完成。我们最常用的BeanFactory实现是org.springframework.beans.factory.xml.XmlBeanFactory。它从XML文件中读取Bean的定义信息。当BeanFactory被创建时,Spring验证每个Bean的配置。当然,要等Bean创建之后才能设置Bean的属性。单例(Singleton)Bean在启动时就会被BeanFactory实例化,其它的Bean在请求时创建。根据BeanFactory的Java文档(Javadocs)介绍,“Bean定义的持久化方式没有任何的限制:LDAP、RDBMS、XML、属性文件,等等”。现在Spring已提供了XML文件和属性文件的实现。无疑,XML文件是定义Bean的最佳方式。
BeanFactory是初始化Bean和调用它们生命周期方法的“吃苦耐劳者”。注意,BeanFactory只能管理单例(Singleton)Bean的生命周期。它不能管理原型(prototype,非单例)Bean的生命周期。这是因为原型Bean实例被创建之后便被传给了客户端,容器失去了对它们的引用。
BeanFactory管理Bean(组件)的生命周期
Bean的定义
id属性必须是一个有效的XML ID,这意味着它在整个XML文档中必须唯一。它是一个Bean的“终身代号(9527)”。同时你也可以用name属性为Bean定义一个或多个别名(用逗号或空格分开多个别名)。name属性允许出现任意非法的XML字母。例如:
<bean id="userDao" name="userDao*_1, userDao*_2"
class="com.dev.spring.simple.MemoryUserDao"/>。
class属性定义了这个Bean的全限定类名(包名+类名)。Spring能管理几乎所有的Java类。一般情况,这个Java类会有一个默认的构造函数,用set方法设置依赖的属性。
Bean元素除了上面的两个属性之外,还有很多其它属性。说明如下:
<bean
id="beanId"(1)
name="beanName"(2)
class="beanClass"(3)
parent="parentBean"(4)
abstract="true | false"(5)
singleton="true | false"(6)
lazy-init="true | false | default"(7)
autowire="no | byName | byType | constructor | autodetect | default"(8)
dependency-check = "none | objects | simple | all | default"(9)
depends-on="dependsOnBean"(10)
init-method="method"(11)
destroy-method="method"(12)
factory-method="method"(13)
factory-bean="bean">(14)
</bean>
(1)、id: Bean的唯一标识名。它必须是合法的XML ID,在整个XML文档中唯一。
(2)、name: 用来为id创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开。
(3)、class: 用来定义类的全限定名(包名+类名)。只有子类Bean不用定义该属性。
(4)、parent: 子类Bean定义它所引用它的父类Bean。这时前面的class属性失效。子类Bean会继承父类Bean的所有属性,子类Bean也可以覆盖父类Bean的属性。注意:子类Bean和父类Bean是同一个Java类。
(5)、abstract(默认为”false”):用来定义Bean是否为抽象Bean。它表示这个Bean将不会被实例化,一般用于父类Bean,因为父类Bean主要是供子类Bean继承使用。
(6)、singleton(默认为“true”):定义Bean是否是Singleton(单例)。如果设为“true”,则在BeanFactory作用范围内,只维护此Bean的一个实例。如果设为“flase”,Bean将是Prototype(原型)状态,BeanFactory将为每次Bean请求创建一个新的Bean实例。
(7)、lazy-init(默认为“default”):用来定义这个Bean是否实现懒初始化。如果为“true”,它将在BeanFactory启动时初始化所有的Singleton Bean。反之,如果为“false”,它只在Bean请求时才开始创建Singleton Bean。
(8)、autowire(自动装配,默认为“default”):它定义了Bean的自动装载方式。
1、“no”:不使用自动装配功能。
2、“byName”:通过Bean的属性名实现自动装配。
3、“byType”:通过Bean的类型实现自动装配。
4、“constructor”:类似于byType,但它是用于构造函数的参数的自动组装。
5、“autodetect”:通过Bean类的反省机制(introspection)决定是使用“constructor”还是使用“byType”。
(9)、dependency-check(依赖检查,默认为“default”):它用来确保Bean组件通过JavaBean描述的所以依赖关系都得到满足。在与自动装配功能一起使用时,它特别有用。
1、 none:不进行依赖检查。
2、 objects:只做对象间依赖的检查。
3、 simple:只做原始类型和String类型依赖的检查
4、 all:对所有类型的依赖进行检查。它包括了前面的objects和simple。
(10)、depends-on(依赖对象):这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。
(11)、init-method:用来定义Bean的初始化方法,它会在Bean组装之后调用。它必须是一个无参数的方法。
(12)、destroy-method:用来定义Bean的销毁方法,它在BeanFactory关闭时调用。同样,它也必须是一个无参数的方法。它只能应用于singleton Bean。
(13)、factory-method:定义创建该Bean对象的工厂方法。它用于下面的“factory-bean”,表示这个Bean是通过工厂方法创建。此时,“class”属性失效。
(14)、factory-bean:定义创建该Bean对象的工厂类。如果使用了“factory-bean”则“class”属性失效。
配置Bean的属性值和Bean对象的组装
我们可以在Spring的配置文件中直接设置Bean的属性值。例如:你的Bean有一个“maxSize”属性,它表示每页显示数据的最大值,它有一个set方法。代码如下:
private int maxSize;
public void setMaxSize(int maxSize) {
this.maxSize = maxSize;
}
这样,你可以在Bean定义时设置这个属性的值:
<property name="maxSize"><value>20</value></property>
前面介绍了Bean原始类型的属性设置。这种方式已经可以非常有效而便利的参数化应用对象。然而,Bean工厂的真正威力在于:它可以根据bean属性中描述的对象依赖来组装(wire)bean实例。例如:userDao对象的一个属性“sessionFactory”引用了另外一个Bean对象,即userDao对象实例依赖于sessionFactory对象:
<bean id="userDao" class="com.dev.spring.simple.HibernateUserDao">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
…..
</bean>
在这个简单的例子中,使用<ref>元素引用了一个sessionFactory实例。在ref标签中,我们使用了一个“local”属性指定它所引用的Bean对象。除了local属性之外,还有一些其它的属性可以用来指定引用对象。下面列出<ref>元素的所有可用的指定方式:
bean:可以在当前文件中查找依赖对象,也可以在应用上下文(ApplicationContext)中查找其它配置文件的对象。
local:只在当前文件中查找依赖对象。这个属性是一个XML IDREF,所以它指定的对象必须存在,否则它的验证检查会报错。
external:在其它文件中查找依赖对象,而不在当前文件中查找。
总的来说,<ref bean="..."/>和<ref local="..."/>大部分的时候可以通用。“bean”是最灵活的方式,它允许你在多个文件之间共享Bean。而“local”则提供了便利的XML验证。
复杂的属性值
Spring的bean工厂不仅允许用String值和其他bean的引用作为bean组件的属性值,还支持更复杂的值,例如数组、java.util.List、java.util.Map和java.util.Properties。数组、set、list和map中的值不仅可以是String类型,也可以是其他bean的引用;map中的键、Properties的键和值都必须是String类型的;map中的值可以是set、list或者map类型 。
Bean的之前初始化
Bean工厂使用Bean的构造函数创建Bean对象之后,紧接着它会做一件非常重要的工作——Bean的初始化。它会根据配置信息设置Bean的属性和依赖对象,执行相应的初始化方法。
自动装配
一般不推荐在大型的应用系统中使用自动装配。当然,它可以很好的用于小型应用系统。如果一个bean声明被标志为“autowire(自动装配)”,bean工厂会自动将其他的受管对象与其要求的依赖关系进行匹配,从而完成对象的装配——当然,只有当对象关系无歧义时才能完成自动装配。因为不需要明确指定某个协作对象,所以可以带来很多的便利性。
依赖检查
如果你希望Bean严格的设置所有的属性,“dependency-check”(依赖检查)属性将会非常有用。它默认为“none”,不进行依赖检查。“simple”会核对所有的原始类型和String类型的属性。“objects”只做对象间的关联检查(包括集合)。“all”会检查所有的属性,包括“simple”和“objects”。
setXXX()
set方法非常简单,它会给class注入所有依赖的属性。这些属性都必须是在配置文件中使用<property>元素定义,它们可以是原始类型,对象类型(Integer,Long),null值,集合,其它对象的引用。
afterPropertiesSet()
有两种方法可以实现Bean的之前初始化方法。1、使用“init-method”属性,在Spring的配置文件中定义回调方法。下面将会具体描述。2、实现接口InitializingBean并实现它的afterPropertiesSet()方法。接口InitializingBean的代码如下:
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
在JavaBean的所有属性设置完成以后,容器会调用afterPropertiesSet()方法,应用对象可以在这里执行任何定制的初始化操作。这个方法允许抛出最基本的Exception异常,这样可以简化编程模型。
在Spring框架内部,很多bean组件都实现了这些回调接口。但我们的Bean组件最好不要通过这种方式实现生命周期的回调,因为它依赖于Spring的API。无疑,第一种方法是我们的最佳选择。
init-method
init-method的功能和InitializingBean接口一样。它定义了一个Bean的初始化方法,在Bean的所有属性设置完成之后自动调用。这个初始化方法不用依赖于Spring的任何API。它必须是一个无参数的方法,可以抛出Exception。
例如:我们的Bean组件UserManger中定义一个初始化方法init()。这样,我们就可以在Bean定义时指定这个初始化方法:
<bean id=”userManger” class=”com.dev.spring.um.DefaultUserManager”
init-method=”init”>
……
</bean>
setBeanFactory()
Bean的准备就绪(Ready)状态
Bean完成所有的之前初始化之后,就进入了准备就绪(Ready)状态。这就意味着你的应用程序可以取得这些Bean,并根据需要使用他们。
Bean的销毁
在你关闭(或重启)应用程序时,单例(Singleton)Bean可以再次获得生命周期的回调,你可以在这时销毁Bean的一些资源。第一种方法是实现DisposableBean接口并实现它的destroy()方法。更好的方法是用“destroy-method”在Bean的定义时指定销毁方法。
面向接口编程
什么是接口?
¨ 接口定义了行为的协议,这些行为在继承接口的类中实现。
¨ 接口定义了很多方法,但是没有实现它们。类履行接口协议并实现所有定义在接口中的方法。
¨ 接口是一种只有声明没有实现的特殊类。
接口的优点:
¨ Client不必知道其使用对象的具体所属类。
¨ 一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。
¨ 对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。
¨ 松散藕合(loosens coupling)。
¨ 增加了重用的可能性。
接口的缺点:
设计的复杂性略有增加