编写Spring第一个程序
Spring是一种开源框架,通过使用它可以大大降低企业应用程序的复杂性。Spring是一种非常完善的框架,几乎涉及WEB开发中的每一层,但是在开发中通常使用Spring开发业务逻辑层,资深的Java EE开发人员会发现Spring框架似曾相识,它其实没有太多的新东西,只是抽象了大量Java EE应用中常用代码,将他们抽象成一个框架,框架中充满了各种设计模式的应用,如单例模式、工厂模式、抽象工厂模式、命令模式、职责链模式、代理模式等,通过使用Spring可以大幅度提高开发效率,课可以保证整个应用具有良好的设计。Spring像是一种中间容器,向上可以与MVC框架无缝整合,向下可以与各种持久层框架无缝整合。
重点内容:
了解Spring架构和为什么使用Spring
下载和安装Spring
在集成开发工具中使用Spring
开发Spring的第一个程序
Spring概述
1.为什么使用Spring
Spring是一种轻量级框架,轻量级是指该框架技术是非入侵式的,用Spring中开发的系统类不需要依赖于Spring中的类,它不需要容器支持。
在没使用Spring之前,如果在业务逻辑层中访问数据访问层,需要在业务逻辑层中创建数据访问层的对象,然后使用该对象调用DAO方法。
使用这种方式使业务逻辑层和数据访问层的耦合性非常高,当数据访问层程序发生改动时,则还需要来改动业务访问层的程序,这样就增加了程序员的工作量。
当Spring出现以后,这种问题就得到了很好的解决。业务逻辑层和数据访问层之间是注入的关系,在业务逻辑层中并不需要创建数据访问层的对象。
2.Spring技术介绍
Spring是一种非常完整的技术,也就是说只使用Spring技术也能开发项目。但是在实际开发中并不这样做,只是让Spring做业务逻辑层,因为Spring的业务处理能力是非常强大的。
在Spring技术中,最经典的就是IoC和AOP技术。其中IoC是指依赖注入,通过使用该技术可以使业务逻辑层和数据访问层解耦合。
Spring的另一个重要技术那就是AOP,通过使用AOP可以很容易的完成切面开发。在后面的学习中将最这两个重要技术进行详细讲解。
Spring开发环境的搭建
1.手动搭建
手动搭建Spring开发环境需要两步,分别是下载和安装。下载Spring可以通过Spring官方网站的下载频道进行下载,下载地址为“http://repo.spring.io/release/org/springframework/spring/”,在其中选择下载Spring Framework。下载后是一个zip文件,将其解压,可以看到在它下面有很多目录。
其中libs是Spring的核心目录,在其中保存了Spring开发生成的JAR文件。docs是帮助目录,其中包含了Spring教程和API文档。
1.手动搭建
Spring的优点就是不依赖于任何服务器和框架,安装Spring时只需要将提供的JAR文件设置到CLASSPATH中。在Spring框架中有很多中JAR文件,它对应着Spring的各种应用。在Spring的研发中,也为初学者考虑到,创建了一个spring-core-版本.jar包,它位于libs目录下,在该包中Spring基本使用类。除了要使用该包外,还要使用到commons-logging.jar包,下载地址:http://commons.apache.org/proper/commons-logging/。
如果使用Spring进行WEB开发,其安装是更简单的,只需要将这两个包复制到WEB应用的WEB-INFlib目录下。
2.使用Eclipse自动搭建
在Eclipse中集成了Spring项目开发,通过它是非常容易的搭建Spring开发环境的,它的步骤如下:
(1)选中要进行Spring开发的项目,右击选择Build Path,Configure Build Path...,将需要的架包加入项目中,如下:
开发Spring的HelloWorld程序
1.开发Spring程序的步骤
Spring的主要功能是对业务逻辑层进行操作,为了简单需要,在该第一个程序中,采用Java项目的方式进行开发,在其中只给出业务逻辑层。
Spring开发是有严格的步骤的,无论项目简单和复杂,都要按照这个步骤进行操作。在开发业务逻辑层时,一定要定义业务接口和业务实现类,这是Spring开发的第一步。然后在Spring配置文件中对业务实现类进行配置。最后是开发客户端程序,进行项目测试。
2.编写业务接口
先来开发业务接口,在该业务接口中定义了SayHello方法。通过该方法创建一个接收姓名信息,然后返回问候语句功能。
package com.tufujie.service; public interface HelloService { public String SayHello(String name); }
3.编写业务实现类
开发完业务接口后,就继续来编写业务实现类。业务实现类要实现业务接口,从而实现业务接口中的抽象方法。
public class HeloServiceImpl implements HelloService { public String SayHello(String name) { return "Hello!!!"+name; } }
4.配置业务实现类
配置业务实现类,需要在项目的src目录下创建一个Spring的配置文件,该文件的名称是可以灵活改变的,通常命名为applicationContext.xml。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="hello" class="com.tufujie.service.HeloServiceImpl"> </bean> </beans>
5.编写客户端进行测试
到目前为止,Spring的程序已经开发完毕,在本小节中就通过一个客户端程序来对Spring的程序进行测试。通过该客户端程序调用业务实现类中的业务方法。
public static void main(String[] args) { BeanFactory factory = new ClassPathXmlAppl icationContext("applicationContext.xml") HelloService helloService=(HelloService) factory.getBean("hello"); String name="Tom"; System.out.println(helloService.SayHello(name)); }
控制反转
控制反转是Spring的核心技术之一,它是英文名称为“Inversion of Control”,所以在很多地方将控制反转缩写为IoC。Spring的很多功能都是基于控制反转技术的。在上一章中学习Spring的第一个程序时,就是一个简单的IoC程序。在本章中将继续学习Spring的这一核心技术。
重点内容:
Spring容器和容器中的Bean
掌握各种依赖注入
Bean特性介绍
ApplicationContext的使用
IoC容器
1.Bean工厂接口
Bean工厂接口在前面的学习中已经见到过,它的全称接口名为“org.springframework.beans.factory.BeanFactory”,该接口程序可以在Spring的核心JAR包spring.jar中找到。在Spring程序中,BeanFactory实际上是IoC的代表者,通过使用该接口对其中的Bean进行管理。
在Spring中,定义了多种Bean工厂接口的实现类,它们的使用是有很大不同的。其中比较常用的就是XmlBeanFactory,使用该类可以XML文件的形式来描述对象和对象之间的关系。在学习第一个Spring程序时,就是以XML文件的形式对业务实现类对象进行配置的。
2.实例化容器
要想使用IoC容器,就需要对容器进行实例化操作,通过实例化后的容器对象就可以访问其中的Bean对象。当使用XML配置文件的方式对Bean对象进行管理时,实例化容器的过程其实也是加载配置文件的过程。实体化容器其实也是加载配置文件的过程实例化容器有多种方式,在本节中就来对这些方式进行一一讲解。
首先来看第一种通过系统路径进行实例化的方式,该方式是和IO流操作非常相似的,在其中使用FileSystemResource类构造函数来指定配置文件的位置。第二种方式是通过ClassPath查找配置文件,该方法的前提是配置文件必须在ClassPath环境配置中。第三种方式是通过上下文实例化,这里要使用到BeanFactory接口的子接口Application Context。
3.多配置文件的使用
当开发的项目很大时,使用一个Spring配置文件是比较复杂的,其中可能具有几千行的配置信息,在其中查找某一个Bean对象的配置是非常困难的。所以在Spring中可以定义多个配置文件,将一类配置放置在一个文件中。
当一个项目中具有多个Spring配置文件时,就会出现一个问题,那就是该如何实例化容器。在Spring中,设计了两种方式。一种是使用ApplicationContext实现类的重载构造函数,将所有配置文件的路径组成字符串数据做为参数,例如:
ApplicationContext context =new ClassPathXmlApplicationContext(new String[]{ "applicationContext.xml","applicationContext-part2.xml"}); BeanFactory factory = context;
4.配置Bean
在学习第一个程序后,已经见过了如何配置Bean。配置Bean是通过使用<bean>标记对其进行配置,其中class属性指定Bean的完整路径。每一个Bean中都有一个或者多个id属性,id是Bean的标识符,在当前IoC容器中必须是唯一的。如果一个Bean有多个id,则将其他id在本质上被认为是别名。
为Bean指定别名在特定环境下是非常必要的,例如我们指定操作老师的DAO的名称为“teacherDAO”,但是在对学生操作时也会使用到该DAO,这时候就可以为它起一个别名为“studentDAO”进行使用。定义别名除了定义多个id属性外,还可以在Bean定义后补充,这里要使用到<alias>标记。例如完成如上功能的代码为:
<alias name=" teacherDAO" alias=" studentDAO "/>
5.使用容器实例化Bean
对容器实例化后,使用容器对象调用getBean方法,在其中使用id属性值做为参数,就可以实例化指定的Bean。例如:
HelloServicehelloService = (HelloService)factory.getBean("hello");
这里使用到了根据Java的反射机制创建一个实际的对象。可以看到我们使用容器实例化Bean是非常简单的,但是在Spring的内部操作是比较复杂的。在Spring中,实例化Bean有三种方式,分别是使用构造器、使用静态工厂方法和使用实例工厂方法。
6.容器常用方法
BeanFactory容器中除了getBean方法中外,还有其他几种常用的方法,这里我们对这些方法进行简单的介绍。
boolean containsBean(String) Object getBean(String, Class) Class getType(String name) boolean isSingleton(String) String[] getAliases(String)
依赖注入
1.Setter方法注入
使用Setter方法进行注入是依赖注入的一种方式。在使用Bean中,定义需要注入属性的Setter方法,然后在Spring配置文件中给出该属性的值,最后在客户端程序中就可以访问这些属性值。先来看一个Bean程序。
<bean id="student" class="com.tufujie.service.Student"> <property name="name"> <value>Tom</value> </property> <property name="age"> <value>21</value> </property> </bean>
2.构造函数注入
虽然在Spring中提倡使用Setter方法的方式进行注入,但是使用构造函数是依赖注入非常重要的方式之一。有些程序员更倾向于使用构造函数的方式进行注入,是因为使用这种方式可以将所有属性一次性注入。
使用构造函数进行注入的方式时,必须在Bean程序中声明有参构造函数,其中的参数就是要注入的属性。看下面的Bean程序代码。
<bean id="teacher" class="com.tufujie.service.Teacher"> <constructor-arg type="java.lang.String"> <value>Jerry</value> </constructor-arg> <constructor-arg type="int"> <value>32</value> </constructor-arg> </bean>
3.注入其他Bean
在前面的学习中,介绍了两种注入的方式,在其中都是通过基本类型和字符串类型进行举例说明的。除此之外,采用将bean中指定属性的值设置为对容器中的另外一个bean的引用的方式,可以在一个Bean中注入其他Bean。该操作是通过<ref>标记对完成,将其放在<constructor-arg>标记对或者<property>标记对中。从这里也可以看出注入其他Bean也是有两种注入方式的,这里我们只以使用Setter方法的方式进行注入。
<bean id="UserDAOImpl" class="com.tufujie.dao.UserDAOImpl"></bean> <bean id="userService" class="com.tufujie.service.UserServiceImpl"> <property name="userDAO"> <ref bean="UserDAOImpl"/> </property> </bean>
4.内部Bean
在Java基本语法中有局部变量的概念,局部变量只能在所在的语句块中使用。同样在Spring中有“局部”Bean,它的标准名称为内部Bean。内部Bean是指注入的Bean只能被它所在的Bean使用,不能再被其他Bean所注入。内部Bean是不需要id属性和name属性的,因为它能做被所在Bean使用,就算设定了id属性和name属性,容器也会将其省略掉。
<bean id="userService" class="com.tufujie.service.UserServiceImpl"> <property name="userDAO"> <bean class="com.tufujie.dao.UserDAOImpl"/> </property> </bean>
5.注入集合
在Java中,集合包括List、Set和Map三种,在Spring除了能够对这三种集合能够注入操作外,还包括Properties映射文件。在Spring配置文件中,使用<list/>、<set/>、<map/>及<props/>标记来对应集合。在这里,我们只给出配置文件代码,读者可以在光盘中查看到Bean代码,在Bean中定义了四种集合变量。
6.空字符串和null值的处理
在Spring中,对空字符串和null值进行注入有不同的。当对空字符串进行注入时,只需要在其中的<value>标记对中不写入任何内容。例如:
<bean class="Teacher"> <property name="name"><value></value></property> </bean>
它相当于Java中的:teacher.setName(“”);
当对null值进行注入时,需要使用<null>标记,例如:
<bean class="Teacher"> <property name="name"><null/></property> </bean>
它相当于Java中的:teacher.setName(null);
7.依赖注入的简写形式
在前面的举例中,读者可能会发现当注入值或者其他Bean时,都要在其中使用到<value>标记或者<ref>标记。Spring为了简化该操作,在<property>、<constructor-arg>和<entry>标记中定义了value属性和ref属性来替代<value>标记和<ref>标记。这里我们仅一使用Setter方法方法注入基本数据类型数据为例,如果使用原<value>标记的方式,形式为:
<property name="name"> <value>Tom</value> </property>
如果使用value属性的方式,则形式为:<property name="name" value="Tom"/>
8.不完全依赖
在前面学习注入其他Bean时,当Bean甲被依赖注入到Bean乙中时,在Bean乙中就可以使用Bean甲进行调用方法。在其中Bean甲是要在Bean乙之前实例化的。
但是实际开发中有这样一种情况,需要Bean甲在Bean乙之前实例化,但是在Bean乙中是不需要使用Bean甲的,这种情况下就可以使用不完全依赖注入。这种情况在数据库开发中比较常见,例如注册数据驱动必须在所有操作之前完成。
不完全依赖是通过<bean>标记中的depends-on属性完成的,它的属性值就是要在所在Bean之前实例化的Bean。例如:
<bean id="student" class="Student" />
<bean id="teacher" class="Teacher" depends-on=" student "/>
9.自动装配
在Spring中,具有自动装配的方式注入其他Bean,使用该方式可以简化注入Bean的复杂配置。自动装配是通过<bean>标记的autowire属性定义的,该属性可选的属性值有五种,它们各自代表不同的自动装配方式。
◆ no,代表不使用自动装配。
◆ byName ,根据属性名自动装配。
◆ byType, 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。
◆ constructor 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
◆ autodetect 通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
Bean作用域
1.Singleton作用域
Singleton作用域是Bean的默认作用域。当一个Bean的作用域为singleton,时,那么在Spring IoC容器中只会存在一个共享的Bean实例。也就是说所有对Bean的请求,只要id与该Bean定义相匹配,则只会返回bean的同一实例。Bean的单一实例存储在单利缓存中,请求和引用该Bean时,将自动调用该缓存中的对象实例。
因为Bean的默认作用域就是Singleton,所以将Bean定义成Singl eton有三种方式,最简单的就是什么都不定义。除此之外,还有如下两种方式:
<bean id="teacher" class="com.Teacher" scope="singleton"/>
<bean id=" teacher " class="com.Teacher" singleton="true"/>
2.Prototype作用域
Prototype作用域是和Singleton作用域相对的。当一个Bean设置为Prototype作用域时,则该Bean每被请求或者引用一次,都会创建一个Bean实例对象。
将Bean的作用域设定为Prototype有两种方式,是和Singleton作用域设定非常相似的,它有两种方式,如下:
<bean id="teacher" class="com.Teacher" scope="prototype"/>
<bean id=" teacher " class="com.Teacher" singleton="flase"/>
3.Request作用域
request、session和global session的作用域是和前面的两种作用域有很大不同的,它们只能应用在Web的项目中。Web中的容器实例化是和前面学过的实例化是有所不同的,它要使用XmlWebApplicationContext来查找Spring配置文件。
在Web中,每次HTTP请求就对应一个request对象。将Bean的作用域设定为request,就表示该Bean只在当前请求中有效。不同请求中的Bean是互相看不到状态变化的。当处理请求结束时,request作用域的Bean实例也就被销毁。request作用域的Bean的配置如下所示:
<bean id="userDAO" class=" com.tufujie.dao.UserDAOImpl" scope="request"/>
4.Session作用域
在Web中,每次HTTP会话就对应一个Session对象。将Bean的作用域设定为session,就表示该Bean只在当前会话中有效。在用户操作比较多的网站中,例如购物网站中,通常一个用户对应一个Session对象。在这种项目中,通过将用户共用的Bean设置为session作用域,这就可以使一个用户不能看到其他用户的状态。
当一次会话结束时,session作用域的Bean实例就会被销毁。session作用域的Bean的配置如下所示:
<bean id="userDAO" class=" com.tufujie.dao.UserDAOImpl" scope="session"/>
5.global session作用域
global session作用域和Session作用域是非常相似的,它在实际开发中是非常少用的。在global session作用域的bean中被限定为全局portlet Session的生命周期范围内。global session作用域的Bean的配置如下所示:
<bean id="userDAO" class=" com.tufujie.dao.UserDAOImpl" scope="globalSession"/>
6.自定义作用域
在Spring 2.0及以上版本中,Spring的作用域中种类是可以扩展的,也就是说我们不但可以使用前面讲解的五种以定义作用域,还可以进行自定义作用操作。
在Spring框架中,作用域是由Scope接口定义的,它接口的全称接口名为org.springframework.beans.factory.config.Scope,在JAR包可以看到该接口的代码。所有的作用域都是实现Scope接口,同样自定义作用域也要实现该接口,并且要实现该接口中的方法,分别是底层存储机制获取和删除对象方法。
自定义完作用域类后,还需要对其进行配置,然后在其他Bean中才能使用。由于该知识点是比较复杂的,在实际开发中使用的也不是很多,这里就仅对功能进行简单讲解。
面向切面编程
在本章中继续学习Spring的第二大功能,那就是面向切面编程。在前面学习Java时,我们知道Java是一门面向对象的语言,在学习Java之前,读者可能还学习过面向过程的语言,这些都是编程思想。面向切面编程在一定程度上弥补了面向对象编程的不足,面向对象编程是对父类子类这种纵向关系编程,而面向切面编程是在方法的前后进行横向关系编程。面向切面编程的缩写是AOP,在本章中就来对AOP的技术进行详细讲解。
重点内容:
了解面向切面编程概念
通过注解的方式进行面向切面编程
通过配置的方式进行切面编程
面向切面编程简介
1.面向切面编程概念
首先来看切面,如果读者是山西人的话,看到切面的概念,肯定会首先想到刀削面。Spring中的“切面”就是刀削面中的那把刀,使用这把刀可以将一个事物一分为二。Spring中的“切面”就是这个作用,它可以将一个程序分为两部分,并在中间加入自己想做的事,并且它可能不仅仅对一个程序,而且是一个包下的所有程序。
在一个程序使用切面,通常是对程序中的方法进行操作,方法调用和处理异常等时间段就称为连接点。在Spring的面向切面编程中,一个连接点就代表一个方法的执行。在一个连接点中,有分为很多个切入点,例如方法调用前,方法调用后等。在Spring中,执行切面编程是通过拦截器的概念,当运行到某一切入点时,会告诉拦截器,这就是通知的概念。
2.面向切面编程的功能
Spring AOP是使用纯Java编写的,所以在我们的程序中可以无缝使用,并很容易完成对它的扩展。在目前的Spring的面向切面编程中仅支持以方法做为连接点。如果读者向完成成员变量的连接点操作,可以扩展Spring框架。只所在在Spring框架中仅仅做了方法的功能,是因为在实际开发中应用最多的就是它,对于成员变量的操作是非常少的。
在后面的学习中,就重要来学习Spring中使用面向切面编程对程序中的方法进行操作。通过这种操作,是方法的功能更加丰富。
使用注解方式进行AOP开发
1.启动AspectJ的支持
要想在Spring的面向切面开发中使用注解方式,就需要使用到AspectJ组件技术。要使用AspectJ技术首先要导入AspectJ相关的两个JAR,分别是aspectjweaver.jar 和aspectjrt.jar,它们位于lib/aspectj目录下。
除了需要导入AspectJ相关JAR外,还需要在Spring的配置文件,通常是applicationContext.xml文件,在其中加入相应的配置,其代码如下所示。
<aop:aspectj-autoproxy/>
2.声明切面
在完成上一小节的内容的基础上,在Spring中开发Bean程序时,需要在Bean类的前面加入@AspectJ,从而标明该类是Spring中的切面。
@Aspect
public class BooksServiceImpl{
}
开发完Bean后,同样需要在Spring配置文件中进行配置,例如:
<bean id="booksServiceImpl" class="BooksServiceImpl"></bean>
可以看到对声明切面的Bean进行配置时,和普通的Bean是没有任何区别的。
3.声明切入点
切入点决定了连接点关注的内容,使得我们可以控制通知什么时候执行。在前面学习中已经知道面向切面编程仅对方法执行,所以切入点也仅仅是判断哪些方法需要进行面向切面编程。
声明切入点是需要使用@Pointcut注解,在后面给出切入点表达式,定义关注哪些方法的执行。后面还要给出一个切入点名称,它通过一个没有返回值的普通方法来构成。例如:
@Pointcut("execution(* com.tufujie.dao..*.*(..) ")
private void allMethod(){};
其中“@Pointcut”是声明切入点的固定格式。“execution(* com.tufujie.dao..*.*(..)”是切入点表达式,它的使用是AspectJ的核心技术,在后面会对其进行讲解。“allMethod”是切入点的名称。
4.声明通知
声明通知的作用是告诉程序当在切入点的什么时刻执行下面的方法。目前的通知方式有前置通知、返回后通知、抛出异常后通知和后通知。其中前置通知是指在切入点方法运行之前进行通知,从而执行下面的方法,使用的是@Before注解。例如:
@Before("allMethod ()")
private void myBeforeMethod() {
}
其中“@Before”是声明通知的固定格式,“allMethod”是切入点的名称,和声明切入点时相对应。下面的“myBeforeMethod”方法是定义的要执行的方法。
切入点
1.切入点指定者
在进行Spring面向切面编程时,支持在切入点表达式中使用很多种AspectJ切入点指定者,其中使用最多的就是前面使用到的execution,通过它来匹配方法执行的连接点。除了execution切入点指定者外,还包括如下几种比较常用的。
◆ within:通过限定匹配特定类型确定连接点。
◆ this:通过指定类型的实例确定连接点。
◆ target:通过目标对象确定连接点。
◆ args:通过参数确定连接点。
2.合并切入点
在Java中有“&&”、“||”和“!”三种短路逻辑运算符,在切入点表达式中也可以使用这三种逻辑运算符,其中最常用的就是“||”,它表示两边的方法都可以声明切入点,例如:
@Pointcut("execution(* com.tufujie.dao.*.*(..)) || execution(* com.tufujie.service.*.*(..))")
上面的切入点表达式表示不管是dao包下的接口还是service包下的接口内的所有方法都会定义成后面的切入点,从而使它们都能够进行面向切面开发。
3.切入点表达式
在前面的学习中,读者可能对切入点表达式有很大疑惑。在本节中就通过execution切入点指定者来讲解切入点表达式,其语法格式为如下。
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern))
其中“modifiers-pattern”表示访问修饰符,它是可选的。“ret-type-pattern”表示返回类型,它是必须的,当使用“*”时,表示可以是任意返回类型。“declaring-type-pattern”表示全程限定的类型名,它也是可选的。“name-pattern”表示限定的方法名,它是必须的,使用“*”可以匹配全部或者某一种类方法。“param-pattern”表示参数,当为空时表示无参数方法,当为“..”时表示匹配任意数量参数的方法,在参数中也可以指定特定的类型。
通知
1.返回后通知
返回后通知是指当匹配的方法执行retrun语句返回值时进行通知,它使用“@AfterReturning”注解进行声明,基本形式为:
@AfterReturning("allMothod()") //声明通知
public void myAfterReturningMethod(){ //定义通知方法
}
从形式上可以看出,这种返回后通知是和前面学习的前置通知非常相似的。
在使用返回后通知时,在通知方法中经常想要得到方法的返回值,这就需要使用到returning子句。
2.抛出后通知
抛出后通知是指当执行匹配方法抛出异常后进行通知,它使用“@AfterThrowing”注解进行声明,基本形式为:
@AfterThrowing("allMothod()") //声明通知
public void myAfterThrowingMethod(){ //定义通知方法
}
抛出后通知是和返回后通知有很大相似的地方的,在通知方法中也可以得到匹配方法抛出的异常,这里要使用到throwing子句。通过使用throwing子句也可以对匹配方法通过抛出异常进行限制,如果不想限制,可以使用所有异常的父类Throwable为类型。
3.后通知
后通知是指当当匹配的方法执行结束后通知,它使用“@After”注解进行声明。需要注意的是,方法的结束有多种情况,例如正常运行结束、返回值后结束和发生异常,不管是哪种方式的结束都会进行后通知。后通知的基本形式为:
@After("allMothod()") //声明通知
public void myAfterMethod(){ //定义通知方法
}
后通知和前置通知非常相似的,也是非常容易学习的,这里就不在举例讲解。
4.环绕通知
环绕通知是前置通知和后通知的结合体,使用环绕通知可以使一个方法的前后都执行通知方法。并且通过环绕通知可以决定方法什么时候执行,如何执行和是否执行。
环绕同时是使用“@Around”注解声明的,在其中指定切入点名称。在通知方法中必须要有一个ProceedingJoinPoint类型的参数。在通知方法内可以使用ProceedingJoinPoint类型参数调用proceed方法可以导致潜在的连接点方法的执行。proceed方法具有参数为Object对象数组的重载方式,通过该方法可以传入方法执行时候的参数。环绕通知的基本形式如下:
@Around("allMothod()") //声明通知 public void myAfterMethod(ProceedingJoinPoint pjp){ Object retVal = pjp.proceed(); return retVal; }
5.通知顺序
当进行数据库开发时,有这样一种情况,在执行数据库操作执行后,不但要关闭数据库资源,还要关闭数据库。当我们通过切面编程完成该功能时,就要写两个通知方法,这就出现通知顺序的问题,也就是在同一时刻有两个通知方法要执行。
如果两个通知方法在同一个切面中,该通知顺序是非常简单的,根据声明顺序来确定执行顺序,声明在前,则执行通知方法的时候也在前。
如果两个通知方法不在同一切面中,这时不进行设置,则执行顺序是不确定的,这在实际开发中就可能出现问题。在线程中,我们可以通过设置线程的优先级来设置线程执行的优先性。同样通知方法也有优先级,在Spring中可以实现“org.springframework.core.Ordered”接口,通过该接口可以获取和设置通知方法的优先级。
使用配置方式进行AOP开发
1.配置声明切面
在配置方式进行AOP开发中,可以将声明切面的类做为普通的类配置到Spring容器中,从而做为一个Bean进行操作,不过这需要给出相应的schema支持。
声明切面是通过<aop:aspect>标记对来声明的,在其中使用id属性指定切面名称,使用ref属性指定切面类Bean的id名称。例如:
<aop:config> <aop:aspect id="myAspect" ref="aspectJXML"> ... </aop:aspect> </aop:config> <bean id="aspectJXML" class=" com.tufujie.service.AspectJXML"> </bean>
切面类Bean是其他Bean一样,都可以由Spring容器进行管理配置和依赖注入操作。
2.配置声明切入点
使用配置方法进行AOP开发时,声明切入点有两种配置方法,根据作用范围的不同,切入点可以分为全局切入点和局部切入点。声明切入点是通过<aop:pointcut>标记对声明的,当进行全局切入点声明式,要求将<aop:pointcut>标记对直接配置到<aop:config>标记对,这样就可以使多个切面和通知共享该切入点。全局切入点的声明如下:
<aop:config> <aop:pointcut id="allMothod" expression="execution(* com.tufujie.dao.*.*(..))"/> </aop:config>
声明局部切入点时需要将<aop:pointcut>标记对配置到表示切面的<aop:aspect>标记对中,从而在该切面中声明切入点。
3.配置声明通知
通知的种类在前面的学习中已经讲解过,使用不同方式进行AOP开发是不影响通知种类的。这里我们仍然通过前置通知将进行讲解,它需要使用<aop:before>标记对进行配置,它需要配置在切面中,也就是<aop:aspect>标记对中,例如:
<aop:config> <aop:aspect id="myAspect" ref="aspectJXML"> <aop:pointcut id="allMothod" expression="execution(* com.tufujie.dao.*.*(..))"/> <aop:before pointcut-ref="allMothod" method="myBeforeMethod"/> </aop:aspect> </aop:config> <bean id="aspectJXML" class=" com.tufujie.service.AspectJXML"> </bean>
面向切面编程API介绍
1.处理切入点
先来看一下在Spring中是如何处理切入点的。在Spring中定义了切入点模型,该模型的核心处理接口为“org.springframework.aop.
Pointcut”,使用该接口可以使切入点和通知相分离,从而使一个切入点可以被多个通知使用。切入点模型的Pointcut接口的代码为:
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); }
2.处理通知
在本节中继续学习Spring中是如何处理通知的。在Spring中,每一种通知都是一个Bean。在其中可以设置类共享或者实例共享的参数,从而设定一个通知实例是被所有被通知的对象共享,还是被每一个被通知对象独占。Spring中已经定义了多种通知类型,这个在前面的学习中已经讲解过。这里我们先来看一下最简单的前置通知是如何实现的。
前置通知是通过MethodBeforeAdvice接口完成的,它的代码为:
public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method m, Object[] args, Object target) throws Throwable; }
3.使用AOP的工厂Bean
在学习Spring的依赖注入时,经常会提到依赖注入的Bean工厂。同样在进行面向切面编程时,也有AOP的工厂Bean。通过使用面向切面的工厂Bean,可以对应用的切入点和通知进行管理。处理AOP的工厂Bean的全称接口名为“org.springframework.aop.framework.Proxy
FactoryBean”。
在Spring中大量使用了代理设计模式,使用ProxyFactoryBean接口引用调用getObject方法将产生一个工厂Bean的代理对象,它就是AOP代理。使用AOP代理的一个直接好处就是可以让IoC容器操作切入点和通知。
ProxyFactoryBean类中具有很多属性,这些属性一部分是自定义的,另一大部分是继承来来的。通过使用这些属性可以指定希望代理的目标对象和指定是否使用CGLIB。
在Spring中进行JDBC编程
1.Spring中的数据库操作封装类
Spring中的数据库操作封装类是JdbcTemplate,它位于“org.
springframework.jdbc.core”包下。使用该类可以完成数据库的连接和关闭,从而简化JDBC操作。JdbcTemplate将完成JDBC核心处理流程,例如SQL语句的创建、执行,而把SQL语句的生成以及查询结果的提取工作留给程序员写的应用代码。
创建JdbcTemplate类对象,通常要使用到DataSource接口参数,DataSource接口将在下一小节中进行讲解。JdbcTemplate类和普通的Bean一样,可能直接使用Java代码进行实例化,也可以通过使用Spring的IoC容器进行实例化。
2.数据源接口
数据源接口也就是DataSource接口,通过使用该接口可以创建一个数据库连接。DataSource接口也称为数据源接口,在Spring中有多种创建数据源的方式,例如JNDI、第三方连接池等。在本书中主要来通过Spring配置的方式建立数据源。在前面学习JDBC和Hibernate时已经知道,要想连接数据库,最少要知道数据库的驱动、url、用户名和密码,所以在配置DataSource接口时需要给出这些信息。
<bean id="dataSource" class="org.springframework.jdbc.datas ource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> 。。。。 </bean>
3.执行SQL语句
学习完Spring中的数据库操作封装类和数据源接口,现在就可以结合使用它们进行数据库开发,先来看一下如何进行基本的SQL语句运行。
private JdbcTemplate jt; private DataSource dataSource; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void doExecute() { jt = new JdbcTemplate(dataSource); jt.execute("create table user (id integer, name varchar(100))"); }
4.更新数据库操作
更新数据库操作中包括插入、修改和删除三种三种,这三种操作都是通过update方法完成的。在update方法中给出SQL语句参数,从而完成相应的数据库操作。
private JdbcTemplate jt; private DataSource dataSource; public void setDataSource(DataSource dataSource){ this.dataSource = dataSource; } public void doUpdate() { jt = new JdbcTemplate(dataSource); jt.update("insert into user values (1,'Tom')"); }
5.查询数据库操作
创建数据表,并且向其中插入数据后,就可以通过查询操作将数据表中的数据查询出来。在Spring的封装数据库中查询数据的方法有很多,这里我们使用其中的两个,分别是查询一个数据和查询所有数据。
public String doGetName() { jt = new JdbcTemplate(dataSource); String name=(String)jt.queryForObject("select name from user where id=1", String.class); return name; } public List doGetNames(){ jt = new JdbcTemplate(dataSource); List list=jt.queryForList("select name from user"); return list; }