#
Mysql分頁采用limt關鍵字
select * from t_order limit 5,10; #返回第6-15行數據
select * from t_order limit 5; #返回前5行
select * from t_order limit 0,5; #返回前5行
Mssql 2000分頁采用top關鍵字(20005以上版本也支持關鍵字rownum)
Select top 10 * from t_order where id not in (select id from t_order where id>5 ); //返回第6到15行數據
其中10表示取10記錄 5表示從第5條記錄開始取
Oracle分頁
①采用rownum關鍵字(三層嵌套)
SELECT * FROM(
SELECT A.*,ROWNUM num FROM
(SELECT * FROM t_order)A
WHERE
ROWNUM<=15)
WHERE num>=5;--返回第5-15行數據
②采用row_number解析函數進行分頁(效率更高)
SELECT xx.* FROM(
SELECT t.*,row_number() over(ORDER BY o_id)AS num
FROM t_order t
)xx
WHERE num BETWEEN 5 AND 15;
--返回第5-15行數據
解析函數能用格式
函數() over(pertion by 字段 order by 字段);
Pertion 按照某個字段分區
Order 按照勒個字段排序
Struts1和Struts2的區別和對比:
Action 類:
• Struts1要求Action類繼承一個抽象基類。Struts1的一個普遍問題是使用抽象類編程而不是接口。
• Struts 2 Action類可以實現一個Action接口,也可實現其他接口,使可選和定制的服務成為可能。Struts2提供一個ActionSupport基類去 實現 常用的接口。Action接口不是必須的,任何有execute標識的POJO對象都可以用作Struts2的Action對象。
線程模式:
• Struts1 Action是單例模式并且必須是線程安全的,因為僅有Action的一個實例來處理所有的請求。單例策略限制了Struts1 Action能作的事,并且要在開發時特別小心。Action資源必須是線程安全的或同步的。
• Struts2 Action對象為每一個請求產生一個實例,因此沒有線程安全問題。(實際上,servlet容器給每個請求產生許多可丟棄的對象,并且不會導致性能和垃圾回收問題)
Servlet 依賴:
• Struts1 Action 依賴于Servlet API ,因為當一個Action被調用時HttpServletRequest 和 HttpServletResponse 被傳遞給execute方法。
• Struts 2 Action不依賴于容器,允許Action脫離容器單獨被測試。如果需要,Struts2 Action仍然可以訪問初始的request和response。但是,其他的元素減少或者消除了直接訪問HttpServetRequest 和 HttpServletResponse的必要性。
可測性:
• 測試Struts1 Action的一個主要問題是execute方法暴露了servlet API(這使得測試要依賴于容器)。一個第三方擴展--Struts TestCase--提供了一套Struts1的模擬對象(來進行測試)。
• Struts 2 Action可以通過初始化、設置屬性、調用方法來測試,“依賴注入”支持也使測試更容易。
捕獲輸入:
• Struts1 使用ActionForm對象捕獲輸入。所有的ActionForm必須繼承一個基類。因為其他JavaBean不能用作ActionForm,開發者經 常創建多余的類捕獲輸入。動態Bean(DynaBeans)可以作為創建傳統ActionForm的選擇,但是,開發者可能是在重新描述(創建)已經存 在的JavaBean(仍然會導致有冗余的javabean)。
• Struts 2直接使用Action屬性作為輸入屬性,消除了對第二個輸入對象的需求。輸入屬性可能是有自己(子)屬性的rich對象類型。Action屬性能夠通過 web頁面上的taglibs訪問。Struts2也支持ActionForm模式。rich對象類型,包括業務對象,能夠用作輸入/輸出對象。這種 ModelDriven 特性簡化了taglib對POJO輸入對象的引用。
表達式語言:
• Struts1 整合了JSTL,因此使用JSTL EL。這種EL有基本對象圖遍歷,但是對集合和索引屬性的支持很弱。
• Struts2可以使用JSTL,但是也支持一個更強大和靈活的表達式語言--"Object Graph Notation Language" (OGNL).
綁定值到頁面(view):
• Struts 1使用標準JSP機制把對象綁定到頁面中來訪問。
• Struts 2 使用 "ValueStack"技術,使taglib能夠訪問值而不需要把你的頁面(view)和對象綁定起來。ValueStack策略允許通過一系列名稱相同但類型不同的屬性重用頁面(view)。
類型轉換:
• Struts 1 ActionForm 屬性通常都是String類型。Struts1使用Commons-Beanutils進行類型轉換。每個類一個轉換器,對每一個實例來說是不可配置的。
• Struts2 使用OGNL進行類型轉換。提供基本和常用對象的轉換器。
校驗:
• Struts 1支持在ActionForm的validate方法中手動校驗,或者通過Commons Validator的擴展來校驗。同一個類可以有不同的校驗內容,但不能校驗子對象。
• Struts2支持通過validate方法和XWork校驗框架來進行校驗。XWork校驗框架使用為屬性類類型定義的校驗和內容校驗,來支持chain校驗子屬性
Action執行的控制:
• Struts1支持每一個模塊有單獨的Request Processors(生命周期),但是模塊中的所有Action必須共享相同的生命周期。
• Struts2支持通過攔截器堆棧(Interceptor Stacks)為每一個Action創建不同的生命周期。堆棧能夠根據需要和不同的Action一起使用。
摘要: 我們看看Spring中的事務處理的代碼,使用Spring管理事務有聲明式和編程式兩種方式,聲明式事務處理通過AOP的實現把事物管理代碼作為方面封裝來橫向插入到業務代碼中,使得事務管理代碼和業務代碼解藕。在這種方式我們結合IoC容器和Spirng已有的FactoryBean來對事務管理進行屬性配置,比如傳播行為,隔離級別等。其中最簡單的方式就是通過配置TransactionProxyFactoryB...
閱讀全文
前面我們分析了Spring AOP實現中得到Proxy對象的過程,下面我們看看在Spring AOP中攔截器鏈是怎樣被調用的,也就是Proxy模式是怎樣起作用的,或者說Spring是怎樣為我們提供AOP功能的;
在JdkDynamicAopProxy中生成Proxy對象的時候:
- return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
這里的this參數對應的是InvocationHandler對象,這里我們的JdkDynamicAopProxy實現了這個接口,也就是說當Proxy對象的函數被調用的時候,這個InvocationHandler的invoke方法會被作為回調函數調用,下面我們看看這個方法的實現:
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- MethodInvocation invocation = null;
- Object oldProxy = null;
- boolean setProxyContext = false;
-
- TargetSource targetSource = this.advised.targetSource;
- Class targetClass = null;
- Object target = null;
-
- try {
-
-
-
- if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
-
-
- return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
- }
- if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
-
- return new Integer(hashCode());
- }
- if (Advised.class == method.getDeclaringClass()) {
-
- return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
- }
-
- Object retVal = null;
-
- if (this.advised.exposeProxy) {
-
- oldProxy = AopContext.setCurrentProxy(proxy);
- setProxyContext = true;
- }
-
-
-
-
- target = targetSource.getTarget();
- if (target != null) {
- targetClass = target.getClass();
- }
-
-
-
- List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
- this.advised, proxy, method, targetClass);
-
-
-
-
- if (chain.isEmpty()) {
-
-
-
- retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
- }
- else {
-
-
-
-
-
- invocation = new ReflectiveMethodInvocation(
- proxy, target, method, args, targetClass, chain);
-
-
-
- retVal = invocation.proceed();
- }
-
-
- if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {
-
-
-
- retVal = proxy;
- }
- return retVal;
- }
- finally {
- if (target != null && !targetSource.isStatic()) {
-
- targetSource.releaseTarget(target);
- }
-
- if (setProxyContext) {
-
- AopContext.setCurrentProxy(oldProxy);
- }
- }
- }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation = null;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Class targetClass = null;
Object target = null;
try {
// Try special rules for equals() method and implementation of the
// Advised AOP configuration interface.
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// What if equals throws exception!?
// This class implements the equals(Object) method itself.
return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// This class implements the hashCode() method itself.
return new Integer(hashCode());
}
if (Advised.class == method.getDeclaringClass()) {
// service invocations on ProxyConfig with the proxy config
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal = null;
if (this.advised.exposeProxy) {
// make invocation available if necessary
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
// 這里是得到目標對象的地方,當然這個目標對象可能來自于一個實例池或者是一個簡單的JAVA對象
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// get the interception chain for this method
// 這里獲得定義好的攔截器鏈
List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this.advised, proxy, method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
// 如果沒有設定攔截器,那么我們就直接調用目標的對應方法
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping or fancy proxying
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
// invocation = advised.getMethodInvocationFactory().getMethodInvocation(
// proxy, method, targetClass, target, args, chain, advised);
// 如果有攔截器的設定,那么需要調用攔截器之后才調用目標對象的相應方法
// 這里通過構造一個ReflectiveMethodInvocation來實現,下面我們會看這個ReflectiveMethodInvocation類
invocation = new ReflectiveMethodInvocation(
proxy, target, method, args, targetClass, chain);
// proceed to the joinpoint through the interceptor chain
// 這里通過ReflectiveMethodInvocation來調用攔截器鏈和相應的目標方法
retVal = invocation.proceed();
}
// massage return value if necessary
if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy)) {
// Special case: it returned "this" and the return type of the method is type-compatible
// Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// must have come from TargetSource
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// restore old proxy
AopContext.setCurrentProxy(oldProxy);
}
}
}
我們先看看目標對象方法的調用,這里是通過AopUtils的方法調用 - 使用反射機制來對目標對象的方法進行調用:
- public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
- throws Throwable {
-
-
-
- try {
- if (!Modifier.isPublic(method.getModifiers()) ||
- !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
- method.setAccessible(true);
- }
- return method.invoke(target, args);
- }
- catch (InvocationTargetException ex) {
-
-
- throw ex.getTargetException();
- }
- catch (IllegalArgumentException ex) {
- throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
- method + "] on target [" + target + "]", ex);
- }
- catch (IllegalAccessException ex) {
- throw new AopInvocationException("Couldn't access method: " + method, ex);
- }
- }
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
// 利用放射機制得到相應的方法,并且調用invoke
try {
if (!Modifier.isPublic(method.getModifiers()) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true);
}
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Couldn't access method: " + method, ex);
}
}
對攔截器鏈的調用處理是在ReflectiveMethodInvocation里實現的:
- public Object proceed() throws Throwable {
-
-
- if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) {
- return invokeJoinpoint();
- }
-
- Object interceptorOrInterceptionAdvice =
- this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);
- if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
-
-
-
- InterceptorAndDynamicMethodMatcher dm =
- (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
- if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
- return dm.interceptor.invoke(nextInvocation());
- }
- else {
-
-
-
- this.currentInterceptorIndex++;
- return proceed();
- }
- }
- else {
-
-
- return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation());
- }
- }
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 這里直接調用目標對象的方法,沒有攔截器的調用或者攔截器已經調用完了,這個currentInterceptorIndex的初始值是0
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
// 這里獲得相應的攔截器,如果攔截器可以匹配的上的話,那就調用攔截器的invoke方法
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(nextInvocation());
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
// 如果攔截器匹配不上,那就調用下一個攔截器,這個時候攔截器鏈的位置指示后移并迭代調用當前的proceed方法
this.currentInterceptorIndex++;
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(nextInvocation());
}
}
這里把當前的攔截器鏈以及在攔截器鏈的位置標志都clone到一個MethodInvocation對象了,作用是當前的攔截器執行完之后,會繼續沿著得到這個攔截器鏈執行下面的攔截行為,也就是會迭代的調用上面這個proceed:
- private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {
- ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();
- invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;
- invocation.parent = this;
- return invocation;
- }
private ReflectiveMethodInvocation nextInvocation() throws CloneNotSupportedException {
ReflectiveMethodInvocation invocation = (ReflectiveMethodInvocation) clone();
invocation.currentInterceptorIndex = this.currentInterceptorIndex + 1;
invocation.parent = this;
return invocation;
}
這里的nextInvocation就已經包含了當前的攔截鏈的基本信息,我們看到在Interceptor中的實現比如TransactionInterceptor的實現中:
- public Object invoke(final MethodInvocation invocation) throws Throwable {
- ......
- try {
-
- retVal = invocation.proceed();
- }
- ......
- else {
- try {
- Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
- new TransactionCallback() {
- public Object doInTransaction(TransactionStatus status) {
-
- TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
-
- try {
- return invocation.proceed();
- }
- ......
- }
public Object invoke(final MethodInvocation invocation) throws Throwable {
......//這里是TransactionInterceptor插入的事務處理代碼,我們會在后面分析事務處理實現的時候進行分析
try {
//這里是對配置的攔截器鏈進行迭代處理的調用
retVal = invocation.proceed();
}
......//省略了和事務處理的異常處理代碼 ,也是TransactionInterceptor插入的處理
else {
try {
Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,
new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
//這里是TransactionInterceptor插入對事務處理的代碼
TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);
//這里是對配置的攔截器鏈進行迭代處理的調用,接著順著攔截器進行處理
try {
return invocation.proceed();
}
......//省略了和事務處理的異常處理代碼 ,也是TransactionInterceptor插入的處理
}
從上面的分析我們看到了Spring AOP的基本實現,比如Spring怎樣得到Proxy,怎樣利用JAVA Proxy以及反射機制對用戶定義的攔截器鏈進行處理。
下面我們來看看Spring的AOP的一些相關代碼是怎么得到Proxy的,讓我們我們先看看AOP和Spring AOP的一些基本概念:
Advice:
通知,制定在連接點做什么,在Sping中,他主要描述Spring圍繞方法調用注入的額外的行為,Spring提供的通知類型有:
before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice,這些都是Spring AOP定義的接口類,具體的動作實現需要用戶程序來完成。
Pointcut:
切點,其決定一個advice應該應用于哪個連接點,也就是需要插入額外處理的地方的集合,例如,被某個advice作為目標的一組方法。Spring pointcut通常意味著標示方法,可以選擇一組方法調用作為pointcut,Spring提供了具體的切點來給用戶使用,比如正則表達式切點 JdkRegexpMethodPointcut通過正則表達式對方法名進行匹配,其通過使用 AbstractJdkRegexpMethodPointcut中的對MethodMatcher接口的實現來完成pointcut功能:
- public final boolean matches(Method method, Class targetClass) {
-
- String patt = method.getDeclaringClass().getName() + "." + method.getName();
- for (int i = 0; i < this.patterns.length; i++) {
-
- boolean matched = matches(patt, i);
- if (matched) {
- for (int j = 0; j < this.excludedPatterns.length; j++) {
- boolean excluded = matchesExclusion(patt, j);
- if(excluded) {
- return false;
- }
- }
- return true;
- }
- }
- return false;
- }
public final boolean matches(Method method, Class targetClass) {
//這里通過放射得到方法的全名
String patt = method.getDeclaringClass().getName() + "." + method.getName();
for (int i = 0; i < this.patterns.length; i++) {
// 這里是判斷是否和方法名是否匹配的代碼
boolean matched = matches(patt, i);
if (matched) {
for (int j = 0; j < this.excludedPatterns.length; j++) {
boolean excluded = matchesExclusion(patt, j);
if(excluded) {
return false;
}
}
return true;
}
}
return false;
}
在JDKRegexpMethodPointcut中通過JDK中的正則表達式匹配來完成pointcut的最終確定:
- protected boolean matches(String pattern, int patternIndex) {
- Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
- return matcher.matches();
- }
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
Advisor:
當我們完成額外的動作設計(advice)和額外動作插入點的設計(pointcut)以后,我們需要一個對象把他們結合起來,這就是通知器 - advisor,定義應該在哪里應用哪個通知。Advisor的實現有:DefaultPointcutAdvisor他有兩個屬性advice和 pointcut來讓我們配置advice和pointcut。
接著我們就可以通過ProxyFactoryBean來配置我們的代理對象和方面行為,在ProxyFactoryBean中有interceptorNames來配置已經定義好的通知器-advisor,雖然這里的名字叫做interceptNames,但實際上是供我們配置advisor的地方,具體的代理實現通過JDK 的Proxy或者CGLIB來完成。因為ProxyFactoryBean是一個FactoryBean,在ProxyFactoryBean中我們通過getObject()可以直接得到代理對象:
- public Object getObject() throws BeansException {
-
- initializeAdvisorChain();
- if (isSingleton()) {
-
- return getSingletonInstance();
- }
- else {
- .......
-
- return newPrototypeInstance();
- }
- }
public Object getObject() throws BeansException {
//這里初始化通知器鏈
initializeAdvisorChain();
if (isSingleton()) {
//根據定義需要生成單件的Proxy
return getSingletonInstance();
}
else {
.......
//這里根據定義需要生成Prototype類型的Proxy
return newPrototypeInstance();
}
}
我們看看怎樣生成單件的代理對象:
- private synchronized Object getSingletonInstance() {
- if (this.singletonInstance == null) {
- this.targetSource = freshTargetSource();
- if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
-
- setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
- }
-
- super.setFrozen(this.freezeProxy);
-
- this.singletonInstance = getProxy(createAopProxy());
-
-
- addListener(this);
- }
- return this.singletonInstance;
- }
-
-
- protected Object getProxy(AopProxy aopProxy) {
- return aopProxy.getProxy(this.beanClassLoader);
- }
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// 這里設置代理對象的接口
setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
}
// Eagerly initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
// 注意這里的方法會使用ProxyFactory來生成我們需要的Proxy
this.singletonInstance = getProxy(createAopProxy());
// We must listen to superclass advice change events to recache the singleton
// instance if necessary.
addListener(this);
}
return this.singletonInstance;
}
//使用createAopProxy放回的AopProxy來得到代理對象。
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.beanClassLoader);
}
ProxyFactoryBean的父類是AdvisedSupport,Spring使用AopProxy接口把AOP代理的實現與框架的其他部分分離開來;在AdvisedSupport中通過這樣的方式來得到AopProxy,當然這里需要得到AopProxyFactory的幫助 - 下面我們看到Spring為我們提供的實現,來幫助我們方便的從JDK或者cglib中得到我們想要的代理對象:
- protected synchronized AopProxy createAopProxy() {
- if (!this.isActive) {
- activate();
- }
- return getAopProxyFactory().createAopProxy(this);
- }
protected synchronized AopProxy createAopProxy() {
if (!this.isActive) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
而在ProxyConfig中對使用的AopProxyFactory做了定義:
-
-
- private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
//這個DefaultAopProxyFactory是Spring用來生成AopProxy的地方,
//當然了它包含JDK和Cglib兩種實現方式。
private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory();
其中在DefaultAopProxyFactory中是這樣生成AopProxy的:
- public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
-
- if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
- advisedSupport.getProxiedInterfaces().length == 0) {
-
- if (!cglibAvailable) {
- throw new AopConfigException(
- "Cannot proxy target class because CGLIB2 is not available. " +
- "Add CGLIB to the class path or specify proxy interfaces.");
- }
-
- return CglibProxyFactory.createCglibProxy(advisedSupport);
- }
- else {
-
- return new JdkDynamicAopProxy(advisedSupport);
- }
- }
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
//首先考慮使用cglib來實現代理對象,當然如果同時目標對象不是接口的實現類的話
if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
advisedSupport.getProxiedInterfaces().length == 0) {
//這里判斷如果不存在cglib庫,直接拋出異常。
if (!cglibAvailable) {
throw new AopConfigException(
"Cannot proxy target class because CGLIB2 is not available. " +
"Add CGLIB to the class path or specify proxy interfaces.");
}
// 這里使用Cglib來生成Proxy,如果target不是接口的實現的話,返回cglib類型的AopProxy
return CglibProxyFactory.createCglibProxy(advisedSupport);
}
else {
// 這里使用JDK來生成Proxy,返回JDK類型的AopProxy
return new JdkDynamicAopProxy(advisedSupport);
}
}
于是我們就可以看到其中的代理對象可以由JDK或者Cglib來生成,我們看到JdkDynamicAopProxy類和Cglib2AopProxy都實現的是AopProxy的接口,在JdkDynamicAopProxy實現中我們可以看到Proxy是怎樣生成的:
- public Object getProxy(ClassLoader classLoader) {
- if (logger.isDebugEnabled()) {
- Class targetClass = this.advised.getTargetSource().getTargetClass();
- logger.debug("Creating JDK dynamic proxy" +
- (targetClass != null ? " for [" + targetClass.getName() + "]" : ""));
- }
- Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
- findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
-
- return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
- }
public Object getProxy(ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
Class targetClass = this.advised.getTargetSource().getTargetClass();
logger.debug("Creating JDK dynamic proxy" +
(targetClass != null ? " for [" + targetClass.getName() + "]" : ""));
}
Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//這里我們調用JDK Proxy來生成需要的Proxy實例
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
這樣用Proxy包裝target之后,通過ProxyFactoryBean得到對其方法的調用就被Proxy攔截了, ProxyFactoryBean的getObject()方法得到的實際上是一個Proxy了,我們的target對象已經被封裝了。對 ProxyFactoryBean這個工廠bean而言,其生產出來的對象是封裝了目標對象的代理對象。
下面我們看看Spring JDBC相關的實現,
在Spring中,JdbcTemplate是經常被使用的類來幫助用戶程序操作數據庫,在JdbcTemplate為用戶程序提供了許多便利的數據庫操作方法,比如查詢,更新等,而且在Spring中,有許多類似 JdbcTemplate的模板,比如HibernateTemplate等等 - 看來這是Rod.Johnson的慣用手法,一般而言這種Template中都是通過回調函數CallBack類的使用來完成功能的,客戶需要在回調接口中實現自己需要的定制行為,比如使用客戶想要用的SQL語句等。不過往往Spring通過這種回調函數的實現已經為我們提供了許多現成的方法供客戶使用。一般來說回調函數的用法采用匿名類的方式來實現,比如:
- JdbcTemplate = new JdbcTemplate(datasource);
- jdbcTemplate.execute(new CallBack(){
- public CallbackInterfacedoInAction(){
- ......
-
- }
- }
JdbcTemplate = new JdbcTemplate(datasource);
jdbcTemplate.execute(new CallBack(){
public CallbackInterfacedoInAction(){
......
//用戶定義的代碼或者說Spring替我們實現的代碼
}
}
在模板中嵌入的是需要客戶化的代碼,由Spring來作或者需要客戶程序親自動手完成。下面讓我們具體看看在JdbcTemplate中的代碼是怎樣完成使命的,我們舉JdbcTemplate.execute()為例,這個方法是在JdbcTemplate中被其他方法調用的基本方法之一,客戶程序往往用這個方法來執行基本的SQL語句:
- public Object execute(ConnectionCallback action) throws DataAccessException {
-
- Connection con = DataSourceUtils.getConnection(getDataSource());
- try {
- Connection conToUse = con;
-
- if (this.nativeJdbcExtractor != null) {
-
- conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
- }
- else {
-
- conToUse = createConnectionProxy(con);
- }
-
- return action.doInConnection(conToUse);
- }
- catch (SQLException ex) {
-
-
- DataSourceUtils.releaseConnection(con, getDataSource());
- con = null;
- throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
- }
- finally {
-
- DataSourceUtils.releaseConnection(con, getDataSource());
- }
- }
public Object execute(ConnectionCallback action) throws DataAccessException {
//這里得到數據庫聯接
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse = con;
//有些特殊的數據庫,需要我們使用特別的方法取得datasource
if (this.nativeJdbcExtractor != null) {
// Extract native JDBC Connection, castable to OracleConnection or the like.
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
else {
// Create close-suppressing Connection proxy, also preparing returned Statements.
conToUse = createConnectionProxy(con);
}
//這里調用的是傳遞進來的匿名類的方法,也就是用戶程序需要實現CallBack接口的地方。
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
//如果捕捉到數據庫異常,把數據庫聯接釋放,同時拋出一個經過Spring轉換過的Spring數據庫異常,
//我們知道,Spring做了一個有意義的工作是把這些數據庫異常統一到自己的異常體系里了。
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
}
finally {
//最后不管怎樣都會把數據庫連接釋放
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
對于JdbcTemplate中給出的其他方法,比如query,update,execute等的實現,我們看看query():
- public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
- throws DataAccessException {
- ..........
-
- return execute(psc, new PreparedStatementCallback() {
- public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
-
- ResultSet rs = null;
- try {
-
- if (pss != null) {
- pss.setValues(ps);
- }
-
- rs = ps.executeQuery();
- ResultSet rsToUse = rs;
- if (nativeJdbcExtractor != null) {
- rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
- }
-
- return rse.extractData(rsToUse);
- }
- finally {
-
- JdbcUtils.closeResultSet(rs);
- if (pss instanceof ParameterDisposer) {
- ((ParameterDisposer) pss).cleanupParameters();
- }
- }
- }
- });
- }
public Object query(PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor rse)
throws DataAccessException {
..........
//這里調用了我們上面看到的execute()基本方法,然而這里的回調實現是Spring為我們完成的查詢過程
return execute(psc, new PreparedStatementCallback() {
public Object doInPreparedStatement(PreparedStatement ps) throws SQLException {
//準備查詢結果集
ResultSet rs = null;
try {
//這里配置SQL參數
if (pss != null) {
pss.setValues(ps);
}
//這里執行的SQL查詢
rs = ps.executeQuery();
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
//返回需要的記錄集合
return rse.extractData(rsToUse);
}
finally {
//最后關閉查詢的紀錄集,對數據庫連接的釋放在execute()中釋放,就像我們在上面分析的看到那樣。
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}
輔助類DataSourceUtils來用來對數據庫連接進行管理的主要工具,比如打開和關閉數據庫連接等基本操作:
- public static Connection doGetConnection(DataSource dataSource) throws SQLException {
-
- ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
- if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
- conHolder.requested();
- if (!conHolder.hasConnection()) {
- logger.debug("Fetching resumed JDBC Connection from DataSource");
- conHolder.setConnection(dataSource.getConnection());
- }
- return conHolder.getConnection();
- }
-
- logger.debug("Fetching JDBC Connection from DataSource");
- Connection con = dataSource.getConnection();
-
- if (TransactionSynchronizationManager.isSynchronizationActive()) {
- logger.debug("Registering transaction synchronization for JDBC Connection");
-
-
- ConnectionHolder holderToUse = conHolder;
- if (holderToUse == null) {
- holderToUse = new ConnectionHolder(con);
- }
- else {
- holderToUse.setConnection(con);
- }
- holderToUse.requested();
- TransactionSynchronizationManager.registerSynchronization(
- new ConnectionSynchronization(holderToUse, dataSource));
- holderToUse.setSynchronizedWithTransaction(true);
- if (holderToUse != conHolder) {
- TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
- }
- }
-
- return con;
- }
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
//把對數據庫連接放到事務管理里面進行管理
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// 這里得到需要的數據庫連接,在配置文件中定義好的。
logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
那我們實際的DataSource對象是怎樣得到的?很清楚我們需要在上下文中進行配置:它作為JdbcTemplate父類JdbcAccessor的屬性存在:
- public abstract class JdbcAccessor implements InitializingBean {
-
-
- private DataSource dataSource;
-
-
- private SQLExceptionTranslator exceptionTranslator;
-
- private boolean lazyInit = true;
-
- ........
- }
public abstract class JdbcAccessor implements InitializingBean {
/** 這里是我們依賴注入數據庫數據源的地方。 */
private DataSource dataSource;
/** Helper to translate SQL exceptions to DataAccessExceptions */
private SQLExceptionTranslator exceptionTranslator;
private boolean lazyInit = true;
........
}
而對于DataSource的緩沖池實現,我們通過定義Apache Jakarta Commons DBCP或者C3P0提供的DataSource來完成,然后只要在上下文中配置好就可以使用了。從上面我們看到JdbcTemplate提供了許多簡單查詢和更新功能,但是如果需要更高層次的抽象,以及更面向對象的方法來訪問數據庫。Spring為我們提供了org.springframework.jdbc.object包,這里面包含了SqlQuery,SqlMappingQuery, SqlUpdate和StoredProcedure等類,這些類都是Spring JDBC應用程序可以使用的主要類,但我們要注意使用這些類的時候,用戶需要為他們配置好一個JdbcTemplate作為其基本的操作的實現。
比如說我們使用MappingSqlQuery來將表數據直接映射到一個對象集合 - 具體可以參考書中的例子
1.我們需要建立DataSource和sql語句并建立持有這些對象的MappingSqlQuery對象
2.然后我們需要定義傳遞的SqlParameter,具體的實現我們在MappingSqlQuery的父類RdbmsOperation中可以找到:
- public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
-
- if (isCompiled()) {
- throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");
- }
-
- this.declaredParameters.add(param);
public void declareParameter(SqlParameter param) throws InvalidDataAccessApiUsageException {
//如果聲明已經被編譯過,則該聲明無效
if (isCompiled()) {
throw new InvalidDataAccessApiUsageException("Cannot add parameters once query is compiled");
}
//這里對參數值進行聲明定義
this.declaredParameters.add(param);
}
而這個declareParameters維護的是一個列表:
-
- private List declaredParameters = new LinkedList();
/** List of SqlParameter objects */
private List declaredParameters = new LinkedList();
這個列表在以后compile的過程中會被使用。
3.然后用戶程序需要實現MappingSqlQuery的mapRow接口,將具體的ResultSet數據生成我們需要的對象,這是我們迭代使用的方法。1,2,3步實際上為我們定義好了一個迭代的基本單元作為操作模板。
4.在應用程序,我們直接調用execute()方法得到我們需要的對象列表,列表中的每一個對象的數據來自于執行SQL語句得到記錄集的每一條記錄,事實上執行的execute在父類SqlQuery中起作用:
- public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {
- validateNamedParameters(paramMap);
- Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);
- RowMapper rowMapper = newRowMapper(parameters, context);
- String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));
-
- return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);
- }
public List executeByNamedParam(Map paramMap, Map context) throws DataAccessException {
validateNamedParameters(paramMap);
Object[] parameters = NamedParameterUtils.buildValueArray(getSql(), paramMap);
RowMapper rowMapper = newRowMapper(parameters, context);
String sqlToUse = NamedParameterUtils.substituteNamedParameters(getSql(), new MapSqlParameterSource(paramMap));
//我們又看到了JdbcTemplate,這里使用JdbcTemplate來完成對數據庫的查詢操作,所以我們說JdbcTemplate是基本的操作類。
return getJdbcTemplate().query(newPreparedStatementCreator(sqlToUse, parameters), rowMapper);
}
在這里我們可以看到template模式的精彩應用和對JdbcTemplate的靈活使用。通過使用它,我們免去了手工迭代ResultSet并將其中的數據轉化為對象列表的重復過程。在這里我們只需要定義SQL語句和SqlParameter - 如果需要的話,往往SQL語句就常常能夠滿足我們的要求了。這是靈活使用JdbcTemplate的一個很好的例子。
Spring還為其他數據庫操作提供了許多服務,比如使用SqlUpdate插入和更新數據庫,使用UpdatableSqlQuery更新ResultSet,生成主鍵,調用存儲過程等。
書中還給出了對BLOB數據和CLOB數據進行數據庫操作的例子:
對BLOB數據的操作通過LobHander來完成,通過調用JdbcTemplate和RDBMS都可以進行操作:
在JdbcTemplate中,具體的調用可以參考書中的例子 - 是通過以下調用起作用的:
- public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
- return execute(new SimplePreparedStatementCreator(sql), action);
- }
public Object execute(String sql, PreparedStatementCallback action) throws DataAccessException {
return execute(new SimplePreparedStatementCreator(sql), action);
}
然后通過對實現PreparedStatementCallback接口的AbstractLobCreatingPreparedStatementCallback的回調函數來完成:
- public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
- LobCreator lobCreator = this.lobHandler.getLobCreator();
- try {
-
- setValues(ps, lobCreator);
- return new Integer(ps.executeUpdate());
- }
- finally {
- lobCreator.close();
- }
- }
-
- protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)
- throws SQLException, DataAccessException;
public final Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
LobCreator lobCreator = this.lobHandler.getLobCreator();
try {
//這是一個模板方法,具體需要由客戶程序實現
setValues(ps, lobCreator);
return new Integer(ps.executeUpdate());
}
finally {
lobCreator.close();
}
}
//定義的需要客戶程序實現的虛函數
protected abstract void setValues(PreparedStatement ps, LobCreator lobCreator)
throws SQLException, DataAccessException;
而我們注意到setValues()是一個需要實現的抽象方法,應用程序通過實現setValues來定義自己的操作 - 在setValues中調用lobCreator.setBlobAsBinaryStrem()。讓我們看看具體的BLOB操作在LobCreator是怎樣完成的,我們一般使用DefaultLobCreator作為BLOB操作的驅動:
- public void setBlobAsBinaryStream(
- PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)
- throws SQLException {
-
- ps.setBinaryStream(paramIndex, binaryStream, contentLength);
- ........
- }
public void setBlobAsBinaryStream(
PreparedStatement ps, int paramIndex, InputStream binaryStream, int contentLength)
throws SQLException {
//通過JDBC來完成對BLOB數據的操作,對Oracle,Spring提供了OracleLobHandler來支持BLOB操作。
ps.setBinaryStream(paramIndex, binaryStream, contentLength);
........
}
上面提到的是零零碎碎的Spring JDBC使用的例子,可以看到使用Spring JDBC可以幫助我們完成許多數據庫的操作。Spring對數據庫操作最基本的服務是通過JdbcTeamplate和他常用的回調函數來實現的,在此之上,又提供了許多RMDB的操作來幫助我們更便利的對數據庫的數據進行操作 - 注意這里沒有引入向Hibernate這樣的O/R方案。對這些O/R方案的支持,Spring由其他包來完成服務。
書中還提到關于execute和update方法之間的區別,update方法返回的是受影響的記錄數目的一個計數,并且如果傳入參數的話,使用的是java.sql.PreparedStatement,而execute方法總是使用 java.sql.Statement,不接受參數,而且他不返回受影響記錄的計數,更適合于創建和丟棄表的語句,而update方法更適合于插入,更新和刪除操作,這也是我們在使用時需要注意的。
摘要: 上面我們分析了IOC容器本身的實現,下面我們看看在典型的web環境中,Spring IOC容器是怎樣被載入和起作用的。 簡單的說,在web容器中,通過ServletContext為Spring的IOC容器提供宿主環境,對應的建立起一個IOC容器的體系。其中,首先需要建立的是根上下文,這個上下文持有的對象可以有業務對象,數據存取對象,資源,事物管理器等各種中間層對象。在這個上下文的基礎上,和web ...
閱讀全文
摘要: 在認真學習Rod.Johnson的三部曲之一:<<Professional Java Development with the spring framework>>,順便也看了看源代碼想知道個究竟,拋磚引玉,有興趣的同志一起討論研究吧! 以下內容引自博客:http://jiwenke-spring.blogspot.com/,歡迎指導:) 在Spring中,IOC容器的重要...
閱讀全文
眾所周知,技術不再是困擾CIO職業前途的必備要件之一。但是,這不是說CIO可以不關心技術了。相反,CIO可以不精通某種技術,但是,一定要對這種技術的優缺點以及發展趨勢有個清晰的了解。因為在信息化管理軟件選型或者信息化戰略制定的時候,還是離不開技術,除了對軟件本身進行選型之外,還需要考慮其采用的技術的選型。
具體的來說,CIO在技術選型上,要考慮所采用的技術是否穩定、擴展性是否較好、跨平臺的性能等等。在軟件選型的時候,不僅要考慮軟件的功能,也要考慮其所采用的技術,只有如此,CIO才能輕松應對后續的變化。
技術選型標準一:跨平臺性 隨著微軟打擊盜版力度的加強,越來越多的企業開始考慮操作系統的使用成本。確實,對于大部分企業來說,已經習慣使用免費的軟件,若讓他們掏腰包,去買正版的操作系統與OFFICE辦公軟件,而且這些費用還不低,他們不一定會肯。隨著微軟盜版力度的加強,企業一定會千方百計的去尋找微軟操作系統與辦公軟件的替代品。恰好,這個就給開源的LINUX操作系統與OPEOFFICE辦公軟件提供了發展的機會。
其實,根據使用經驗,這個開源的操作系統與辦公軟件,其性能方面并不比微軟的操作系統與辦公軟件差。而只是員工以前在學校中學的都是微軟的產品,所以,對于LINUX系統上的軟件操作并不時很熟悉,所以,會對他有排斥心理。另一方面,開源的LINUX操作系統與辦公軟件在界面的友好性上可能還是跟微軟的產品有所差別,但是,這并不影響實用。而且,LINUX操作系統,在業界的評論上,其穩定性要比微軟的操作系統要好,而且,其受到病毒襲擊的機率也比微軟的操作系統少得多。
所以,借著微軟打擊盜版力度的加強,恰好給了一個LINUX系統普及的機會。相信隨著企業對于LINUX系統認識的加強,會有越來越多的企業采用這個開源的LINUX系統。
但是,這對企業或許是一件好事情,可是,對于CIO來說,卻是一個頭疼的事情。為什么呢?因為現在很多的管理軟件,如ERP、財務管理軟件等等,都是在微軟操作系統的平臺上開發的。由于平臺開發技術的限制,這些為微軟操作系統量身定制的信息化管理系統,無法在LINUX平臺上運行順暢,有的甚至是不兼容的。為此,企業若向放棄使用微軟的操作系統,雖然可以節省操作系統上的授權費用,但是,也必將面臨著這些管理軟件的取舍問題。
所以,為了我們CIO后續在操作系統的選型上沒有這么多的限制,我們從現在開始,就要看到這個趨勢,在信息化管理軟件選型的時候,要注意軟件的跨平臺性能。簡單地說,我們選擇的信息化管理軟件不僅要在微軟的操作系統上能夠跑的順暢,而且,在LINUX系統平臺上也要能夠運行順利,甚至還能夠支持蘋果等操作系統。只有如此,下次我們在操作系統轉型的時候,才不會受到這些信息化管理軟件的限制。
一般來說,現在一些基于JAVA開發的、WEB模式的信息化管理軟件,基本上都能夠實現多平臺的兼容。而且,現在這也是信息化管理軟件發展的趨勢。以后支持單一平臺的信息化管理軟件,就像以前只有單一語言的信息化管理軟件一樣,市場份額將會逐漸縮小。
技術選型標準二:擴展性與集成性的考慮 信息化管理系統現在已經發展成為一個體系。為什么這么說呢?現在企業中所采納的信息化管理系統,已經不是以前單一的一個財務管理軟件。如某些企業,已經有了CRM客戶關系管理系統、ERP企業資源計劃系統、OA辦公自動化系統等等。現在要成功實施一個信息化項目,已經不是一件難事,大家都有了豐富的信息化項目實施經驗。像以前所說的“上ERP系統是找死,不上ERP系統是等死”的現狀已經一去不復返。現在ERP實施的成功率已經非常的高。
所以,現在信息化項目成功上線已經不是我們CIO所追求的最高目標,而是最基本的目標。現在我們CIO所要考慮的,是系統的集成問題。如何把企業先后實施的各個信息化項目有效的集成起來,減少信息化項目上的重復投資,提高信息化管理的效果,這已經是考驗我們CIO能力的一個非常有效的手段。
現在SOA系統,一個系統集成的平臺,在信息化管理中,如一匹黑馬,獨樹一幟。從這里就可以看出,企業對于信息化系統整合的迫切需求。
為此,我們CIO在信息化管理軟件進行選型的時候,就需要考慮這個系統集成性的問題。要考慮我們所即將采用的信息化管理軟件,有否提供豐富的接口,可以跟其他信息化管理系統進行交流。
雖然現在還沒有統一的標準,SOA發展也只是起步,沒有成熟,還有很多需要改善的地方。但是,我們CIO需要有高瞻遠矚的目光,在信息化管理軟件選型的時候,需要關注軟件開發商有沒有這方面的嘗試。如公布自己的接口的詳細參數信息,如此的話,即使后面沒有統一的集成平臺,則通過少量的開發,也可以把兩個或者兩個以上的操作系統平臺整合起來。
另外關于系統整合還有一個偷懶的方法。就是我們在軟件選型的時候,要看看其軟件開發商有沒有形成一個信息化管理的體系產品。如企業現在可能只需要采用ERP系統,但是我們在CIO選型的時候,需要考慮這個軟件開發商是否還開發了CRM系統或者工作六管理系統。因為他們的這些系統之間往往可以實現很好的集成。如此的話,下次若需要其他的信息化管理軟件的時候,我們也可以考慮購買他們的產品,如此的話,就可以非常輕松的實現各大信息化管理系統的集成。
系統的集成是未來信息化管理軟件發展的一個趨勢,所以,為了后續工作的方便,我們在選擇第一個信息化管理軟件的時候,就需要有軟件集成的思想。只有如此,后續企業需要軟件集成的時候,我們才不會手忙腳亂。
技術選型標準三:技術穩定性的考慮 軟件企業跟生產或者商品企業對于技術的追求可能有所區別。軟件企業更多的關注是這個技術會否過時,他們往往希望利用“時髦”來推銷他們的新技術產品。而對于生產企業來說,時髦并不是企業所關注的重點,他們更加關注的是這門技術的成熟程度,是否會像微軟操作系統一樣,隔幾天就需要打補丁或者軟件升級。
在我們CIO嚴重,最適合企業的技術不時往往不是那種剛出爐的技術,雖然他們比較先進,解決了以前產品中的種種缺陷。但是,他們也有一個致命的缺點,就是不夠穩定。隔三差五的會出現幾個漏洞。而這真是作為CIO最不能夠忍受的。因為在企業信息化管理利用中,可能你做的好人家不一定主意,但是,一出現什么問題人家就會抓住你不妨。這大概就叫做“好事不出門、壞事傳千里”吧。
各位CIO在軟件選型的時候,考慮技術因素的時候,不要太關注與技術是否夠時髦,會不會過時的問題;而應該更多的考慮,這門技術或者產品,是否穩定,用戶群是否過大。
在信息化管理上,要學會模仿,而不是爭做“第一個吃螃蟹的人”。在信息化管理行業,變數太多。第一個吃螃蟹的人,大部分是死的比較慘的人。若我們等到別人先試用了之后,產品或者技術被改善的差不多了,然后我們再去采用這種技術或者產品,則就可以最大限度的避免由此帶來的風險。
技術選型有時候比產品功能選型更加難。因為技術選系的話,需要考慮的因素太多,而且現在技術的更新換代實在讓人有點目不暇接。現在比較流行的不 B/S模式說不定哪一天也會像以前C/S模式一樣被其他模式所代替。而功能選型的話,至少功能是可以收集的,只需要細心與時間,一般不會有多大的誤差。
現在接觸的信息化項目越來越多,由此帶來的感受也就越來越深。你如果把現在的企業當作臨時的中轉站,你可能不會有這么大的顧慮,可能會以為只需要考慮未來的幾年的時間就可以了。但是,你若想在這家企業中終老,那么,考慮的因素就會比較多,什么軟件的集成、軟件的擴展性、技術的穩定性與先進行之間的矛盾與取舍,都是我們CIO所需要關注的內容。
所以,信息化技術日新月異的發展,給我們CIO帶來了發展的機遇,但是,也給我們帶來的巨大的壓力。
摘要: 1.文本框焦點問題onBlur:當失去輸入焦點后產生該事件onFocus:當輸入獲得焦點后,產生該文件Onchange:當文字值改變時,產生該事件Onselect:當文字加亮后,產生該文件<input type="text" value="mm" onfocus="if(value=='mm) {value=''}" onblur="if(value=='') {value='mm'}"&g...
閱讀全文