Spring
一:Ioc
1含义:为解决企业应用开发的复杂性而创建的开源框架,用基本的javaBean来完成EJB的事情 从大小和开销方向spring都是轻量级的
2,用途
-
- Ioc容器可以将对象之间的依赖关系交由spring管理,进行控制
- AOP:方便进行面向切面的编程,是oop的扩展,想加什么功能直接加
- 能够集成各种优秀的框架,struts hibernate等
3,spring 组成内容
4准备配置工作
①下载SpringFramework的最新版本,并解压缩到指定目录。
在IDE中新建一个项目,并将Spring.jar将其相关类库加入项目
② 配置文件 bean.xml
③在classpath创建日志输出文件。log4j.properties
④org.springframework.beans及org.springframework.context包是Spring IoC容器的基础
5 Spring 基础语义
1)IoC (Inversion of Control)=DI (Dependency Injection)控制反转和依赖注入
它是一种基于接口的编程,bean由容器创建在需要的时候拿来用即可,主要是采用反射来实现,其核心组建就是BeanFactory 但实际开发常用XmlBeanFactory
2)依赖注入的几种实现类型
Type1设值注入:通过类的setter方法完成依赖关系的设置,就是给bean类中属性加set方法
Type3 构造子注入:即通过构造函数完成依赖关系的设
public class DIByConstructor {
private final DataSource dataSource;
private final String message;
public DIByConstructor(DataSource ds, String msg) {
this.dataSource = ds;
this.message = msg;
}}
3)几种依赖注入模式的对比总结
Type2 设值注入的优势
1. 对于习惯了传统JavaBean开发的程序员而言,通过setter方法设定依赖关系显得更加直
观,更加自然。
2. 如果依赖关系(或继承关系)较为复杂,那么Type3模式的构造函数也会相当庞大(我们需
要在构造函数中设定所有依赖关系),此时Type2模式往往更为简洁。
3. 对于某些第三方类库而言,可能要求我们的组件必须提供一个默认的构造函数(如Struts
中的Action),此时Type3类型的依赖注入机制就体现出其局限性,难以完成我们期望的功
能。
Type3 构造子注入的优势:
1. “在构造期即创建一个完整、合法的对象”,对于这条Java设计原则,Type3无疑是最好的
响应者。
2. 避免了繁琐的setter方法的编写,所有依赖关系均在构造函数中设定,依赖关系集中呈现,
更加易读。
3. 由于没有setter方法,依赖关系在构造时由容器一次性设定,因此组件在被创建之后即处于
相对“不变”的稳定状态,无需担心上层代码在调用过程中执行setter方法对组件依赖关系
产生破坏,特别是对于Singleton模式的组件而言,这可能对整个系统产生重大的影响。
4. 同样,由于关联关系仅在构造函数中表达,只有组件创建者需要关心组件内部的依赖关系。
对调用者而言,组件中的依赖关系处于黑盒之中。对上层屏蔽不必要的信息,也为系统的
层次清晰性提供了保证。
5. 通过构造子注入,意味着我们可以在构造函数中决定依赖关系的注入顺序,对于一个大量
依赖外部服务的组件而言,依赖关系的获得顺序可能非常重要,比如某个依赖关系注入的
先决条件是组件的DataSource及相关资源已经被设定。
理论上,以Type3类型为主,辅之以Type2
类型机制作为补充,可以达到最好的依赖注入效果,不过对于基于Spring Framework开发的应用而言,
Type2使用更加广泛。
5)bean.xml配置文件
Bean Factory,顾名思义,负责创建并维护Bean实例。
Bean Factory负责根据配置文件创建Bean实例,可以配置的项目有:
1. Bean属性值及依赖关系(对其他Bean的引用)
2. Bean创建模式(是否Singleton模式,即是否只针对指定类维持全局唯一的实例)
3. Bean初始化和销毁方法
4. Bean的依赖关系
6)XmlBeanFactory两中注入方式的配置
①property-------àset方法的注入配置
<p:bean id=”hello” class=”com.kettas.HelloIFImp”>
<p:property name=”user” value=”xxx”></p:property>
</p:bean>
②constructor---------à构造方法的注入配置
<p:bean id="hello2" class="com.kettas.spring.ioc.day1.HelloIFImpl">
<p:constructor-arg index=”0” value="world"></p:constructor-arg>
<p:constructor-arg type="java.lang.String"”ref="calendar"></p:constructor-arg>
</p:bean>
说明: index=”0”构造方法第一个参数,用index可以稍微减少冗余,但是它更容易出错且不如type属性可读性高。你应该仅在构造函数中有参数冲突时使用index。
7) 依赖的目标类型分成三种形式
1) 基本类型+String
<value>data</value>类型自动转化
2) 对其他bean 的引用
<ref bean="target"/>
3) 集合类型 list props set map
list set properties配置类似
<p:property name="intList">
<p:list>
<p:value>1</p:value>
<p:value>2</p:value>
</p:list>
</p:property>
<p:property name="objMap">
<p:map>
<p:entry>
<p:key>
<p:value>1</p:value>
</p:key>
<p:ref local="hello2"/>
</p:entry>
</p:map>
</p:property>
<p:property name="pros">
<p:props>
<p:prop key="1">red</p:prop>
<p:prop key="2">green</p:prop>
</p:props>
</p:property>
Xml配置文件属性的说明
<bean id="TheAction" ⑴ class="net.xiaxin.spring.qs.UpperAction" ⑵ singleton="true" ⑶
init-method="init" ⑷destroy-method="cleanup" ⑸depends-on="ActionManager" ⑹ >
<property…>
</bean>
⑴ id
Java Bean在BeanFactory中的唯一标识,代码中通过BeanFactory获取
JavaBean实例时需以此作为索引名称。
⑵ class Java Bean 类名 即真正接口的实现类
⑶ singleton bean的作用域(创建模式(prototype还是singleton))
单例(Singleton)模式,如果设为“true”,只维护此Java Bean的一个实例,反之,如果设为“false”, BeanFactory每次都将创建一个新的实例返回。默认为true
实现方式是第一次getBean时放入Map中保存,第二次再用时直接在Map中拿,类名为key,实例为value。Bean的其他作用域还有prototype:原型模式:在获取prototype定义的bean时都产生新的实例,其生命周期由客户端维护。Session对每次HTTPsession中都回产生一个新的实例。Global session 仅在使用portletcontext的时候才有效,常用的是singleton和prototype
⑷ init-method
初始化方法,此方法将在BeanFactory创建JavaBean实例之后,在向应用层返回引
用之前执行。一般用于一些资源的初始化工作。在javaBean中创建init方法,再添加属性init-method=“init”就行
⑸ destroy-method
销毁方法。此方法将在BeanFactory销毁的时候执行,一般用于资源释放。与init用法类似
⑹ depends-on
Bean依赖关系。一般情况下无需设定。Spring会根据情况组织各个依赖关系的构建工作(这里
示例中的depends-on属性非必须)。
只有某些特殊情况下,如JavaBean中的某些静态变量需要进行初始化(这是一种Bad
Smell,应该在设计上应该避免)。通过depends-on指定其依赖关系可保证在此Bean加
载之前,首先对depends-on所指定的资源进行加载。
⑺ <value>
通过<value/>节点可指定属性值。BeanFactory将自动根据Java Bean对应的属性
类型加以匹配。
下面的”desc”属性提供了一个null值的设定示例。注意<value></value>代表一
个空字符串,如果需要将属性值设定为null,必须使用<null/>节点。
⑻ <ref>指定了属性对BeanFactory中其他Bean的引用关系。
8)使用抽象bean 定义抽象类Abstract=“true”抽象bean不能实例化,一个类可以创建多个bean
抽象bean的配置和一般bean的配置基本一样只是在增加了Abstract=“true”抽象bean是一个bean的模板,容器会忽略抽象bean的定义,不会实例化抽象bean,故不能通过getBean()显示的获得抽象bean的实例也不能将抽象bean注入其他bean的依赖属性。
抽象bean的配置和继承
通过Abstract属性配置抽象bean
<bean id=”fatherTemple” class=”abstractClass” abstract=”true”>
<!—注入属性à
<property name=”name” ref=”xxx”/>
</bean>
<!—通过parent属性定义子beanà
<bean id=”childTemple” parent=”fatherTemple”>
<property name=”name2” ref=”yyyy”/> -定义自己的属性
</bean>
说明:子bean配置可以增加新的配置信息,并可以定义新的配置覆盖父类的定义
子类和父类中至少有一个class属性否则不知道实现类,父类的class可以不写
9)bean在容器上的生命周期
初始化两种方法 1使用init-method属性指定那个方法在bean依赖关系设置好后自动执行
2实现initializingBean接口 实现该接口必须实现void afterPropertiesSet()throws Exception那么就不用设置init-method方法了,注意:最好使用init-method方法,减少代码的侵入性,如果两种方法都实现则先实现接口再init方法(一般写入日志文件)
销毁两种方法和初始化一样也有两种方法:1,destroy-method和1实现DisposableBean接口
6,Spring容器,最基本的接口就是BeanFactory 负责创建,配置,管理bean 它有一个子接口ApplicationContext并将功能扩展。
理论上bean装配可以从任何资源获得,包括属性文件,关系数据库等,但xml是最常用的XmlBeanFactory , ClassPathXmlApplicationContext , FileSystemXmlApplicationContext ,
XmlWebApplicationContext应用系统配置源。Spring中的几种容器都支持使用xml装配bean,包括:
XmlBeanFactory , ClassPathXmlApplicationContext ,FileSystemXmlApplicationContext ,
XmlWebApplicationContext
BeanFactory接口包含如下的基本方法:
Boolean containsBean(String name): 判断Spring容器是否包含id为name的bean定义。
Object getBean(String name): 返回容器id为name的bean.
Object getBean(String name, Class requiredType): 返回容器中id为name,并且类型为requiredType的bean.
Class getType(String name): 返回容器中id为name的bean的类型.
ApplicationContext有三个实现类实现资源访问
FileSystemXmlApplicationContext:基于文件系统的xml配置文件创建ApplicationContext,
ClassPathXmlApplicationContext :以类加载路径下的xml的配置文件创建ApplicationContext(更为常用)
XmlWebApplicationContext:对使用servletContextResource进行资源访问
获得容器的应用的方式
①InputStream is = new FileInputStream("bean.xml");
XmlBeanFactory factory = new XmlBeanFactory(is);
或者BeanFactory factory = new XmlBeanFactory(
new ClassPathResource("classpath:bean.xml"))
Action action = (Action) factory.getBean("TheAction");
②ApplicationContext bf=new ClassPathXmlApplicationContext("classpath:bean.xml")
Action action = (Action) factory.getBean("TheAction");
③ApplicationContext bf=new FileSystemXmlApplicationContext(“classpath:bean.xml”)
第一种BeanFactory启动快(启动是不创建实体,到用时才创建实体),
第二种ApplicationContext运行快(在加载时就创建好实体)更为常用,继承BeanFactory
5)工厂bean和bean工厂
FactoryBean(工厂bean):是bean的加工工厂,是对已知Bean的加工,是一个接口,要实现三个方法:
- Object getObject()可以对bean进行加工添加功能
②Class getObjectType()③Boolean isSingleton()
Bf.getBean(“ab”)只是得到MyFactory.getObject()的object对象 所以最后要强转。
Beanfactory bean工厂 就是生产bean的工厂,注入:
由于Spring IoC容器以框架的方式提供了工厂方法的功能,并以透明的方式给开发者,不过在一些遗留系统或第三方类库中,我们还会碰到工厂方法,这时用户可以使用Sping使用工厂方法注入的方式进行配置。
静态工厂方法:
很多工厂类方法都是静态的,这意味着用户在无须创建工厂类实例的情况就可以调用工厂类方法。因此静态工厂方法比非静态工厂方法的调用
更加方便。我们将carFactory类的getCar()方法调整为静态的然后再Spring配置如下:
<bean id=”car” class =”carFactory” factory-method=”getCar”/>
用户直接通过class属性指定工厂类, 然后在通过factory-method指定对应的静态工厂方法创建bean。
如果静态工厂方法需要参数则用<p:constructor-arg index=”1”value="calendar"></p:constructor-arg>传入
实例工厂方法:
有些工厂是非静态的,即必须是实例化工厂类才能调用工厂方法。
下面我们实例化一个工厂类CarFactory类来为Car类提供实例。
package com.car;
public class CarFactory
{ public Car getCar(){return new Car();}}
工厂类负责创建一个或多个目标类实例,工厂类方法一般以接口或抽象类变量的形式返回目标类。工厂类对外屏蔽了目标类的实例化步骤。调
用甚至不知道如何具体的目标类是什么。
下面我们在Spring 配置文件中进行配置
<!--工厂Bean生成目标Bean-->
<bean id=”carFactory” class=”com.CarFactory”/>
<!--工厂Bean目标Bean-->
<bean id=”car” factory-bean=”carFactory” factory-method=”getCar”/>
factory-bean=”carFactory”指定了工厂类Bean,factory-method=”getCar”指定了工厂类Bean创建该Bean的工厂方法。
和静态工厂类似如果工厂方法需要参数则用
<p:constructor-arg index=”0”value="calendar"></p:constructor-arg>传入
7,使用ApplicationContext ApplicationContext覆盖了BeanFactory的所有功能,并提供了更多的特,容器创建时就创建了singleton Bean
相对BeanFactory而言,ApplicationContext提供了以下扩展功能:
1. 国际化支持:继承MessageSource接口,提供国际化支持
2. 资源访问:支持对文件和URL的访问。
3. 事件传播:事件传播特性为系统中状态改变时的检测提供了良好支持。
4. 多实例加载:可以在同一个应用中加载多个Context实例,即加多个配置文件
1,国际化处理的步骤
- 写相应的国家资源文件如:ApplicationResources_zh.properties
注意字符的转化类似struts的国际化
- bean.xml 的配置
<p:bean id="messageSource[U1] " class="org.springframework.context.support.ResourceBundleMessageSource">
<p:property name="basename" value="com.kettas.spring.ioc.day3.ApplicationResource" />
</p:bean>
<p:bean id="msc" class="com.kettas.spring.ioc.day3.MessageSourceConsumer" />
</p:beans>
3)实现类MessageSourceConsumer
具体的实现类implements MessageSourceAware。注入messageSource ms
得到字符:String name = ms.getMessage("name", null, Locale.CHINA);name是资源文件的key值
Locale.CHINA是中文,Locale.ENGLISH英文
2. 资源访问:即外部资源文件的获取;资源文件
两种引入外部资源的方式
①<!-- <p:bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<p:property name="location" value="com/kettas/spring/ioc/day3/jdbc.properties"></p:property>
</p:bean> -->
②,引入解析:xmlns:context="http://www.springframework.org/schema/context"
<context:property-placeholder location="com/kettas/spring/ioc/day3/jdbc.properties"/>
使用<p:property name="driverClassName" value="${jdbc.driver}"></p:property>
jdbc.driver是资源的key值
其它:ApplicationContext.getResource方法提供了对资源文件访问支持,如:
Resource rs = ctx.getResource("classpath:config.properties");
File file = rs.getFile();
3. 事件传播:事件机制是观察者模式的实现
事件框架两个重要的成员:
- ApplicationEvent:容器事件,必须由ApplicationContext发布
- ApplicationListener:监听器:可有其他任何监听器bean担任
- ApplicationContext是事件源必须由java程序显示的触发
1)事件流程:
2)代码实例:
- 事件源.
public class LogEventSource implements ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher; public void setApplicationEventPublisher(
ApplicationEventPublisher publisher){
this.publisher = publisher;
}
public void fireEvent(){
LogEvent evt = new LogEvent(this, "Test message");
publisher.publishEvent(evt);
}
}
- 事件监听,
public class Logger implements ApplicationListener {
private Log logger = LogFactory.getLog(Logger.class);
public void onApplicationEvent(
ApplicationEvent evt) {
logger.info("Event type: " + evt.getClass().getName());
if(evt instanceof LogEvent){ logger.info(((LogEvent)evt).getMessage());
}}
}
3)配置文件
<p:bean id="les" class="com.kettas.spring.ioc.day3.LogEventSource"> 有相应的事件方法
</p:bean>
<p:bean class="com.kettas.spring.ioc.day3.Logger"></p:bean> 处理事件的后台
</p:beans>
说明:LogEventSourc有相应的事件方法publisher.publishEvent(evt)主动触发容器事件; Logge处理事件的后台
4. 多实例加载
BeanPostProcessor bean后处理器 实现BeanPostProcessor接口 对bean实例进一步增加功能,实现两个方法processBeforeInitialization(Object bean,String name)方法(该方法的第一个参数是将进行后处理的实例bean,name是该bean的名字)和ProcessaAfterInitialization(Object bean,String name).在init()或destory之前做一些处理.Spring的工具类就是通过bean的后处理器完成的。
BeanFactoryPostProcessor 容器后处理器:实现接口BeanFactoryPostProcessor只负责处理容器本身 ,实现的方法是 postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)参加资源的引入和获取,可以修改bean工厂bean的定义相当一个再配置的过程。类似BeanPostProcessor,ApplicationContext可以自动检测到容器中的容器后处理器,但是BeanFacory必须手动调用。
5,web中如何使用spring
1),加入相应的jar包
2),Web.xml的配置
<listener>
<listener-class> org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
或者
( <servlet><servlet-name>context</servlet-name>
<servlet-class> org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet> )
通过以上配置,Web容器会默认自动加载/WEB-INF/applicationContext.xml初始化
ApplicationContext实例,如果需要指定配置文件位置,可通过context-param加以指定:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myApplicationContext.xml</param-value>
</context-param>
3) Servlet中应用
WebApplicationContext wac= WebApplicationUtils.getRequiredWebApplication(Context(getServletContext()));
HelloIf h=( HelloIf)wac.getBean(“hello”);
6,BeanFactoryLocator工厂的工厂,主要的两个实现类ContextSingletonBeanFactoryLocator和SingletonBeanFactoryLocator
BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bfr = facLoc.useBeanFactory("il8b");
// BeanFactory fac = bfr.getFactory();
MessageSourceConsumer msc=(MessageSourceConsumer)bfr.getFactory().getBean(“xxxx”);
配置文件只能是beanRefFactory.xml且放在根目录下
<p:bean id=”il8n” class=”org.springframework.context.support.ClassPathXmlApplicationContext”>
<p:constructor-arg><p:value>spring/il8n.xml</p:value></p:constuctor-arg>
</p:bean> 应用另外一个配置文件即另外一个beanFactory
7, .让spring完成自动装配 Autowiring 解决<ref>标签为javaBean注入时难以维护而实现的
下面是几种autowire type的说明:
● byname : 试图在容器中寻找和需要自动装配的属性名相同的bean或id,如果没有找到相应的bean,则这个属性未被装配上。配置文件中的id/name中查找
● byType : 试图在容器中寻找一个与需要自动装配的属性类型相同的bean或id,如果没有找到,则该属性未被装配上。
相当set方法注入
● constructor : 试图在容器中寻找与需要自动装配的bean的构造函数参数一致的一个或多个bean,如果没找到则抛出异常。构造方法注入
● autodetect : 首先尝试使用constructor来自动装配,然后再使用byType方式。
Dependeney_cheching 依赖检查 一般和自动装配配套使用 四种类型:name,simple,object ,all
缺点:spring不一定能很准确的找到javaBean的依赖对象,大型应用一般不用,且配置文件可读性差
8,BeanFactoryAware和BeanNameAware
实现 BeanFactoryAware 接口的 bean 可以直接访问 Spring 容器,被容器创建以后,它会拥有一个指向 Spring 容器的引用。
BeanFactoryAware 接口只有一个方法void setBeanFactory(BeanFactorybeanFactory)。配置和一般的bean一样
如果某个 bean 需要访问配置文件中本身的 id 属性,则可以使用 BeanNameAware 接口,该接口提供了回调本身的能力。实现
该接口的 bean,能访问到本身的 id 属性。该接口提供一个方法:void setBeanName(String name)。
9,spring2.5标签的使用(新特性)对属性,方法的注入 减少配置文件是工作量
1)属性,方法,构造函数的标签注入
1)@Autowired @Autowired按byType自动注入 是对属性的注入,按照类型匹配原则(set和constructors)
2)@Resource(name="target"):@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分别是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。--à常用
3)bean lifecycle :@PostConstruct--àinit注入 @PreDestroy ----àdestory方法注入
4)ClassPath 类路径扫描,就是注入bean
2)Bean的标签注入spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
@Service用于标注业务层组件bean、@Service("studentBiz")。 @Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。@Repository("dao")
而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注,即就是一般的bean。
二,AOP相关术语
1
▲AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面的编程。并不是全部的AOP框架都是一样的。他们连接点模型的功能可能有强弱之分,有些可以字段,方法,构造函数级别的,有些只能是方法的比如spring aop 最主要的三种aop框架:AspectJ Jboss AOP Spring Aop 前面两种都可以对字段方法构造函数的支持。Sping和AspectJ有大量的协作
▲Aop添加的主要功能有:事务管理,安全,日志,检查,锁 等
▲Spring对Aop支持的4种情况:
★经典的基于代理的Aop(各个版本的spring) ★@AspectJ注解驱动的切面(spring 2.0)
★纯POJO切面(spring 2.0) ★注入式AspectJ切面(各个版本的spring)
2 名词解释:
☆关注点 (Concern):关注点就是我们要考察或解决的问题。如订单的处理,用户的验证、用户日志记录等都属于关注点。
关注点中的核心关注点 (Core Concerns) ,是指系统中的核心功能,即真正的商业逻辑。如在一个电子商务系统中,订单处理、客户管理、库存及物流管理都是属于系统中的核心关注点。
还有一种关注点叫横切关注点 (Crosscutting Concerns) ,他们分散在每个各个模块中解决同一样的问题,跨越多个模块。如用户验证、日志管理、事务处理、数据缓存都属于横切关注点。
在 AOP 的编程方法中,主要在于对关注点的提起及抽象 。我们可以把一个复杂的系统看作是由多个关注点来有机组合来实现,一个典型的系统可能会包括几个方面的关注点,如核心业务逻辑、性能、数据存储、日志、授权、安全、线程及错误检查等,另外还有开发过程中的关注点,如易维护、易扩展等。
☆切面 (Aspect):切面是通知和切入点的结合,通知和写入点定义了切面的全部内容—它的功能,在何时何地完成功能。在Spring 2.0 AOP中,切面可以使用通用类(基于模式的风格XML Schema 的风格) 或者在普通类中以 @Aspect 注解(@AspectJ风格)来实现。
下面的例子是基于 xml schema 风格来定义一个 Aspect( 红字部分 ) :
<aop:aspect id="aspectDemo" ref="aspectBean">
< aop:pointcut id = "myPointcut" expression = "execution(* package1.Foo.handle*(..)" />
< aop:before pointcut -ref = "myPointcut" method = "doLog" />
</aop:aspect >
这个定义的意思是:每执行到 package1.Foo 类的以 handle 开头的方法前,就会先执行 aspectBean 的 doLog 方法
☆连接点 (Join point):连接点就是在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。这个点可以
是一个方法、一个属性、构造函数、类静态初始化块,甚至一条语句。切面代码可以通过这些点插入到程序的一般流程之中,从而添加新的行为。 而对于 SPRING 2.0 AOP 来说他是基于动态代理的,故
只支持方法连接点,这个一定要记住~!每一个方法都可以看成为一个连接点,(AspectJ和Jboss可以提供其他aop的实现如,
字段构造函数等)只有被纳入某个Point Cut的 JointPoint 才有可能被 Advice 。 通过声明一个org.aspectj.lang.JoinPoint
类型的参数可以使通知(Advice)的主体部分获得连接点信息。JoinPoint 与CutPoint 之间的关系见下面的CutPoint 的讲解
☆切入点 (Pointcut):如果说通知定义了切面的“什么”和“何时”那么切入点就定义了“何地”。切入点指一个或多个连接点,可以理解成连接电点的集合 .我们通常使用明确的类和方法或是利用正则表达式定义这些切入点。 Advice 是通过 Pointcut 来连接和介入进你的 JointPoint 的。
比如在前面的例子中,定义了
< aop:pointcut id = "myPointcut" expression = "execution(* package1.Foo.handle*(..)" /> 那个类的那个方法使用
那么这就是定义了一个PointCut ,该Pointcut 表示“在package1.Foo类所有以handle开头的方法”
假设package1.Foo类里类似于:
Public class Foo {
public handleUpload(){..}
public handleReadFile(){..}
}
那么handleUpload 是一个JointPoint ,handleReadFile 也是一个Joint,那么上面定义的id=”myPointcut” 的PointCut 则
是这两个JointPoint 的集合
☆通知 (Advice):通知定义了切面是什么,以及何时使用,去了描述切面要完成的工作,通知还要解决何时执行这个工作的问题。它应该在某个方法之前调用还是之后调用,或者抛出一个异常。故通知有各种类型Advice。定义了切面中的实际逻辑(即实现 ) ,比如日志的写入的实际代码。换一种说法Advice 是指在定义好的切入点处,所要执行的程序代码 。
通知有以下几种:
§前置通知( Before advice ) :在切入点匹配的方法执行之前运行使用@Before 注解来声明。implements MethodBeforeAdvice实现的方法是public void before(Method method, Object[] args, Object target)
§返回后通知( After returning advice ) :在切入点匹配的方法返回的时候执行。使用 @AfterReturning 注解来声明
§抛出后通知( After throwing advice ) : 在切入点匹配的方法执行时抛出异常的时候运行。使用 @AfterThrowing 注解来声明
§后通知( After (finally) advice ) :不论切入点匹配的方法是正常结束的,还是抛出异常结束的,在它结束后(finally)后通知(After (finally) advice)都会运行。使用 @After 注解来声明。这个通知必须做好处理正常返 回和异常返回两种情况。通常用来释放资源。
§环绕通知( Around Advice ) :环绕通知既在切入点匹配的方法执行之前又在执行之后运行。并且,它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。在环绕通知中,除了可以自由添加需要的横切功能以外,还需要负责主动调用连接点 ( 通过 proceed) 来执行激活连接点的程序 。 请尽量使用最简单的满足你需求的通知。(比如如果前置通知也可以适用的情况下,就不要使用环绕通知)
§环绕通知使用 @Around 注解来声明。而且该通知对应的方法的第一个参数必须是 ProceedingJoinPoint 类型 。在通知体内(即通知的具体方法内),调用 ProceedingJoinPoint 的 proceed() 方法来执行连接点方法 。
☆引入 (Introduction):引入是指给一个现有类添加方法或字段属性,引入还可以在不改变现有类代码的情况下,让现有的 Java 类实现新的接口 (以及一个对应的实现 )。相对于 Advice 可以动态改变程序的功能或流程来说,引入 (Introduction) 则用来改变一个类的静态结构 。比如你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。举例来说,我们可以创建一个Audiable通知类,记录对象在最后一次被修改是时的状态,这很简单,只要一个方法,setLastModified(Date),和一个实例变量来保存这个状态,这个新的方法和实例变量然后就可以引入到现在的类,从而在不修改他们的情况下让他们有新的行为和状态。
☆目标对象( Target Object )被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做 被通知(advised) 对象。 既然Spring AOP是通过运行时代理实现的,这个对象永远是一个 被代理(proxied )对象。
☆AOP 代理( AOP Proxy )“代理”是向目标对象应用通知之后被创建的对象,对客户端来说目标对象和代理对象是一样的。AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)。 在Spring中,AOP生成被代理的类有两种:如果目标对象实现的是一个接口则是用JDK的 java.lang.reflect.Proxy动态代理;如果不是接口则用CGLIB库生成目标生成目标类的子类在创建这个子类的时候spring织入通知,并且把这个子类的调用委托给子类,这样就要注意两点,1 最好是用接口实现代理,cglib只是实现没有接口也可以通知的情况。2,被标记final的方法和类不能通知因为无法创建子类。。
☆织入( Weaving )把切面(aspect)连接到其它的应用程序类型或者对象上,并创建一个被通知(advised)的对象,这样一个行为就叫做Weaving。 这些可以在编译时(例如使用AspectJ 编译器),类加载时和运行时完成。 Spring 和其他纯 Java AOP 框架一样,在运行时完成织入 。
其实织入的方式有3种:
1、编译器织入: 切面在目标类编译时植入,这需要特殊的编译器。AspectJ 主要就是是使用这种织入方式 。
2、类加载器织入: -切面在目标类加载到JVM的时候进行织入,这需要特殊的ClassLoader。它可以在目标类被加载到程序之前增强类的字节代码。比如AspectWerkz ( 已并入 AspecJ ) 及 JBoss 就使用这种方式 。
3、运行时织入: -即在 java运行的过程中,使用Java提供代理来实现织入。根据代理产生方式的不同,运行时织入又可以进一步分为J2SE动态代理及动态字节码生成两种方式。由于J2SE动态代理只能代理接口,因此,需要借助于一些动态字节码生成器来实现对类的动态代理。大多数 AOP 实现都是采用这种运行时织入的方式,包括 Spring 。
3:AOP的工作模式
代理主要有静态代理和动态代理
静态代理:在代理中实现接口并创建实现类对象,在对实现类的方法增加功能(不常用)
动态代理:实现implements InvocationHandler接口。实现方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
System.out.println("=========");
Object o=method.invoke(this.ins, args);
System.out.println("---------");
return o;
}
流程图:在配置文件的配置
配置文件代码:
<!-- pointcut definition -->
<p:bean id="cf" class="com.kettas.spring.aop.day4.MyClassFilter">
<p:property name="classes">
<p:set>
<p:value>com.kettas.spring.ioc.day1.HelloIF</p:value> ---
</p:set>
</p:property>
</p:bean>
<p:bean id="mm" class="com.kettas.spring.aop.day4.MyMethodMatcher">
<p:property name="methodNames">
<p:set>
<p:value>sayHello</p:value>
</p:set>
</p:property>
</p:bean>
<p:bean id="pc" class="com.kettas.spring.aop.day4.MyPointcut">
<p:property name="classFilter" ref="cf"></p:property>
<p:property name="methodMatcher" ref="mm"></p:property>
</p:bean>
<!--advice 要继承implements MethodInterceptor-->
<p:bean id="timingAdvice" class="com.kettas.spring.aop.day4.TimingInterceptor"></p:bean>
<!-- advisor 把通知和切入点结合在一起- 最好给代理增加功能->
<p:bean id="timingAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<p:property name="advice" ref="timingAdvice"></p:property>
<p:property name="pointcut" ref="pc"></p:property>
</p:bean>
<!—target 目标 -->
<p:bean id="helloTarget" class="com.kettas.spring.ioc.day1.HelloIFImpl">
<p:property name="cal">
<p:bean class="java.util.GregorianCalendar"></p:bean>
</p:property>
<p:property name="user" value="world"></p:property>
</p:bean>
<!-- proxy -->
<p:bean id="hello" class="org.springframework.aop.framework.ProxyFactoryBean">
<p:property name="target" ref="helloTarget"></p:property>
<p:property name="interceptorNames">
<p:list>
<p:idref bean="timingAdvisor"/> 增加一种服务
<p:idref bean="xxxAdvisor"/> 增加另一种服务
</p:list>
</p:property>
<p:property name="proxyInterfaces"> 要和目标类实现共同的接口
<p:value>com.kettas.spring.ioc.day1.HelloIF</p:value>
</p:property>
</p:bean>
</p:beans>
简化配置:有可能只是目标类不一样,其他的都是一样的。解决每一个目标类都需要一个复杂的代理过程配置的问题,可以简化配置的问题 抽象ProxyFactoyBean的方法 如:
<!-- 抽象proxy --定义抽象的类,只是没有目标类,其他的通知和接口都一样>
<p:bean id="helloBase" class="org.springframework.aop.framework.ProxyFactoryBean" abstract=“true”>
<p:property name="interceptorNames">
<p:list>
<p:idref bean="timingAdvisor"/> 增加一种服务
<p:idref bean="xxxAdvisor"/> 增加另一种服务
</p:list>
</p:property>
<p:property name="proxyInterfaces"> 要和目标类实现共同的接口
<p:value>com.kettas.spring.ioc.day1.HelloIF</p:value>
</p:property>
</p:bean>
</p:beans>
真正的代理
<!—target 目标 继承抽象方法 只用写目标类就可以了 -->
<!-- proxy -->
<p:bean id="hello" parent=” helloBase”>
<p:property name="target" ref="helloTarget"></p:property>
</p:bean>
4:AOP的自动代理
Spring的aop机制提供两类方式实现类代理。一种是单个代理,一种是自动代理。
单个代理通过ProxyFactoryBean来实现(就如上面的配置),
自动代理:自动代理能够让切面定义来决定那个bean需要代理,不需要我们为特定的bean明确的创建代理从而提供一个更完整的aop实现 通过BeanNameAutoProxyCreator或者 DefaultAdvisorAutoProxyCreator实现。
◆采用单个代理方式 (费时费力,配置复杂臃肿)
下面就采用自动代理
实现代理bean的两种方式:
1,“基于Spring上下文的里声明的通知者bean的基本自动代理”:通知者的切点表达式用于决定哪个bean和那些方法需要被代理
2,”基于@AspectJ注释驱动切面的自动代理”:切面里包含的通知里指定的切点将用于选择哪个bean和哪个方法要被代理
第一种:<!——自动代理增加此行,容器会自动根据通知要匹配的切入点,为包含切入点的类创建 代理。注意这个bean没有id,因为永远都不会直接引用它——>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/> feedom.net
第二种 自动代理@AspectJ切面
然而aspectJ提供可一种基于jdk1.5注解技术的方式,使得配置文件更少,更方便。能够把POJO类注释为切面这通常称为
@AspectJ.我们利用@AspectJ注释,我们不需要声明任何额外的类或Bean就可以把POJO转换成一个切面例如:
@Aspect 定义切面不再是普通的POJO了 在POJO类中加注释
public class AspectJMixAspect {
private Log logger = LogFactory.getLog(AspectJMixAspect.class);
@Pointcut("execution(* *..HelloIF.*(..)) || execution(* *..TestBeanIF.*(..))")定义切入点那些类的那些方法添加
public void allMethods() { }
@Pointcut("execution(* *..TestBeanIF.toDate(..)) && args(dateStr)")
public void toDate(String dateStr) {
}
@Around("allMethods()") 环绕通知
public Object timing(ProceedingJoinPoint pjp) throws Throwable {
long begin = System.currentTimeMillis();
Object ret = pjp.proceed();
long end = System.currentTimeMillis();
String methodName = pjp.getSignature().getName();
String targetClass = pjp.getTarget().getClass().getName();
logger.info("Around advice: It took " + (end - begin) + "ms to call "
+ methodName + " on " + targetClass);
return ret;
}
@Before("allMethods()")
public void logBefore(JoinPoint jp) {
logger.info("Before advice: " + jp.getSignature().toLongString());
}
@AfterReturning(value="toDate(dateStr)", returning = "date", argNames = "date, dateStr")
public void afterReturning(Date date, String dateStr) {
logger.info("call method toDate(" + dateStr + ") and return " + date);
}
@AfterThrowing(value="toDate(dateStr)", throwing="ex", argNames="dateStr, ex")
public void afterThrowing(String dateStr, ParseException ex){方法名任意但参数要和argNames=""中的参数顺序一样,
}
}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<p:beans xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="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.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd ">
<!-- target -->
<p:bean id="hello" class="com.kettas.spring.ioc.day1.HelloIFImpl">
<p:property name="cal">
<p:bean class="java.util.GregorianCalendar"></p:bean>
</p:property>
<p:property name="user" value="world"></p:property>
</p:bean>
<p:bean id="testBean" class="com.kettas.spring.aop.day5.TestBean">
<p:property name="pattern" value="yyyy-MM-dd"></p:property>
</p:bean>
<!-- apsect bean -->
<p:bean id="aspectJAspect" class="com.kettas.spring.aop.day5.AspectJMixAspect"></p:bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> 声明自动代理bean需要命名空间:aop="http://www.springframework.org/schema/aop"
</p:beans>
5,定义纯粹的POJO切面 不在普通的bean中加注释,而是在配置文件中配置
<!-- target -->
<bean xxx ></bean>
ß-Pojo bean-->
<bean id =” aspectJAspect”class=” com.kettas.spring.aop.day5.AspectJMixAspect”/>
<aop:config>
<aop:aspect ref=” aspectJAspect”> 将aspectJAspect定义为切面 下面定义加方法的类和方法
<aop:pointcut id="allMethods" expression="execution(* *..HelloIF.*(..)) or execution(* *..TestBeanIF.*(..))"/>
<aop:advisor pointcut-ref="allMethods" advice-ref="timingAdvice" />
<aop:advisor pointcut-ref="allMethods" before-method=“logBefore” />
<aop:advisor pointcut-ref="allMethods" advice-ref="logAfterAdvice" /> 引入外部的通知
<aop:advisor pointcut="execution(* *..TestBeanIF.toDate(..))" advice-ref="logThrowingAdvice" />
</aop:config>
14,注入AspectJ切面
为什么要用AspectJ:AspectJ提供了Spring AOP很多不能实现的多种切点类型(比如属性,构造方法切入,由于不能实现构造方法的切入spring aop就不能实现对象创建过程的通知)
AspectJ是一个代码生成工具(Code Generator)。AspectJ有自己的语法编译工具,编译的结果是Java Class文件,运行的时候,classpath需要包含AspectJ的一个jar文件。AspectJ是AOP最早成熟的Java实现,它稍微扩展了一下Java语言,增加了一些Keyword等
public aspect TestAspectJ {
public TestAspectJ();
public pointcut writeOperations():
execution(public boolean Worker.createData()) ||
execution(public boolean Worker.updateData()) ||
execution(public boolean AnotherWorker.updateData()) ;
before() : writeOperations() {
XXXXXX; advice body
}
after() : writeOperations() {
XXXX;// advice body
}
}
配置文件
<bean class=”xxx/TeatAspectJ” factory-method=”aspectof”>
<property name=”” ref=””/></bean>
说明机制:这个<bean>和其他的bean并没有太多区别,只是多了 factory-nmthod属性 要想获得切面的实例,就必须使用factory-method来调用 aspectof()方法,而不是调用TestAspectJ的构造方法,简单来说Spring不使用《bean》声明来创建TestAspectJ实例,它已经被AspectJ运行时创建了,Spring通过aspectof()工厂方法获得切面的引用,然后利用<bean>元素对她执行属性注入
上述代码关键点是pointcut,意味切入点或触发点,那么在那些条件下该点会触发呢?是后面红字标识的一些情况,在执行
Worker的createData()方法,Worker的update方法等时触发
Pointcut类似触发器,是事件Event发生源,一旦pointcut被触发,将会产生相应的动作Action,这部分Action称为Advice。
Advice在AspectJ有三种:before、 after、Around之分,上述aspect Lock代码中使用了Advice的两种before和after。
所以AOP有两个基本的术语:Pointcut和Advice。你可以用事件机制的Event和Action来类比理解它们
其中advice部分又有:
Interceptor - 解释器并没有在AspectJ出现,在使用JDK动态代理API实现的AOP框架中使用,解释有方法调用或对象构造或者字段访问等事件,是调用者和被调用者之间的纽带,综合了Decorator/代理模式甚至职责链等模式。
Introduction - 修改一个类,以增加字段、方法或构造或者执行新的接口,包括Mixin实现。
Spring2.5 aop新特性
在配置文件中的配置:就和POJO中的配置一样
在java类中的配置:就和自动代理类配置一样 ,只是在配置的时候注意加上<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
注意: execution(* *..HelloIF.*(..)))的含义 任意权限(*),任意返回值(*),任意包下(。。),类名(HelloIF),任意方法(*),任意参数(。。)。 星号(*)代表之间没有逗号和点号,(。。)代表可以有逗号和点号
- 切入点指示符:前面的execution就是一个切入点指示符。Spring AOP还支持的切入点指示符有
Within:配置特定的连接点 。 this:Aop代理必须是指定类型的实例 如this(org.crazyit.service.AccountService)匹配实现了AccountService接口的所有连接点. Target:和this的表达类似。实现限定目标对象必须是指定类型的实例。
Bean:匹配实例内方法执行的连接点 bean(tradeService)可以用* 通配符
三:Data Access 数据库的模板操作
1,好处:支持事务的管理 2有统一的异常处理 RuntimeException 3 Support各种数据库
2,数据源的配置:在spring中数据源的配置有
★有JDBC驱动程序定义的数据源 :在org.springframework.jdbc.datesource包有两个实现类
DriverManagerDateSouce。每个请求时都建立一个连接
或者SingleConnectionDataSource 配置api中的set方法,用户名,密码,url driver
★由JNDI查询的数据源 :在tomcate配置数据源,还有Jbose等都可以 通过jndi引用就可以了
<bean id=”dataSource” class=”…jndi.jndiObjectFactory”>
<property name=”jndi” value=”/jdbc/rantzDataSource”/>
<property name=”resourceRef” value=”true”/>
★连接池的数据源:Spring 没有提供数据源连接池,但是DBCP项目提供了一个,下载相关的jar到lib中
配置类似第一种JDBC驱动的配置
3 JDBC :我们知道,Spring JDBC的主要目标是为了简化JDBC的编程,方便我们构建健壮的应用程序。这里,它的一个基
本设计理念,就是将JDBC编程中变化的和不变化的分开。 在JDBC中,什么是变化的?毫无疑问,SQL语句是变
化的。那什么是不变化的?正确的使用JDBC的方式是不变化的。使用JDBC模板
提供了三个模板:JdbcTemplate 最基本的jdbc模板 使用索引参数查询数据库
NamedParameterJdbcTemplate ,查询时把值绑定到SQL里的命名参数而不是使用索引参数
SimpleJdbcTemplate利用java5的特性,比如自动装箱,通用,可变参数列表来简化JDBC模板的使用
以JdbcTemplate为例子说明:在已经配置datasource的情况下就能使用 JdbcTemplate有相关的查询,插入,等方法
在xml中配置模板JdbcTemplate bean
<bean id=”jdbcTemplate” class=”org.springframework.jdbc.core.JdbcTemplate”>
< property name=”dataSource” ref=”dataSource”>
</bean>
在dao层的bean中加入JdbcTemplate引用就可以了,这样就可以使用它来访问数据库
如:class JdbcDao{
Private JdbcTemplate jdbcTemplate;
//setJdbcTemplate 方法注入
Public User getUserById(long id){
List users=jdbcTemple.query(sql,new Object[]{Long.valueOf(id)}),new RowMapper(){
Public Object mapRow(ResultSet rs,int rowNum) throws SQLException
User user=new User();
user.setId(rs.getInt(1)); ….;
return user;
}
Return users.size()>0?(user)users.get(0):null;
}
}
说明:一个字符串,包含用于从数据库里选择数据的 SQL 语句。
一个 Object 数组,包含与查询里索引参数绑定的值。Sql几个问号就绑定几个值
一个 RowMapper 对象,它从ResultSet 里提取数值并构造一个域对象。封装对象返回结果
然后像下面的设置一样注入模板JdbcTemplate属性就可以了
<bean id=”jdbcDao”class=”…/JdbcTemplate”>
<property name=” JdbcTemplate” ref=” jdbcTemplate”></bean>
JdbcDaoSupport
注意:可以使用Spring 对JDBC 的DAO 支持类 JdbcDaoSupport
思想: 全部 DAO 对象创建一个通用父类,在其中设置JdbcTemplate 属性,然后让全部DAO 继承这个
类,使用父类的JdbcTemplate 进行数据访问,这样可以减少配置量。Spring 的JdbcDaoSupport 就是用于编写基于JDBC 的DAO 类的基类,我们只需让自己的DAO 类继承它即可 例如上面的
class JdbcDao extends JdbcDaoSupport{
Public User getUserById(long id){
List users=getJdbcTemple().query(sql,new Object[]{Long.valueOf(id)}),new RowMapper(){
Public Object mapRow(ResultSet rs,int rowNum) throws SQLException
User user=new User();
user.setId(rs.getInt(1)); ….;
return user;
}
Return users.size()>0?(user)users.get(0):null;
}
}
在配置文件与配置不继承JdbcDaoSupport 的DAO 没有什么区别
<bean id=”jdbcDao”class=”…/JdbcTemplate”>
<property name=” JdbcTemplate” ref=” jdbcTemplate”></bean>
4 在Spring 里集成Hibernate
1)下载相关的Hibernante的jar包到lib文件 注意最好是3.0版本以上
2)使用Hibernate 模板 HibernateTemplate 有各种增删改查的方法
▲与 Hibernate 进行交互的主要接口是org.hibernate.Session。这个Session 接口提供了基本的数据访问功
能,比如从数据库保存、更新、删除和加载对象。获得 Hibernate Session 对象引用的标准方式是实现。Hibernate 的SessionFactory 接口。SessionFactory负责打开、关闭和管理Hibernate Session,以及其他一些功能。
3)为了让事情简单一些,Spring 提供了HibernateDaoSupport,用法类似JdbcDaoSupport,它能够让我们把会话工厂Bean 直接装配到DAO 类
两种获得session的配置 bean.xml
<!-- 自己加载 hibernate -->
<bean id="dataSource"[U2] class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>jdbc:oracle:thin:@localhost:1521:XE</value>
</property>
<property name="password">
<value>kettas</value>
</property>
<property name="username">
<value>kettas</value>
</property>
</bean>
<bean id="factory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingResources">
<list>
<value>com/kettas/entity/shoppingcart.hbm.xml</value>[U3]
</list>
</property>
<property name="hibernateProperties[U4] ">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<!-- use 外面的 hibernate.cfg.xml
<bean id="factory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
</bean>-->
<bean id="DaoSupport" class="com.kettas.dao.impl.Hibernate2DaoSupportImpl">
<property name="sessionFactory[U5] ">
<ref bean="factory"/>
</property>
</bean>
<bean id="template"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory"> 模板注入sessionFactory
<ref bean="factory" />
</property>
</bean>
HibernateTemplate中常用的方法:getHibernateTemplate().delete(o); update() 。。。。;
super.getHibernateTemplate().executeFind(
new HibernateCallback() { 回调使用hibenate的方法
public Object doInHibernate(Session session)
throws HibernateException, SQLException { 。。。。}
- 使用Hibernate 3 上下文会话
如果使用 Hibernate 2,那么就只能使用HibernateTemplate。
? Hibernate 上下文会话的主要好处在于把DAO 实现与Spring 解耦。
? Hibernate 上下文的主要缺点是它们抛出Hibernate 特有的异常。虽然HibernateException 是运行
时异常,但这个异常体系是Hibernate 特有的,而且不像Spring 存留异常体系那样与ORM 无关,
这可能会在程序向不同ORM 迁移时产生问题。
HibernateTemplate 的缺点是具有一定的侵入性,HibernateDaoSupport),HibernateRantDao 类都被耦合到Spring API。
我们还有另外一种办法。Hibernate 3 引入的上下文会话可以管理每事务一个会话,也就不需要让HibernateTemplate 来确
保这种情况了。所以,我们可以在DAO 里装配一个Hibernate SessionFactory 来取代HibernateTemplate
public class HibernateRantDao implements RantDao {
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory; }
public void saveRant(Rant rant) {
sessionFactory.getCurrentSession().saveOrUpdate(rant);
}}
<bean id="rantDao" class="com.roadrantz.dao.hibernate.HibernateRantDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
- JPA 是个基于POJO 的存留机制,不仅从Hibernate 和Java 数据对象(JDO)吸取了观念,还适当地混合了Java 5 的注解功能。用法类似Hibernate。基于JPA 的程序使用EntityManagerFactory 的一个实现来获取EntityManager 的实例,他用entityManagerFactory进行管理,相当sessionFactory。可以在persistence.xml,类似hibernate的hibernate.cfg.xml配置打他source等资源,也可以直接在spring的配置文件配置(使用见EJB)
在spring中的配置
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.
➥ LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="rantzPU"[U6] />
</bean>
<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
获得jpa的模板,用法类似Hibernate,就不多说,Ejb中再讨论
四:事务管理
1,事务 (见hibernate的ACID): Spring和EJB一样,不仅提供对程序控制事务管理的支持(手动事务),也对提供声明式事务管理的支持(容器管理事务),但是Spring对程序控制事务管理的支持与EJB很不一样。EJB的事务管理和Java Transaction API(JPA)密不可分。和Ejb不同的是Spring采用的是一种回调机制,把真实的事务从事务代码中抽象出来,那么Spring就不需要JPA的实现。选择手动事务还是容器管理,就是在细微控制和简便操作之间做出选择。想精确控制事务就可以选择手动事务,不用那么精确就可以容器管理事务。
事务管理器:不管你是在bean中代码编写事务还是用切面(aspect aop)那样声明事务,都需要Spring的事务管理器连接特定平台的事务实现,每一种访问形式都有一个事务管理器。比如:
jdbc.datasource.DataSourceTransactionManager:jdbc连接的事务管理,iBATIS也支持
orm.hibernate3. HibernateTransactionManager :hibernate3的事务支持
orm.jpa.JpaTransactionManager :jpa的事务支持
orm.jdo.JdoTransactionManager :Jdo事务管理支持
这些事务管理器分别充当了某个特定的事务实现门面,这样你就只要和Spring的事务打交道,而不用关心实际上的事务是怎么实现的(门面模式)
各种事务管理器的配置,以Hibernate 3为例:
<bean id="transactionManager" class="org.springframework.
➥ orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory[U7] "/>
</bean>
JDBC事务管理
<bean id="transactionManager" class="org.springframework.jdbc.
➥ datasource.DataSourceTransactionManager"> ---à DataSourceTransactionManager调用Connection来管理事务
<property name="dataSource" ref="dataSource"/>
</bean>
4,在spring中手动编写事务
利用事务模板TransactionTemplate来手动添加事务
public void addRant(Rant rant) {
transactionTemplate.execute(-àtransactionTemplate是注入transactionManager得到的
new TransactionCallback() {-à TransactionCallback()只有一个方法实现doInTransaction,用一个匿名内部类实现
public Object doInTransaction(TransactionStatus ts) { ------à在事务内执行
try {
rantDao.saveRant(rant);
} catch (Exception e) {
ts.setRollbackOnly();------------------à出现异常就回滚
}return null;
}
}
配置文件
<bean id="rantService"
class="com.roadrantz.service.RantServiceImpl">
…
<property name="transactionTemplate[U8] ">
<bean class="org.springframework.transaction.support.
➥ TransactionTemplate">
<property name="transactionManager"
ref="transactionManager" />
</bean>
</property>
</bean>
5,声明式事务
可以把事务想成一个切面,那么就可以用事务性边界包裹Biz层的方法,然后注入事务
Spring提供了三种在配置文件声明事务性边界的方式:★常用的Spring aop代理 bean来支持事务。★但在Spring 2中增加了两种新的方式:简单的XML声明(xml-declared)事务和★注释驱动事务
1)代理事务:声明式事务管理通过使用Spring的TransactionProxyFactoryBean代理POJO来完成。TransactionProxyFactoryBean是ProxyFactoryBean的一个特化,他知道如何通过事务性边界包裹一个POJO的方法来代理他们。
<bean id="rantService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target" ref="rantServiceTarget" /> --à装配事务目标,相当给biz层的方法加事务
<property name="proxyInterfaces" value="com.roadrantz.service.RantService" />
<property name="transactionManager" ref="transactionManager" /> --à提供适当的事务管理器
<property name="transactionAttributes[U9] ">
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
事务传播行为:
PROPAGATION_REQUIRED :当前方法必须有一个事务,有事务则运行该事务,没有则开始新的事务。---à最常用
PROPAGATION_MANDATORY:该方法必须有事务,没有事务则抛出异常
PROPAGATION_NESTED :该方法运行在嵌套事务中。如果封装事务不存在则就像第一种PROPAGATION_REQUIRED
PROPAGATION_NEVER :该方法不能有事务,有事务则抛出异常。
PROPAGATION_NOT_SUPPORTED:该方法不能有事务,如果有事务,则将该方法在运行期间挂起。
PROPAGATION_REQUIRES_NEW:方法必须运行在事务里,
PROPAGATION_SUPPORTS:表示当前方法不需要事务性上下文,但是如果有一个事务已经在运行的话,他可以在这个事务里运行。
PROPAGATION, ISOLATION, readOnly, -Exception, +Exception
(传播行为) (隔离级别 可选) (事务只读 可选) (回滚规则 可选)
可以创建事务模板简化配置 :建立事务的抽象声明
<bean id="TXServiceTemplate" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract=“true”>[U10]
<property name="transactionManager" ref="transactionManager" /> --à提供适当的事务管理器
<property name="transactionAttributes[U11] ">
<props>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
<bean id=”ranBiz” parent=” TXServiceTemplate[U12] ”>
<property name="proxyInterfaces" value="com.roadrantz.service.RantService" />
<property name="transactionManager" ref="transactionManager" /> --à提供适当的事务管理器
</bean>
2)在Spring2.0声明事务 <tx>上面的方法会导致配置很臃肿,下面就是更简单的配置
在Spring2.0中专门为声明事务提供了一些新的标签 tx名称空间下
xmlns:tx=http://www.springframework.org/schema/tx
xmlns:aop[U13] ="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<tx:advice[U14] id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="create*" />
<tx:method name="join*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor[U15] pointcut="execution(* *..Roster.*(..))" advice-ref="txAdvice"/>
</aop:config>
3)定义注释驱动的事务,@Transactional可以在源代码中注释来进一步简化配置
@Transactional[U16] (propagation=Propagation.SUPPORTS, readOnly=true)
@Service("roster")
public class RosterImpl implements Rosterpublic
@Transactional --------------- --------à方法层面的事务
Public Player createPlayer(Player p) {
playerDao.save(p);
return p;
}
<context:component-scan
base-package="com.kettas.spring.dao.day5.roster.dao,com.kettas.spring.dao.day5.roster.biz">
</context:component-scan>
<tx:annotation-driven/> 自动搜索@Transactional的bean 然后把事务通知告诉它。
五:Spring的MVC
目前比较好的MVC,老牌的有Struts、Webwork。新兴的MVC 框架有Spring MVC、
Tapestry、JSF等。这些大多是著名团队的作品,另外还有一些边缘团队的作品,也相当出色,
如Dinamica、VRaptor等。
六:Spring的安全机制 Spring Security:它提供全面的安全性解决方案,同时在Web请求和方法调用处理身份确认和授权,利用依赖注入和aop技术。主要名词:
1安全拦截器:相当应用的一把锁,能够阻止对应用程序中保护资源的访问(通常是用户名和密码 正确才能打开锁)
2 认证管理器:通过用户名和密码来做到这点的,负责确定用户的身份。是由
3:访问决策管理器:根据你的身份来确定你是否拥有对资源的访问,即授权管理
4:运行身份管理器:运行身份管理器可以用来使用另一个身份替换你的身份,从而允许你访问应用程
序内部更深处的受保护对象。
5:调用后管理器:访问结束后还可以返回取得相关资源,其他安全管理器组件在受保护资源
被访问之前实施某种形式的安全措施强制执行,而调用后管理器则是在受保护资源被访问之后执行安全措
施。
认证管理器是由org.acegisecurity. AuthenticationManager 接口定义的
ProviderManager 是认证管理器的一个实现,它将验证身份的责任委托给一个或多个认证提供者
DaoAuthenticationProvider 是一个简单的认证提供者,它使用数据存取对象(DAO)来从关系数据库
中检索用户Spring Security 支持通过LdapAuthenticationProvider 根据LDAP 进行身份验证,
LdapAuthenticationProvider 是一个知道如何根据LDAP 仓库查看用户凭证的认证提供信息(包括用户的密码)
身份验证只是Spring Security 安全保护机制中的第一步。一旦Spring Security 弄清用户的身份后,它必须
决定是否允许用户访问由它保护的资源
Spring Security 对Web 安全性的支持大量地依赖于Servlet 过滤器。这些过滤器拦截进入请求,并且
在你的应用程序处理该请求之前进行某些安全处理。Spring Security 提供有若干个过滤器,它们能够拦截
Servlet 请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。
七:Spring的远程调用:Spring远程支持是由普通(Spring)POJO实现的,这使得开发具有远程访问功能的服务变得相当容易
四种远程调用技术:
◆ 远程方法调用(RMI) ◆ Caucho的Hessian和Burlap
◆Spring自己的Http invoker ◆使用SOAP和JAX-RPC的web Services
Spring对上面的远程服务调用都有支持
Spring远程调用支持6种不同的RPC模式:远程方法调用(RMI)、Caucho的Hessian和Burlap、Spring自己的HTTP invoker、
EJB和使用JAX-RPC 的Web Services。表6.1概括地论述了每个模式,并简略讨论它们在不同情况下的用处。
表6.1 Spring远程调用所支持的RPC模式
RPC模式 | 在何种情况下有用 |
远程方法调用(RMI) | 不考虑网络限制(如防火墙)时,访问/公开基于Java的服务 |
Hessian或 Burlap | 考虑网络限制时,通过HTTP访问/公开基于Java的服务 |
HTTP invoker | 考虑网络限制时,访问/公开基于Spring的服务 |
EJB | 访问用EJB实现的遗留的J2EE系统 |
JAX-RPC | 访问Web Services |
|
|
远程方法调用(RMI)。通过使用 RmiProxyFactoryBean 和 RmiServiceExporter,Spring同时支持传统的RMI(使用java.rmi.Remote接口和java.rmi.RemoteException)和通过RMI调用器实现的透明远程调用(支持任何Java接口)。
Spring的HTTP调用器。Spring提供了一种特殊的允许通过HTTP进行Java串行化的远程调用策略,支持任意Java接口(就像RMI调用器)。相对应的支持类是 HttpInvokerProxyFactoryBean 和 HttpInvokerServiceExporter。
Hessian。通过 HessianProxyFactoryBean 和 HessianServiceExporter,可以使用Caucho提供的基于HTTP的轻量级二进制协议来透明地暴露服务。
Burlap。 Burlap是Caucho的另外一个子项目,可以作为Hessian基于XML的替代方案。Spring提供了诸如 BurlapProxyFactoryBean 和 BurlapServiceExporter 的支持类。
JAX RPC。Spring通过JAX-RPC为远程Web服务提供支持。
客户端发起对代理的调用,好像是代理提供了这些服务的功能一样。代理代表客户端和远程服务交流。它处理连接的具体情况,
并向远程服务发起远程调用。在Spring里,远程服务是被代理的,所以他们能像一般的Bean那样置入到客户端的代码里
八:Spring的Web服务:
Spring支持:使用JAX-RPC暴露服务 访问Web服务
除了上面所说的支持方法,你还可以用XFire xfire.codehaus.org 来暴露你的服务。XFire是一个轻量级的SOAP库,目前在Codehaus开发。
使用JAXI-RPC暴露服务
Spring对JAX-RPC Servlet的端点实现有个方便的基类 - ServletEndpointSupport。为暴露我们的Account服务,我们继承了Spring的ServletEndpointSupport类来实现业务逻辑,这里通常把调用委托给业务层。
访问服务:
Spring有两个工厂bean用来创建Web服务代理,LocalJaxRpcServiceFactoryBean 和 JaxRpcPortProxyFactoryBean。前者只返回一个JAX-RPT服务类供我们使用。后者是一个全功能的版本,可以返回一个实现我们业务服务接口的代理。本例中,我们使用后者来为前面段落中暴露的AccountService端点创建一个代理。你将看到Spring对Web服务提供了极好的支持,只需要很少的代码 - 大多数都是通过类似下面的Spring配置文件:
<bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
<property name="serviceInterface" value="example.RemoteAccountService"/>
<property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/>
<property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/>
<property name="serviceName" value="AccountService"/>
<property name="portName" value="AccountPort"/>
</bean>
XFire是一个Codehaus提供的轻量级SOAP库。在写作这个文档时(2005年3月)XFire还处于开发阶段。虽然Spring提供了稳定的支持,但是在未来应该会加入更多特性。暴露XFire是通过XFire自身带的context,这个context将和RemoteExporter风格的bean相结合,后者需要被加入到在你的WebApplicationContext中。
九:Spring的消息 Java Message Service (JMS):
前面的RMI,Hesian ,Burlap,HTTP invoker web服务的间通信,都是程序之同步,即当客户端调用远程方法时必须在方法完成之后才能继续执行。
JMS是一种异步消息传递的标准API,客户端不需要等待服务端处理消息,客户端发送消息,会继续执行,这是因为客户端假设服务端最终能够收到并处理这条消息。类似Ajax
发送消息 使用JMS的模板JsmTemplate 接收消息
十:Spring和EJB的整合:
Spring有两种方法提供对EJB的支持:
Spring能让你在Spring的配置文件里,把EJB作为Bean来声明。这样,把EJB引用置入到其他Bean的属性里就成为可能了,好像EJB就是另一个POJO。
Spring能让你写EJB,让EJB成为Spring配置的Bean的代理的工作。
Spring提供了两个代理工厂Bean,来代理EJB的访问:
LocalStatelessSessionProxyFactoryBean——用来访问本地EJB(EJB和它的客户端在同一个容器中)。
SimpleRemoteStatelessSessionProxyFactoryBean——用来访问远程EJB(EJB和它的客户端在独立的容器中)。
十一:访问企业服务:
注意:这里声明了一个名为messageSource的Bean(注意对于Message定义,Bean ID必须为messageSource,这是目前Spring的编码规约)
配置数据源,和前面的配置类似
映射文件 ,并且可以配置多个
Hibernate的配置文件,数据库的方言一定要配置hibernate.dialect
Hibernate2DaoSupportImpl extends HibernateDaoSupport 这样就不需要HibernateTemplate 属性,而是使用getHibernateTemplate()
方法获得由HibernateDaoSupport 创建的HibernateTemplate,HibernateDaoSupport 需要一个Hibernate SessionFactory,这样它才能在内部生成一个HibernateTemplate。所
以我们要把sessionFactory Bean 装配到Hibernate2DaoSupportImpl 的sessionFactory 属性
persistence.xml中
<persistence-unit name="rantzPU">
persistenceUnitName 属性的值就是persistence.xml 里存留单元的名称。
这sessionFactory属性必须被装配为一个Hibernate的SessionFactory 。
name=“sessionFactory”是固定的。HibernateTransactionManager把事务管理委托给一个当前Hibernate会话中检索到的org.hibernate.Transaction对象,当事务成功则调用Transaction.commit(),失败则调用Transaction.rollback().
transactionTemplate是注入transactionManager得到的
声明什么方法将在一个事务内运行,以及相关的事务参数每一个<prop>的key是方法,可以有*通配符。Value是事务传播行为:
抽象事务,其他biz类可以继承,从而简化配置
声明什么方法将在一个事务内运行,以及相关的事务参数每一个<prop>的key是方法,可以有*通配符。Value是事务传播行为:
继承上面的抽象
Aop空间也应该包含在内,新的声明事务要依赖一些新的Aop元素
定义通知,事务参数在一个<tx:atrributes>中定义,他包含一个或多个<tx:method>方法
txManager是上面已经定义的事务管理器。
定义通知器,告知那些类那些方法使用advice-ref="txAdvice"定义的事务通知
这是类层面的事务注入,说明下面所有的方法都支持事务,且是只读的。
该事务标签也可以注释接口,那么该接口的实现方法都支持事务。
代理和服务实现相同的接口,用代理调用服务的方法,而客户端调用代理的方法就像调用服务端的方法一样。客户端没有察觉。