spring 简入简出

47
Spring 研研研研

Upload: floria

Post on 12-Jan-2016

183 views

Category:

Documents


0 download

DESCRIPTION

Spring 简入简出. 引子 : 在没有 Spring 的日子里. EJB 的例子. EJB 容器架构 : 容器提供服务 组件提供业务逻辑. 口号 : 应用开发者只需 关注业务逻辑. 引子 : 在没有 Spring 的日子里. 写一个 EJB 组件真的只需要关注业务逻辑吗 ?. 一个最基本的 SLSB. 在没有 Spring 的日子里. 淘宝系统早期的各种 Factory. 在没有 Spring 的日子里. 淘宝系统早期的各种 Factory. BO 、 VOF 的构造方式类似. - PowerPoint PPT Presentation

TRANSCRIPT

研究院

Spring 简入简出

研究院

引子 : 在没有 Spring 的日子里

EJB 的例子

EJB容器事务 持久

分布 安全

EJB组件

业务逻辑

EJB组件

业务逻辑

EJB 容器架构 :•容器提供服务•组件提供业务逻辑

口号 :应用开发者只需

关注业务逻辑

研究院

引子 : 在没有 Spring 的日子里

写一个 EJB 组件真的只需要关注业务逻辑吗 ?

Bean实现类

Remote接口

Home接口

标准部署描述符

容器特定部署描述符

Jar Manifest

EJB封装工具 EJB组件

一个最基本的 SLSB

研究院

在没有 Spring 的日子里

淘宝系统早期的各种Factory

研究院

在没有 Spring 的日子里

<ao-factories> <ao-factory name="XXXAO" class="com....XXXAO" /> <ao-factory name="YYYAO" class="com....YYYAO"> <property name="pName" value="pValue"/> </ao-factory></ao-factories>

DefaultAOFactoryService.java.txt BO、 VOF的构造方式类似

淘宝系统早期的各种 Factory

研究院

Spring 的理念 : 一个目标,三个原则

目标•简化 J2EE 应用开发

原则•易事简为•难事善为•重用轮子

易事简为 难事善为

重用轮子

研究院

Spring 的方式 : 一个中心,三个基础

中心• POJO 编程模型

基础•反向控制•面向切面•服务抽象

反向控制(IoC)

面向切面(AOP)

服务抽象(Service Abstraction)

POJO

研究院

Spring 的言行一致性

中心• POJO 编程模型

基础•反向控制•面向切面•服务抽象

目标•简化 J2EE 应用开发

原则•易事简为•难事善为•重用轮子

研究院

POJO 编程模型

通过 POJI 松散耦合的 POJO• 对象合作 :IoC 将 POJO 根据依赖关系组装成一个对象网络

• 对象配置 :IoC 为 POJO 提供了一致、灵活的配置机制

• 公共服务 :AOP 以声明式非侵入的方式将企业服务应用于 POJO 之上

一个POJO

合作POJO

配置

公共服务事务、分布持久、安全审计、性能质量、管理

研究院

应用架构

视图生成

生成HTML等页面元素

动作处理

处理输入调用业务层选择视图

远程服务输出

Web service等协议处理

业务服务提供

提供业务功能控制业务规则管理事务边界等等

DAO接口

定义持久操作

DAO接口实现

查添删改对象

ORM

对象关系映射

关系数据库其它事务性资源

表现层 业务层 数据访问层

可持久化实体领域对象(核心OO模型)

研究院

I oC容器 AOP框架

DAO JMS事务 远程 J NDI

Spring应用框架

J2EE应用程序

Spring 框架布局

基于 POJO简单开发

消除重复代码

支持企业服务

研究院

Spring 运行时环境

Java运行环境(JRE)

资源 安全 事务 群集 消息持久

应用服务器

Spring容器

变更管理与运行监控

企业消息总线

企业数据库

Spring运行时环境

研究院

全景透视

Java运行环境(JRE)

资源 安全 事务 群集 消息持久

应用服务器

Spring容器

变更管理与运行监控

企业消息总线

企业数据库

Spring运行时环境

I oC容器 AOP框架

DAO JMS事务 远程 J NDI

Spring应用框架

J2EE应用程序

视图生成

生成HTML等页面元素

动作处理

处理输入调用业务层选择视图

远程服务输出

Web service等协议处理

业务服务提供

提供业务功能控制业务规则管理事务边界等等

DAO接口

定义持久操作

DAO接口实现

查添删改对象

ORM

对象关系映射

关系数据库其它事务性资源

表现层 业务层 数据访问层

可持久化实体领域对象(核心OO模型)

一个POJO

合作POJO

配置

公共服务事务、分布持久、安全审计、性能质量、管理

研究院

Inverse of Control :反向控制

好莱坞原则Don’t call me, I’ll call you.

反向控制原则由框架调用应用代码、控制全局流程;应用代码不调用框架。反向控制是一种原则

反向控制原则的普遍性EJB 、 Servlet 、 Webx Pipeline 、业务应用框架

研究院

反向控制实例 :Alipay 交易框架

交易加锁

修改交易状态和属性

设置超时

设置下一步动作

记录交易日志

存储数据

发送邮件

MQ通知淘宝

事务边界

修改交易模板

扩展槽

共享动作

交易请求

交易结果

修改交易状态和属性

设置超时

设置下一步动作

记录交易日志

存储数据

发送邮件

MQ通知淘宝

事务边界

创建交易模板

扩展槽

共享动作

交易请求

交易结果

研究院

反向控制实例 : Alipay 交易框架

由容器,而不是组件控制全局规则• 容器 -> 交易核心框架• 组件 -> 交易 Action• 全局规则 -> action 清单和序列、事务边界、并发控制等等

研究院

反向控制实例 : Alipay 交易框架

研究院

Dependency Injection: 依赖注入

依赖注入是 Spring 运用反向控制原则解决配置管理和对象关系管理的手段。

依赖注入的优势 :•代码简化•配置方式统一•不依赖特定框架或对象查找 API•自文档化,显式表达依赖关系

研究院

DI 的类型 : 设值注入

设值注入 (setter injection)public interface HollywoodService { public void callMe();}public class HollywoodServiceImpl implements HollywoodService { private hollywoodManager; public void setHollywoodService (HollywoodManager hollywoodManager) { this. hollywoodManager = hollywoodManager; } public void callMe() { System.out.println(“Don’t call me, I’ll call you!”);} }

<beans> <bean id=“hollywoodService” class=“HollywoodServiceImpl”> <property name=“hollywoodManager”> <ref bean=“hollywoodManager”/> </property> </bean></beans>

研究院

构造器注入 (constructor injection)

DI 的类型 : 构造器注入

public interface HollywoodService { public void callMe();}public class HollywoodServiceImpl implements HollywoodService { private hollywoodManager; public HollywoodServiceImpl (HollywoodManager hollywoodManager) { this. hollywoodManager = hollywoodManager; } public void callMe() { System.out.println(“Don’t call me, I’ll call you!”);} }

<beans> <bean id=“hollywoodService” class=“HollywoodServiceImpl”> <construtor-arg> <ref bean=“hollywoodManager”/> </ construtor-arg> </bean></beans>

研究院

方法注入 (method injection)

DI 的类型 : 方法注入

public interface HollywoodService { public void callMe();}public abstract class HollywoodServiceImpl implements HollywoodService { protected abstract HollywoodManager getHollywoodManager();

public void callMe() { System.out.println(“Don’t call me, I’ll call you!”); getHollywoodManager().notify(); } }

研究院

DI 的类型 : 方法注入 (cont.)

方法注入 (cont.)•容器在运行时动态生成方法的实现•实现的方式是通过容器对象查找•典型应用场景是将一个非单例的 bean 注入给一个单例的 bean ,实现每次引用时可以使用一个新的实例,满足线程安全性要求。

<beans> <bean id=“hollywoodService” class=“HollywoodServiceImpl”> <lookup-method name=“getHollywoodManager” bean=“hollywoodManager”/> </bean>

<bean id=“hollywoodManager” singleton=“false” class=“HollywoodManagerImpl”/></beans>

研究院

DI 实例 :FactoryBean

FactoryBean 是一个特别的接口,当它的实现类作为 bean 定义时,它代表的不是该类的实例,而是由它创建的对象。

FactoryBean 实例 :• JNDIObjectFactoryBean: 查找 JNDI 获取对象;应用与 JNDI API 解耦。

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>ZFBDataSource</value> </property></bean>

研究院

DI 实例 :FactoryBean(cont.)

PropertyPathFactoryBean: 获取指定 bean的属性值

<bean id=“person" class=“…“/><bean id=“theCity” class=“org.springframework.beans.factory.config.PropertyPathFactoryBean ”> <property name=“targetObject"><ref local=“person”/></property> <property name=“propertyPath"><value>address.city</value></property> </bean>

MethodInvokingFactoryBean: 调用指定 bean 的方法,获取返回值

研究院

DI 应用 : 基于 DB 的密钥管理器

场景 : 应用中的加解密操作过程中需要使用密钥,出于安全起见,密钥不应该出现在配置文件或源代码中。在没有专用硬件支持的情况下,一个可行的选择是将密钥保存在 DB 中,并在系统启动时将密钥注入到加解密操作对象中,如何实现 ?

研究院

DI 应用 : 基于 DB 的密钥管理器 (cont.)

public interface KeyManager { public String getKeyByName(String keyName); }

public class KeyManagerImpl { private KeyDAO keyDAO; private Map keyMap; public void init() { keyMap = keyDAO.findAll(); } public String getKeyByName(String keyName) { return (String) keyMap.get(keyName) }; }

研究院

DI 应用 : 基于 DB 的密钥管理器 (cont.)

public interface CryptoService { public String encrypt(String clearText); }

public class CryptoServiceImpl { private String key; private Encrypter encrypter; public String encrypt(String clearText) { encrypter.encrypt(key, clearText); } public void setKey(String key) { this.key = key; } }

研究院

DI 应用 : 基于 DB 的密钥管理器 (cont.) <bean id="keyManager" class="KeyManagerImpl“ init-method=“init”> <property name="keyDAO"><ref bean="keyDAO"/></property> </bean>

<bean id="cryptoService" class=cryptoServiceImpl"> <property name="key"> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryB

ean"> <property name="targetObject"><value>keyManager</value></property> <property name="targetMethod"><value>getKeyByName</value></property> <property name="arguments"> <list><value>cryptoKey</value></list> </property> </bean> </property> </bean>

研究院

AOP: 面向切面编程

理解 AOP: 交叉关注点会员 账

务交易

认证...

业务维度

开发维度

系统维度

WEB层

BIZ层

DAL层

事务

权限

日志

监控

分布

交叉关注点横切业务领域与架构分层

研究院

AOP: 面向切面编程

理解 AOP: 关注点分解

研究院

AOP 核心概念

切面 (aspect)需要实现的交叉功能逻辑。

通知 ?(advice)切面的实现。

连接点 (jointpoint)应用执行中需要插入切面的点。

切入点 (pointcut)一组连接点的集合,切面将应用于这组连接点。

研究院

AOP 核心概念

研究院

AOP 核心概念

引入 (introduction)为现存的类引入新的方法或属性。

目标对象 (target)被通知的对象。

代理 (proxy)将通知应用于目标对象之后得到的对象。

织入 (weaving)将通知应用于目标对象的过程。

研究院

Spring 的 AOP 支持

Spring 基于代理实现对 AOP 的支持•通过 J2SE dynamic proxies ,可以代理任意 Jav

a 接口。•通过 CGLIB 字节码生成,可以代理 Java 类。

Spring 的代理机制可以支持由 Spring 容器创建的 bean ,无法支持应用中自行 new 出来的 bean 。

研究院

Spring AOP 应用基本步骤

创建通知(Advice)

创建通知者(Advisor)定义切入点并组合切入点与通知

使用ProxyFactoryBean代理目标对象

自动创建?

N

创建自动代理Bean由容器创建代理对象

Y

自动创建?

N

Y

研究院

创建 AOP 通知

通知类型 接口 描述

Around org.aopalliance.intercept.MethodInterceptor 拦截对目标对象方法调用

Before org.springframework.aop.BeforeAdvice 在目标方法被调用之前调用

After org.springframework.aop.AfterReturningAdvice 在目标方法被调用之后调用

Throws org.springframework.aop.ThrowsAdvice 当目标方法抛出异常时调用

研究院

创建 AOP 通知示例

环绕通知示例public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable;}

public class PerformanceMonitorInterceptor implements MethodInterceptor { private int threshold = 1;.. public Object invoke(MethodInvocation invocation) throws Throwable { long startTime = System.currentTimeMillis(); try { return invocation.proceed(); } finally { long elapseTime = System.currentTimeMillis() - startTime; if (elapseTime > threshold) { logger.info(" 执行时间超过阈值,实际执行时间为 " + elapseTime + " 毫秒。 "); } } }}

研究院

定义切入点

Spring 提供的静态切入点• NameMatchMethodPointcutAdvisor: 匹配调用方法的名字

• RegexpMethodPointcutAdvisor: 使用正则表达式匹配调用的类名与方法

<bean id="samplePointcutAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <property name="mappedName"> <value>order*</value> </property> <property name="advice"> <ref bean="sampleAdvice"/> </property></bean>

研究院

用 ProxyFactoryBean 织入通知

属性 使用target 代理的目标对象proxyInterfaces 代理应该实现的接口列表interceptorNames 需要应用到目标对象上的通知 Bean 的名字。可

以是拦截器、 Advisor 或其他通知类型的名字。这个属性必须按照在 BeanFactory 中使用的顺序设置。

<bean id="tradeDealer" class="org.springframework.aop.framework.ProxyFactoryBean">

<property name="proxyInterfaces"><value>com.beyond.biz.tradecore.TradeDealer</value>

</property><property name="interceptorNames">

<list><value>tradeDealerPointcutAdvisor</value>

</list></property><property name="target"><ref bean="tradeDealerTarget"/></property>

</bean>

研究院

自动创建代理

BeanNameAutoProxyCreator为符合相同命名规则的 Bean 应用一个或一组切面。

DefaultAdvisorAutoProxyCreator实现了 BeanPostProcessor 接口。当 ApplicationContext读入所有 Bean 的配置信息后,DefaultAdvisorAutoProxyCreator 将扫描上下文,寻找所有的 Advisor 。它将这些 Advisor 应用到所有符合 Advisor 切入点的 Bean 中。

研究院

自动创建代理示例

<bean id="daoAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

<property name="interceptorNames"><list><value>daoPerformanceMonitorInterceptor</value></list>

</property><property name="beanNames">

<value>*DAO</value></property>

</bean>

研究院

AOP 应用示例 : 将消息发送统一移到事务外

应用场景 : 在对象 B 中,发送邮件 ( 通过异步消息实现 ) 被明智地放在事务外,避免出现事务回滚 (相当于业务操作未执行 ) 而邮件发送出去的情况。但对象 A 的doA() 方法在事务中调用对象 B 的 doB() 方法,并且假设事务传播属性为 PROPAGATION_REQUIRED ,造成发送邮件操作实际包含在事务中了,于是当 doA() 方法的事务回滚时,邮件已经发送出去,给用户带来困扰。

启动事务

更新DB

提交事务

objectB.doB()

objectA.doA()

启动事务

更新DB

发送邮件

提交事务

objectB.doB()

objectA objectBdoB()

研究院

AOP 应用示例 : 将消息发送统一移到事务外 (cont.)

需求 : 在不改变业务代码的前提下,确保系统中 所有的异步消息都移到事务提交成功之后执行

这个需求 (异步消息需要在事务提交之后再发送 ) 是一个交叉关注点,横切所有的消息发送操作,因此,适于用 AOP 来实现。

第1步:创建Advice,拦截所有消息发送操作

第2步:配置Spring自动创建代理,将Advice应用到所有的异步消息发送对象中

研究院

AOP 应用示例 : 将消息发送统一移到事务外 (cont.)

第 1步,创建 Advice ,拦截消息发送操作public class AfterCommitCommandInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { if (StringUtil.equals(invocation.getMethod().getName(), "execute")) { // 只对 execute 方法进行拦截 if (TransactionSynchronizationManager.isActualTransactionActive()) { // 由于当前处于活动事务中,因此向事务同步管理器注册同步对象,在事务提交后再执行真正的命令发送。 TransactionSynchronizationManager.registerSynchronization(new MethodInvocationTransactionSynchronization(invocation)); return new ResultSupport(true, ResultCode.SUCCESS); } else { return invocation.proceed(); } } else { return invocation.proceed(); } }}

研究院

AOP 应用示例 : 将消息发送统一移到事务外 (cont.)

辅助类 :MethodInvocationTransactionSynchronization 的实现

public class MethodInvocationTransactionSynchronization implements TransactionSynchronization { private MethodInvocation methodInvocation;

public void afterCompletion(int status) { if (status == STATUS_COMMITTED) { try { Object result = methodInvocation.proceed(); } catch (Exception e) { } } }}

研究院

AOP 应用示例 : 将消息发送统一移到事务外 (cont.)

第 2步,配置 Spring 自动创建代理,应用 Advice

<bean id="afterCommitCommandInterceptor" class="com.iwallet.biz.common.aop.AfterCommitCommandInterceptor"/>

<bean id="afterCommitCommandAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

<property name="interceptorNames"><list><value>afterCommitCommandInterceptor</value></list>

</property><property name="beanNames"><!-- 这些都是发送异步消息的 CommandDispatcher Bean --> <value>me

ssageDispatcher,bankQueryDispatcher,externalNotifyDispatcher,eventMessageDispatcher,gotoneDispatcher</value>

</property></bean>

研究院

讨论与交流