#
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...
阅读全文