目录
Spring
spring概述
认识spring
Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。
Spring框架主要有7大模块组成,它们提供了企业级开发需要的所有功能,而且每个模块都可以单独使用,也可以和其他模块组合使用,灵活且方便的部署可以使开发的程序更加简洁灵活。图1是Spring的7个模块的部署。
- 核心模块
Spring Core模块是Spring的核心容器,它实现了IoC模式、提供了Spring框架的基础功能。在模块中包含最重要的BeanFactory类是Spring的核心类,负责对JavaBean的配置与管理。它采用Factory模式实现了IoC容器即依赖注入。
- Context模块
Spring Context模块继承BeanFactory(或者说Spring核心)类,并且添加了事件处理、国际化、资源装载、透明装载以及数据校验等功能。它还提供了框架式的Bean的访问方式和很多企业级的功能,如JNDI访问、支持EJB、远程调用、集成模板框架、E-mail和定时任务调度等。
- AOP模块
Spring集成了所有AOP功能。通过事务管理可以使任意Spring管理的对象AOP化。Spring提供了用标准Java语言编写的AOP框架,它的大部分内容都是根据AOP联盟的API开发的。它使应用程序抛开EJB的复杂性,但拥有传统EJB的关键功能。
- DAO模块
DAO模块提供了JDBC的抽象层,简化了数据库厂商的异常错误(不再从SQLException继承大批代码),大幅度减少代码的编写,并且提供了对声明式事务和编程式事务的支持。
- O/R映射模块
Spring ORM模块提供了对现有ORM框架的支持,各种流行的ORM框架已经做的非常成熟,并且拥有大规模的市场(例如Hibernate)。Spring没有必要开发新的ORM工具,但是它对Hibernate提供了完美的整合功能,同时也支持其他ORM工具。
- Web模块
Spring Web模块建立在Spring Context基础之上,它提供了Servlet监听器的Context和Web应用的上下文。对现有的Web框架如JSF、Tapestry、Struts等提供了集成。
MVC模块
Spring Web MVC模块建立在Spring核心功能之上,这使它能拥有Spring框架的所有特性,能够适应多种多视图、模板技术、国际化和验证服务,实现控制逻辑和业务逻辑清晰的分离。Spring 的获取
在开始使用Spring之前,必须先获取Spring工具包。可以在Spring的官方网站下载Spring工具包,官方网址为
注意: 这里用的是Spring-framework-2.5.6 版本的.手上资料是2.5.6的.所以先看着不影响学习. 目前最新版本已经是 4+
spring 的jar包功能简单介绍
注意:不同版本jar包会有所区别 ,这里采用的手上资料2-5-6的版本,以后不再嗷述.
如表所示,除spring.jar文件以外,每个JAR文件都对应一个Spring模块。可以根据需要引入单独的模块,也可以直接使用spring.jar文件应用整个Spring框架。在学习过程中建议选择整个Spring框架。
另外Spring内置了日志组件log4j.jar,所以在正式使用Spring之前需要对log4j进行简单的配置,在项目的src根目录下创建log4j.properties属性文件,具体代码如下:
#输出级别,输出错误信息,输出源为标准输出源stdout
log4j.rootLogger=WARN,stdout
#将stdout输出到控制台中
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
#日志输出的布局类
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#指定日志输出内容的格式
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] -%m %n
使用BeanFactory 管理bean
BeanFactory采用了Java经典的工厂模式,通过从XML配置文件或属性文件(.properties)中读取JavaBean的定义,来实现JavaBean的创建、配置和管理。BeanFactory有很多实现类,其中XmlBeanFactory可以通过流行的XML文件格式读取配置信息来装载JavaBean。
例如:以装载Bean为例:
Resource resource = new ClassPathResource("applicationContext.xml"); //装载配置文件
BeanFactory factory = new XmlBeanFactory(resource);
Test test = (Test) factory.getBean("test"); //获取Bean
在上面的代码中,使用了ClassPathResource读取XML文件(applicationContext.xml)并传参给XmlBeanFactory,applicationContext.xml文件中的代码如下:
<?xml version="16.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="test" class="com.mr.test.Test"/>
</beans>
在标签中通过标签定义JavaBean的名称和类型,在程序代码中利用BeanFactory的getBean()方法获取JavaBean的实例并且向上转为需要的接口类型,这样在容器中就开始了这个JavaBean的生命周期。
BeanFactory在调用getBean()方法之前不会实例化任何对象,只有在需要创建JavaBean的实例对象时,才会为其分配资源空间。这使它更适合于物理资源受限制的应用程序,尤其是内存限制的环境。
Spring中Bean的生命周期包括:实例化JavaBean、初始化JavaBean、使用JavaBean和销毁JavaBean4个阶段。
AppllicationContext的应用
BeanFactory实现了IoC控制,所以它可以称为“IoC容器”,而ApplicationContext扩展了BeanFactory容器并添加了I18N(国际化)、生命周期事件的发布监听等更加强大的功能,使之成为Spring中强大的企业级IoC容器。在这个容器中提供了对其他框架和EJB的集成、远程调用、WebService、任务调度和JNDI等企业服务。在Spring应用中大多采用ApplicationContext容器来开发企业级的程序。
ApplicationContext因为它不仅提供了BeanFactory的所有特性,同时也允许使用更多的声明方式来得到开发人员需要的功能。
ApplicationContext接口有3个实现类,可以实例化其中任何一个类来创建Spring的ApplicationContext容器。
下面分别介绍这3个实现类:
- ClassPathXmlApplicationContext类
ClassPathXmlApplicationContext是ApplicationContext接口的3个实现类之一,它从当前类路径中检索配置文件并装载它来创建容器的实例。具体语法格式如下:
ApplicationContext context=new ClassPathXmlApplicationContext(String configLocation);
其中的configLocation参数指定了Spring配置文件的名称和位置。
- FileSystemXmlApplicationContext类
FileSystemXmlApplicationContext类也是ApplicationContext接口的实现类,它和ClassPathXmlApplicationContext类的区别在于读取Spring配置文件的方式。它不再从类路径中获取配置文件,而是通过参数指定配置文件的位置,可以获取类路径之外的资源。具体语法格式如下:
ApplicationContext context=new FileSystemXmlApplicationContext(String configLocation);
- WebApplicationContext类
WebApplicationContext是Spring的Web应用容器,有两种方法可以在Servlet中使用WebApplicationContext。第一种方法是在Servlet的web.xml文件中配置Spring的ContextLoaderListener监听器;第二种方法同样要修改web.xml配置文件,在配置文件中添加一个Servlet,定义使用Spring的org.springframework.web.context.ContextLoaderServlet类。
说明:
JavaBean在ApplicationContext容器和在BeanFactory容器中的生命周期基本相同,如果在JavaBean中实现了ApplicationContextAware接口,容器会调用JavaBean的setApplicationContext()方法将容器本身注入到JavaBean中,使JavaBean包含容器的应用。
依赖注入
什么是控制反转与依赖注入
Ioc 的英文全称是(“Inversion of control”)即控制反转。它使程序组件或类之间尽量形成一种松耦合的结构,开发者在使用类的实例之前,需要先创建对象的实例。但是IoC将创建实例的任务交给IoC容器,这样开发应用代码时只需要直接使用类的实例,这就是IoC控制反转。通常用一个所谓的好莱坞原则(“Don’t call me. I will call you.”请不要给我打电话,我会打给你。)来比喻这种控制反转的关系。Martin Fowler曾专门写了一篇文章《Inversion of Control Containers and the Dependency Injection pattern》讨论控制反转这个概念,并提出一个更为准确的概念,称为依赖注入(Dependency Injection)。
依赖注入有3种实现类型,Spring 支持后两种.
接口注入
基于接口将调用与实现分离.这种依赖注入方式必须实现容器索规定的接口,使程序代码和容易的API 绑定在一起,这不是理想的依赖注入方式.
Setter 注入
基于JavaBean 的Setter 方法为属性赋值.在实际开发中得到了最广泛的应用(其中很大一部分得力于Spring 框架的影响) .例如:
public class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在上述代码中定义了一个字段属性name 并且使用了Getter 方法和Setter方法,这两个方法可以为字段属性赋值 .
构造器注入
基于构造方法为属性赋值。容器通过调用类的构造方法,将其所需的依赖关系注入其中。例如:
public class User {
private String name;
public User(String name){ //构造器
this.name=name; //为属性赋值
}
}
在上述代码中使用构造方法为属性赋值,这样做的好处是,在实例化类对象的同时就完成了属性的初始化。
控制反转与依赖注入的优点
由于在控制反转的模式下把对象都放在XML文件中定义,所以开发人员实现一个子类将变的更为简单,只需要修改XML文件即可。而且控制反转颠覆了“使用对象之前必须创建”的传统观念,在控制反转模式下开发人员不必再关注类是如何创建的,只需从容器中抓取一个类然后直接调用它即可
Bean 的配置
在Spring中无论使用哪种容器,都需要从配置文件中读取JavaBean的定义信息,再根据定义信息去创建JavaBean的实例对象并注入其依赖的属性。由此可见Spring中所谓的配置,主要是对JavaBean的定义和依赖关系而言,JavaBean的配置也是针对于配置文件进行的。
想要在Spring IoC容器中获取一个Bean,首先要在配置文件中的元素中配置一个子元素,Spring的控制反转机制会跟据元素的具体配置来实例化这个Bean实例。
例如配置一个简单的JavaBean:
<bean id="test" class="com.mr.Test"/>
其中id属性为bean的名称,class属性为对应的类名,这样通过BeanFactory容器的getBean(“test”)方法就可以获取到该类的实例。
Setter 注入
一个简单的JavaBean最明显的规则就是一个私有属性对应Setter和Getter方法,来实现对属性的封装。既然JavaBean有Setter方法来设置Bean的属性,Spring就会有相应的支持。配置文件中的元素可以为JavaBean的Setter方法传参,即通过Setter方法为属性赋值。
通过Spring的赋值为用户JavaBean的属性赋值。
首先创建用户的JavaBean,关键代码如下:
public class User {
private String name; //用户姓名
private Integer age; //年龄
private String sex; //性别
…… //省略的Setter和Getter方法
}
在Spring的配置文件applicationContext.xml中配置该JavaBean,关键代码如下:
<!-- User Bean -->
<bean name="user" class="com.mr.user.User">
<property name="name">
<value>小强</value>
</property>
<property name="age">
<value>26</value>
</property>
<property name="sex">
<value>男</value>
</property>
</bean>
提示:
元素:该标签将为“name”属性赋值,这是一个普通的赋值标签,直接在成对的标签中放入数值或其他赋值标签,Spring会把这个标签提供的属性值注入到指定的JavaBean中。
说明:
如果当JavaBean的某个属性是List集合类型或数组时,需要使用标签为List集合类型或数组的每一个元素赋值。
创建类Manger,其main()方法中的关键代码如下:
Resource resource = new ClassPathResource("applicationContext.xml"); //装载配置文件
BeanFactory factory = new XmlBeanFactory(resource);
User user = (User) factory.getBean("user"); //获取Bean
System.out.println("用户姓名——"+user.getName()); //输出用户的姓名
System.out.println("用户年龄——"+user.getAge()); //输出用户的年龄
System.out.println("用户性别——"+user.getSex()); //输出用户的性别
构造器注入
在类被实例化的时候,它的构造方法被调用并且只能调用一次。所以构造器被常用于类的初始化操作。是元素的子元素。通过元素的子元素可以为构造方法传参。
通过Spring的构造器注入为用户JavaBean的属性赋值。
在用户JavaBean中创建构造方法,代码如下:
public class User {
private String name; //用户姓名
private Integer age; //年龄
private String sex; //性别
//构造方法
public User(String name,Integer age,String sex){
this.name=name;
this.age=age;
this.sex=sex;
}
//输出JavaBean的属性值方法
public void printInfo(){
System.out.println("用户姓名——"+name); //输出用户的姓名
System.out.println("用户年龄——"+age); //输出用户的年龄
System.out.println("用户性别——"+sex); //输出用户的性别
}
}
在Spring 的配置文件applicationContext.xml 中通过元素为JavaBean 的属性赋值,关键代码如下:
<!-- User Bean -->
<bean name="user" class="com.mr.user.User">
<constructor-arg>
<value>小强</value>
</constructor-arg>
<constructor-arg>
<value>26</value>
</constructor-arg>
<constructor-arg>
<value>男</value>
</constructor-arg>
</bean>
容器通过多个标签完成了对构造方法的传参,但是如果标签的赋值顺序与构造方法中参数的顺序或参数类型不同,程序会产生异常。可以使用元素的index属性和type属性解决此类问题。index属性用于指定构造方法的参数索引,指定当前标签为构造方法的哪个参数赋值。type属性:可以指定参数类型以确定要为构造方法的哪个参数赋值,当需要赋值的属性在构造方法中没有相同的类型时,可以使用这个参数。
创建类Manger,其main()方法中的关键代码如下:
Resource resource = new ClassPathResource("applicationContext.xml"); //装载配置文件
BeanFactory factory = new XmlBeanFactory(resource);
User user = (User) factory.getBean("user"); //获取Bean
user.printInfo(); //输出JavaBean的属性值
引用其他的Bean
Spring利用IoC将JavaBean所需要的属性注入其中,不需要编写程序代码来初始化JavaBean的属性,使程序代码整洁、规范化。最主要的是它降低了JavaBean之间的耦合度,Spring开发的项目中的JavaBean不需要修改任何代码就可以应用到其他程序中。在Spring中可以通过配置文件使用元素引用其他JavaBean的实例对象。
将User对象注入到Spring的控制器Manger中,并在控制器中执行User的printInfo()方法。
在控制器Manger中注入User对象,关键代码如下:
upublic class Manger extends AbstractController {
private User user; //注入User对象
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
v protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
user.printInfo(); //执行User中的信息打印方法
return null;
}
}
u控制器AbstractController:AbstractController是Spring中最基本的控制器,所有的Spring控制器都继承了AbstractController控制器,它提供了诸如缓存支持和mimetype设置这样的功能。vhandleRequestInternal()抽象方法:当从AbstractController继承时,需要实现handleRequestInternal()抽象方法,该方法将用来实现自己的逻辑,并返回一个ModelAndView对象,在本例中返回了一个null。
在Spring的配置文件applicationContext.xml中设置JavaBean的注入,关键代码如下:
<!-- 注入JavaBean -->
<bean name="/main.do" class="com.mr.main.Manger">
<property name="user">
<ref local="user"/>
</property>
</bean>
在web.xml文件中配置自动加载applicationContext.xml文件,在项目启动时,Spring的配置信息都将自动加载到程序中,所以在调用JavaBean时不再需要实例化BeanFactory对象。
<!--设置自动加载配置文件-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
匿名内容部JavaBean
编程中经常遇到匿名的内部类,在Spring中该如何利用XML装配呢?其实非常简单,在需要匿名内部类的地方直接用标签定义一个内部类即可,如果要使这个内部类匿名,可以不指定标签的id或name属性。例如下面这段代码:
<!--定义学生匿名内部类-->
<bean id="school" class="School">
<property name="student">
<bean class="Student"/>
</property>
</bean>
代码中定义了匿名的Student类,将这个匿名内部类赋给了School类的实例对象。
Spring AOP 概述
了解AOP
Spring AOP的实现是基于Java的代理机制,从JDK16.3开始就支持代理功能,但是性能成为一个很大问题,为了解决JDK代理性能问题,出现了CGLIB代理机制。它可以生成字节码,所以它的性能会高于JDK代理。Spring支持这两种代理方式。但是,随着JVM(Java虚拟机)的性能的不断提高,这两种代理性能的差距会越来越小。
在学习Spring AOP之前,首先对它的一些术语做一个了解,它们是构成Spring AOP的基本组成部分,下面将介绍Spring AOP术语。
切面(Aspect)
切面是对象操作过程中的截面,如图1所示。
如图1所示,由于平行四边形拦截了程序流程,Spring形象的把它叫切面,所谓“面向切面编程”正是指的这个。以后提到的“切面”是形象的指这个“平行四边行”
实际上“切面”是一段程序代码,这段代码将被“植入”到程序流程中。
l 连接点(Join Point)
对象操作过程中的某个阶段点,如图2所示。
如图2所示,在程序流程上的任意一点,都可以是连接点。
它实际上是对象的一个操作,例如,对象调用某个方法、读写对象的实例或者某个方法抛出了异常等。
l 切入点(Pointcut)
切入点是连接点的集合,如图3所示。
如图3所示,切面与程序流程的“交叉点”便是程序的切入点,确切地说它是“切面注入”到程序中的位置,换句话说,“切面”是通过切入点被“注入”的。在程序中可以有很多个切入点。
l 通知(Advice)
通知是某个切入点被横切后,所采取的处理逻辑。也就说在“切入点”处拦截程序后,通过通知来执行切面,如图4所示。
l 目标对象(Target)
所有被通知的对象(也可以理解为被代理的对象)都是目标对象。目标对象被AOP所关注,它的属性的改变会被关注,它行为的调用也会被关注,它方法传参的变化仍然会被关注。AOP会注意目标对象的变动,随时准备向目标对象“注入切面”。
l 织入(Weaving)
织入是将切面功能应用到目标对象的过程。由代理工厂创建一个代理对象,这个代理对象可以为目标对象执行切面功能。
AOP的织入方式有三种:编译时期(Compile time)织入、类加载时期(Classload time)织入、执行期(Runtime)织入。Spring AOP一般多见于执行期(Runtime)织入。
l 引入(Introduction)
对一个已编译完的类(class),在运行时期,动态的向这个类里加载属性和方法。
AOP 的简单实现
下面将讲解Spring AOP简单实例的实现过程,从而来了解AOP编程的特点。
利用Spring AOP使日志输出与方法分离,让在调用目标方法之前执行日志输出。
对方法做日志输出是常见的基本功能。传统的做法是把输出语句写在方法体的内部,在调用该方法的时候,用输出语句输出信息来记录方法的执行。AOP可以分离与业务无关的代码。日志输出与方法都做些什么是无关的,它主要的目的是记录方法被执行过。现在将利用Spring AOP使日志输出与方法分离,让在调用目标方法之前执行日志输出。
首先创建类Target,它是被代理的目标对象,其中有一个execute()方法,它可以专注与自己的职能,现在使用AOP对execute()方法做日志输出。在执行execute()方法前,做日志输出。目标对象的代码如下:
public class Target {
public void execute(String name){
System.out.println("程序开始执行:" + name);//输出信息
}
}
通知可以拦截目标对象的execute()方法,并执行日志输出。创建通知的代码如下:
public class LoggerExecute implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
before();//执行前置通知
u invocation.proceed();
return null;
}
v private void before() { //前置通知
System.out.println("程序开始执行!");
}
}
u proceed()方法:invocation为MethodInvocation类型,invocation.proceed()用于执行目标对象的execute()方法。v before()方法:before()方法将在invocation.proceed()之前执行,用于输出提示信息。
若想使用AOP的功能必须创建代理。可以用代码创建代理,代码如下:
public class Manger {
//创建代理
创建代理
public static void main(String[] args) {
Target target = new Target(); //创建目标对象
ProxyFactory di=new ProxyFactory();
di.addAdvice(new LoggerExecute());
di.setTarget(target);
Target proxy=(Target)di.getProxy();
proxy.execute(" AOP的简单实现"); //代理执行execute()方法
}
}
Spring 切入点
静态切入点与动态点
静态切入点与动态切入点需要在程序中进行选择使用。
l 静态切入点
静态往往意味着不变,例如一个对象的方法名是固定不变的,无论在程序的任何位置调用,方法名都不会改变。静态切入点可以为对象的方法名。例如在某个对象调用了execute()方法时,这个execute方法就可以是静态切入点。静态切入点需要在配置文件中指定,关键配置如下:
<bean id="pointcutAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="MyAdvisor" /><!-- 指定通知 -->
</property>
<property name="patterns">
<list>
u<value>.*getConn*.</value><!-- 指定所有以getConn开头的方法名都是切入点 -->
v<value>.*closeConn*.</value>
</list>
</property>
</bean>
正则表达式.getConn.:表示所有以getConn开头的方法都是切入点。v 正则表达式.closeConn.:表示所有以closeConn开头的方法都是切入点。
优点:由于静态切入点只在代理创建的时候执行一次,然后将结果缓存起来,下一次被调用的时候直接从缓存中取得即可。所以,在性能上使用静态切入点要远高于动态切入点。静态切入点在第一次织入切面时,首先会计算切入点的位置:它通过反射在程序运行的时候获得调用的方法名,如果这个方法名是定义的切入点,就会织入切面。然后,将第一次计算结果缓存起来,以后就不需要再进行计算了。这样使用静态切入点的程序性能会好很多。
缺陷:虽然使用静态切入点的性能会高一些,但是它也具有一些缺点:当需要通知的目标对象的类型多于一种,而且需要织入的方法很多,使用静态切入点编程会很烦琐。而且使用静态切入不是很灵活、性能降低。这时可以选用动态切入点。
l 动态切入点
动态切入点是相对于静态切入点的。静态切入点只能应用在相对不变的位置,而动态切入点应用在相对变化的位置。例如方法的参数上,由于在程序运行过程中传递的参数是变化的,所以切入点也随之变化,它会根据不同的参数来织入不同的切面。由于每次织入都要重新计算切入点的位置,而且结果不能缓存,所以动态切入点比静态切入点的性能要低的多,但是它能够随着程序中参数的变化而织入不同的切面,所以它要比静态切入点要灵活很多。
在程序中静态切入点和动态切入点可以选择使用,当程序对性能要求很高的时候而且相对注入不是很复杂时可以选用静态切入点,当程序对性能要求不是很高而且注入也比较复杂时,可以使用动态切入点。
深入静态切入点
静态切入点在某个方法名上织入切面的,所以在织入程序代码前,要进行方法名的匹配,判断一下当前正在调用的方法是不是已经定义的静态切入点,如果该方法已经被定义为静态切入点,说明方法匹配成功,织入切面。如果该方法没有被定义为静态切入点,则匹配失败,不织入切面。这个匹配过程是Spring自动进行的,不需要人为编程的干预。
实际上Spring是使用boolean matches(Method,Class)方法来匹配切入点的,利用method.getName()方法反射取得正在运行的方法的名称。在boolean matches(Method,Class)方法中Method是java.lang.reflect.Method类型,用method.getName()是利用反射取得正在运行的方法名。Class是目标对象的类型。该方法在AOP创建代理的时候被调用,并返回结果,true表示将切面织入,false则不织入。下面介绍静态切入点的匹配过程,代码如下:
<!-- 深入静态切入点 -->
<bean id=" pointcutAdvisor "
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="patterns">
<list>
<value>.*execute.*</value><!-- 指定切入点 -->
</list>
</property>
</bean>
以下是Matches()方法匹配成功后的代码:
public bollean matches(Method method,Class targetClass){
return(method.getName().equals("execute")); //匹配切入点成功
}
深入切入点底层
掌握Spring切入点底层将有助于更加深刻地理解切入点。下面将简单讲解Spring切入点底层机制。
Pointcut接口是切入点的定义接口,用它来规定可切入的连接点的属性。通过对此接口的扩展可以处理其他类型的连接点,例如域等(但是这样做很罕见)。切入点接口定义的代码如下:
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
}
使用ClassFilter接口来匹配目标类,代码如下:
public interface ClassFilter {
boolean matches(Class class);
}
可以看到,在ClassFilter接口中定义了matches()方法,意思是与…相匹配,其中class代表被检测的Class实例,该实例是应用切入点的目标对象,如果返回true表示目标对象可以被应用切入点,如果返回false表示目标对象不可以应用切入点。
使用MethodMatcher接口来匹配目标类的方法或方法的参数,代码如下:
public interface MethodMatcher {
boolean matches(Method m,Class targetClass);
boolean isRuntime();
boolean matches(Method m,Class targetClass,Object[] args);
}
Spring支持两种切入点,静态和动态。究竟执行静态切入点还是动态切入点,取决于isRuntime()方法的返回值。在匹配切入点之前,Spring会调用isRuntime(),如果返回false,则执行静态切入点,如果返回true,则执行动态切入点。
Spring 中其他切入点
Spring提供了丰富的切入点,可供大家选择使用,目的是使切面灵活的注入到程序中的位置。例如使用流程切入点,可以根据当前调用堆栈中的类和方法来实施切入。下面列出了Spring常见的切入点,如表1所示。
Aspect 对AOP 的支持
了解Aspect
Aspect是对系统中的对象操作过程中截面逻辑进行模块化封装的AOP概念实体。在通常情况下Aspect可以包含多个切入点和通知。例如,以AspectJ形式定义的Aspect,代码如下:
aspect AjStyleAspect
{
//切入点定义
pointcut query():call(public * get*(...));
pointcut delete():execution(public void delete(...));
...
//通知
before():query(){...}
after returnint:delete(){...}
...
}
AspectJ是Spring框架2.0版本之后增加的新特性,Spring使用了AspectJ 提供的一个库来做切入点(Pointcut)解析和匹配的工作。 但是AOP在运行时仍旧是纯粹的Spring AOP,它并不依赖于AspectJ 的编译器或者织入器,在底层中使用的仍然是Spring2.0之前的实现体系。
使用AspectJ需要在应用程序的classpath中引入两个AspectJ库:aspectjweaver.jar 和aspectjrt.jar,这两个jar包可以在Spring依赖库的lib/aspectj目录下找到。
在Spring的2.0版本之后,可以通过使用@AspectJ的注解并结合POJO的方式来实现Aspect。
Spring 中的Aspect
最初在Spring中没有完全明确的Aspect概念,只是在Spring中的Aspect的实现和特性有所特殊而已。而Advisor就是Spring中的Aspect。
Advisor是切入点的配置器,它能将Adivce(通知)注入程序中的切入点的位置,可以直接编程实现Advisor,也可以通过XML来配置切入点(Pointcut)和Advisor。由于Spring的切入点有多样性,而Advisor是为各种各样的切入点而设计的配置器,因此相应地Advisor也有很多。
在Spring中的Advisor的实现体系中是由两个分支家族构成,分别由PointctuAdvisor家族和IntrodcutionAdvisor家族构成,说是家族因为每个分支下都含有多个类和接口,如图1所示。
DefaultPointcutAdvisor 切入点配置器
DefaultPointcutAdvisor位于org.springframework.aop.support.DefaultPointcutAdvisor包下的默认切入点通知者。它可以把一个通知配给一个切入点。使用之前,它首先要先创建一个切入点和通知。
首先创建一个通知,这个通知可以是自定义的,关键代码如下:
public TestAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation mi) throws Throwable {
Object Val=mi.proceed();
return Val;
}
}
然后创建自定义切入点,Spring提供了很多类型的切入点,可以选择一个继承它并且分别重写matches ()方法和getClassFilter()方法,实现自己定义的切入点。关键代码如下:
public class TestStaticPointcut extends StaticMethodMatcherPointcut {
public boolean matches (Method method Class targetClass){
return (“targetMethod”.equals(method.getName()));
}
public ClassFilter getClassFilter() {
return new ClassFilter() {
public boolean matches(Class clazz) {
return (clazz==targetClass.class);
}
};
}
}
分别创建一个通知和切入点的实例,关键代码如下:
Pointcut pointcut=new TestStaticPointcut (); //创建一个切入点
Advice advice=new TestAdvice (); //创建一个通知
如果使用SpringAOP的切面注入功能,需要创建AOP代理。通过Spring的代理工厂来实现。
Target target =new Target(); //创建一个目标对象的实例
ProxyFactory proxy= new ProxyFactory();
proxy.setTarget(target); //target为目标对象
//前面已经对“advisor”做了配置,现在需要将“advisor”设置在代理工厂里
proxy.setAdivsor(advisor);
Target proxy = (Target) proxy.getProxy();
Proxy.……//此处省略的是代理调用目标对象的方法,目的是实施拦截注入通知
NameMatchMethodPointcutAdvisor 切入点配置器
此配置器位于org.springframework.aop.support. NameMatchMethodPointcutAdvisor包下,是方法名切入点通知者,使用它可以更加简洁地将方法名设定为切入点。关键代码如下:
uNameMatchMethodPointcutAdvisor advice=new NameMatchMethodPointcutAdvisor(new TestAdvice());
advice.addMethodName(“targetMethod1name”);
advice.addMethodName(“targetMethod2name”);
vadvice.addMethodName(“targetMethod3name”);
……//可以继续添加方法的名称
……//省略创建代理,可以参考上一小节创建AOP代理
new TestAdvice():它是一个通知。v advice.addMethodName(“targetMethod3name”).:参数targetMethod3name是一个方法的名称,advice.addMethodName(“targetMethod3name”)表示将targetMethod3name()方法添加为切入点。
当程序调用targetMethod1()方法时,会被执行通知(TestAdvice)。
Spring 持久化
DAO 模式介绍
DAO代表数据访问对象(Data Access Object),它描述了一个应用中DAO的角色,DAO的存在提供了读写数据库中数据的一种方法,把这个功能通过接口提供对外服务,程序的其他模块通过这些接口来访问数据库,这样会有很多好处,首先,由于服务对象不再和特定的接口实现绑定在一起,使得它们易于测试,因为它提供的是一种服务,在不需要连接数据库的条件下就可以进行单元测试,极大地提高了开发效率。其次,通过使用与持久化技术无关的方法访问数据库,在应用程序的设计和使用上都有很大的灵活性,对于整个系统无论是在性能上还是应用上也是一个巨大的飞跃。
DAO的主要目的就是将持久性相关的问题与一般的业务规则和工作流隔离开来,它为定义业务层可以访问的持久性操作引入了一个接口并且隐藏了实现的具体细节,该接口的功能将依赖于采用的持久性技术而改变,但是DAO接口可以基本上保持不变。
DAO属于O/R Mapping技术的一种。在O/R Mapping技术发布之前,开发者需要直接借助于JDBC和SQL来完成与数据库的相互通信,在O/R Mapping技术出现之后,开发者能够使用DAO或其他不同的DAO框架来实现与RDBMS(关系数据库管理系统)的交互。借助于O/R Mapping技术,开发者能够将对象属性映射到数据表的字段、将对象映射到RDBMS中、这些Mapping技术能够为应用自动创建高效的SQL语句等,除此之外,O/R Mapping技术还提供了延迟加载、缓存等高级特征,而DAO是O/R Mapping技术的一种实现,因此,使用DAO能够大量节省程序开发时间,减少代码量和开发的成本。
Spring 的DAO 理念
Spring提供了一套抽象的DAO类,供开发者扩展,这有利于以统一的方式操作各种DAO技术,例如JDO、JDBC等,这些抽象DAO类提供了设置数据源及相关辅助信息的方法,而其中的一些方法同具体DAO技术相关。目前,Spring DAO抽象提供了以下几种类:
JdbcDaoSupport:JDBC DAO抽象类,开发者需要为它设置数据源(DataSource),通过子类,开发者能够获得JdbcTemplate来访问数据库。
HibernateDaoSupport:Hibernate DAO抽象类。开发者需要为它配置Hibernate SessionFactory。通过其子类,开发者能够获得Hibernate实现。
JdoDaoSupport:Spring为JDO提供的DAO抽象类,开发者需要为它配置PersistenceManagerFactory,通过其子类开发者能够获得JdoTemplate。
在使用Spring的DAO框架进行数据库存取的时候,无须接触使用特定的数据库技术,通过一个数据存取接口来操作即可。下面通过一个简单的实例来讲解如何实现Spring中的DAO操作。
在使用Spring的DAO框架进行数据库存取的时候,无须接触使用特定的数据库技术,通过一个数据存取接口来操作即可。下面通过一个简单的实例来讲解如何实现Spring中的DAO操作。
在Spring中利用DAO模式向tb_user表中添加数据。
该实例中DAO模式实现的示意图如图1所示。
定义一个实体类User,然后在类中定义对应数据表字段的属性,关键代码如下:
public class User {
private Integer id; //唯一性标识
private String name; //姓名
private Integer age; //年龄
private String sex; //性别
…… //省略的Setter和Getter方法
}
创建接口UserDAOImpl,并定义用来执行数据添加的insert()方法,其中insert()方法中使用的参数是User实体对象,代码如下:
public interface UserDAOImpl {
public void inserUser(User user); //添加用户信息的方法
}
编写实现这个DAO接口的UserDAO类,并在这个类中实现接口中定义的方法。首先定义一个用于操作数据库的数据源对象DataSource,通过它创建一个数据库连接对象建立与数据库的连接,这个数据源对象在Spring中提供了javax.sql.DataSource接口的实现,只须在Spring的配置文件中进行相关的配置就可以,稍后会讲到关于Spring的配置文件。这个类中实现了接口的抽象方法insert()方法,通过这个方法访问数据库,关键代码如下:
public class UserDAO implements UserDAOImpl {
private DataSource dataSource; //注入DataSource
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
//向数据表tb_user中添加数据
public void inserUser(User user) {
String name = user.getName(); //获取姓名
Integer age = user.getAge(); //获取年龄
String sex = user.getSex(); //获取性别
Connection conn = null; //定义Connection
Statement stmt = null; //定义Statement
try {
conn = dataSource.getConnection(); //获取数据库连接
stmt = conn.createStatement();
stmt.execute("INSERT INTO tb_user (name,age,sex) "
+ "VALUES('"+name+"','" + age + "','" + sex + "')"); //添加数据的SQL语句
} catch (SQLException e) {
e.printStackTrace();
}
…… //省略的代码
}
编写Spring的配置文件applicationContext.xml,在这个配置文件中首先定义一个名称为DataSource的数据源,它是Spring中的DriverManagerDataSource类的实例,然后再配置前面编写完的userDAO类,并且注入它的DataSource属性值,其具体的配置代码如下:
<!-- 配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/db_database16</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>111</value>
</property>
</bean>
<!-- 为UserDAO注入数据源 -->
<bean id="userDAO" class="com.mr.dao.UserDAO">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
创建类Manger,在其main()方法中的关键代码如下:
Resource resource = new ClassPathResource("applicationContext.xml"); //装载配置文件
BeanFactory factory = new XmlBeanFactory(resource);
User user = new User(); //实例化User对象
user.setName("张三"); //设置姓名
user.setAge(new Integer(30)); //设置年龄
user.setSex("男"); //设置性别
UserDAO userDAO = (UserDAO) factory.getBean("userDAO"); //获取UserDAO
userDAO.inserUser(user); //执行添加方法
System.out.println("数据添加成功!!!");
事务应用的管理
Spring中的事务是基于AOP实现的,而Spring的AOP是以方法为单位的,所以Spring的事务属性就是对事务应用到方法上的策略描述。这些属性分为:传播行为、隔离级别、只读和超时属性。
说明:
事务管理在应用程序中起着至关重要的作用,它是一系列任务组成的工作单元,在这个工作单元中,所有的任务必须同时执行,它们只有两种可能的执行结果,要么所有任务全部成功执行、要么全部执行失败。
事务的管理通常分为两种方式,即编程式事务管理和声明式事务管理,在Spring中这两种事务管理方式被实现的更加优秀。
l 编程式事务管理
在Spring中主要有两种编程式事务的实现方法,即使用PlatformTransactionManager接口的事务管理器实现或使用TransactionTemplate实现。虽然二者各有优缺点,但是推荐使用TransactionTemplate实现方式,因为它符合Spring的模板模式。
提示:
TransactionTemplate模板和Spring的其他模板一样,它封装了资源的打开和关闭等常用的重复代码,在编写程序时只须完成需要的业务代码即可。
l 声明式事务管理
Spring的声明式事务不涉及组建依赖关系,它通过AOP实现事务管理,Spring本身就是一个容器,相对EJB容器而言,Spring显得更为轻便小巧。在使用Spring的声明式事务时不须编写任何代码,便可通过实现基于容器的事务管理。Spring提供了一些可供选择的辅助类,这些辅助类简化了传统的数据库操作流程,在一定程度上节省了工作量,提高了编码效率,所以推荐使用声明式事务。
在Spring中常用TransactionProxyFactoryBean完成声明式事务管理。
说明:
使用TransactionProxyFactoryBean需要注入它所依赖的事务管理器,设置代理的目标对象、代理对象的生成方式和事务属性。代理对象是在目标对象上生成的包含事务和AOP切面的新的对象,它可以赋给目标的引用来替代目标对象以支持事务或AOP提供的切面功能。
应用JdbcTemplate 操作数据库
JdbcTemplate类是Spring的核心类之一,可以在org.springframework.jdbc.core包中找到它。JdbcTemplate类在内部已经处理完了数据库资源的建立和释放,并可以避免一些常见的错误,例如关闭连接、抛出异常等。因此,使用JdbcTemplate类简化了编写JDBC时所使用的基础代码。
JdbcTemplate类可以直接通过数据源的引用实例化,然后在服务中使用,也可以通过依赖注入的方式在ApplicationContext中产生并作为JavaBean的引用给服务使用。
JdbcTemplate类运行了核心的JDBC工作流程,例如应用程序要创建和执行Statement对象,只须在代码中提供SQL语句。还有这个类可以执行SQL中的查询、更新或者调用存储过程等操作,同时生成结果集的迭代数据。它还可以捕捉JDBC的异常并将它们转换成org.springframework.dao包中定义的通用的能够提供更多信息的异常体系。
JdbcTemplate类中提供了接口来方便访问和处理数据库中的数据,这些方法提供了基本的选项用于执行查询和更新数据库操作。对于数据查询和更新的方法中,JdbcTemplate类提供了很多重载的方法,提高了程序的灵活性。表1列出了JdbcTemplate方法中常用的数据查询方法。
表1中方法的参数说明:sql:查询条件的语句;requiredType:返回对象的类型;args:查询语句的条件参数。
例001 利用JdbcTemplate向数据表tb_user添加用户信息。
在配置文件applicationContext.xml中,配置JdbcTemplate和数据源,关键代码如下:
<!-- 配置jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref local="dataSource"/>
</property>
</bean>
创建类AddUser,获取JdbcTemplate对象,并利用它的update()方法执行数据库的添加操作,其main()方法中关键代码如下:
DriverManagerDataSource ds = null;
JdbcTemplate jtl = null;
Resource resource = new ClassPathResource("applicationContext.xml"); //获取配置文件
BeanFactory factory = new XmlBeanFactory(resource);
jtl =(JdbcTemplate)factory.getBean("jdbcTemplate"); //获取JdbcTemplate
String sql = "insert into tb_user(name,age,sex) values ('小明','23','男')"; //SQL语句
jtl.update(sql); //执行添加操作
JdbcTemplate类进行数据写入主要是通过update方法,它实现了很多方法的重载特征,在实例中使用了JdbcTemplate类写入数据的常用方法update(String)。
与Hibernate 整合
Spring整合了对Hibernate的设定,并且整合步骤非常简单,Spring中提供了HibenateTemplate类和HibernateDaoSupport类以及相应的子类,使用户在结合Hibernate使用的时候可以简化程序编写的资源,完全可以与JDBC相类似的使用模型,一样简洁方便,同时还提供使用Hiberante时的编程式的事务管理与声明式的事务管理。
众所周知Hibernate的连接、事务管理等是由建立SessionFactory类开始的,SessionFactory在应用程序中通常只存在一个实例,因而SessionFactory底层的DataSource可以使用Spring的IoC注入,之后再注入SessionFactory到依赖的对象之中。
在应用的整个生命周期中,只要保存一个SessionFactory实例就可以了。
在Spring中配置的SessionFactory对象是通过实例化LocalSessionFactoryBean类来完成的。为了让这个SessionFactory可以获取到连接的后台数据库的信息,需要配置一个数据源dataSource,配置方法如下:
<!-- 配置数据源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/db_database16
</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>111</value>
</property>
</bean>
通过一个LocalSessionFactoryBean配置Hibernate,Hibernate本身有很多属性,通过这些属性可以控制它的行为,其中最重要的一个就是mappingResources属性,通过设置该属性中的value值,来指定Hibernate所使用的映射文件,代码如下:
<!-- 定义Hibernate的sessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<!-- 数据库连接方言 -->
<prop key="dialect">org.hibernate.dialect.SQLServerDialect</prop>
<!-- 在控制台输出SQL语句 -->
<prop key="hibernate.show_sql">true</prop>
<!-- 格式化控制台输出的SQL语句 -->
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
<!--Hibernate映射文件 -->
<property name=" mappingResources ">
<list>
<value>com/mr/User.hbm.xml</value>
</list>
</property>
</bean>
配置完成之后,就可以使用Spring中所提供的很多支持Hibernate的类,例如通过HibenateTemplate类和HibernateDaoSupport的子类,完全可以实现Hibernate的大部分功能,这给实际项目的编写带来了很大的方便。
Spring 基于注解的配置
可以去w3cschool 看或者百度谷歌 http://www.w3cschool.cn/wkspring/