客户端进行多次请求时,服务器与客户端怎么确认是不是同一个sesson?

前往页面...
警告信息不用搭理
奇怪 加我们客服张老师 qq 给你远程看下
基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Trasactional注解的事务管理,但在通过基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务是有区别的,我们接下来看看到底有哪些区别。
一、基础工作
首先修改我们上一次做的 ,如下所示:
&&& 将xml声明式事务删除
java代码:
&aop:config expose-proxy=&true&&
&!-- 只对业务逻辑层实施事务 --&
&aop:pointcut id=&txPointcut& expression=&execution(* cn.javass..service..*.*(..))& /&
&aop:advisor advice-ref=&txAdvice& pointcut-ref=&txPointcut&/&
&/aop:config&
&&& 并添加注解式事务支持:
java代码:
&tx:annotation-driven transaction-manager=&txManager&/&
& &&在我们的BaseService接口上添加 @Transactional 使该方法开启事务
java代码:
package mon.
public interface IBaseService&M extends java.io.Serializable, PK extends java.io.Serializable& {
@Transactional
//开启默认事务
public int countAll();
在我们的log4j.properties中添加如下配置,表示输出spring的所有debug信息
java代码:
log4j.logger.org.springframework=INFO,CONSOLE
在我们的resources.properties里将hibernate.show_sql=true 改为true,为了看到hibernate的sql。
单元测试类:
java代码:
package cn.javass.ssonline.spider.service.
import org.junit.T
import org.junit.runner.RunW
import org.springframework.beans.factory.annotation.A
import org.springframework.test.context.ContextC
import org.springframework.test.context.junit4.SpringJUnit4ClassR
import org.springframework.test.context.transaction.TransactionC
import cn.javass.demo.service.UserS
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {&classpath:spring-config.xml&})
public class UserServiceTest2 {
@Autowired
private UserService userS
public void testCreate() {
userService.countAll();
基础工作做好,接下来我们详细看看 Spring基于 JDK动态代理 和 CGLIB类级别代理到底有什么区别。
二、基于JDK动态代理:
java代码:
&tx:annotation-driven transaction-manager=&txManager&/&
& &该配置方式默认就是JDK动态代理方式
运行单元测试,核心日志如下:& & & & & & & & & & & & & & & & & & &
java代码:
09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Creating new transaction with name [mon.service.impl.BaseService.countAll]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
//开启事务
09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Opened new Session
09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] to thread [main] //绑定session到ThreadLocal
09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization
09:58:44 [main] DEBUG org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [mon.service.impl.BaseService.countAll]
09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]
09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Found thread-bound Session
09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] bound to thread [main]
Hibernate:
count(*) as col_0_0_
tbl_user usermodel0_
09:58:44 [main] DEBUG org.springframework.orm.hibernate4.HibernateTransactionManager - Committing Hibernate transaction on Session
//提交事务
09:58:44 [main] DEBUG org.springframework.transaction.support.TransactionSynchronizationManager - Removed value [org.springframework.orm.hibernate4.SessionHolder@1184a4f] for key [org.hibernate.internal.SessionFactoryImpl@107b56e] from thread [main] //解除绑定session到ThreadLocal
到此我们可以看到事务起作用了,也就是说即使把@Transactional放到接口上 基于JDK动态代理也是可以工作的。
三、基于CGLIB类代理:
java代码:
&tx:annotation-driven transaction-manager=&txManager& proxy-target-class=&true&/&
& &该配置方式是基于CGLIB类代理
启动测试会报错,No Session found for current thread,说明事务没有起作用
java代码:
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1024)
at mon.dao.hibernate4.BaseHibernateDao.getSession(BaseHibernateDao.java:63)
at mon.dao.hibernate4.BaseHibernateDao.aggregate(BaseHibernateDao.java:238)
at mon.dao.hibernate4.BaseHibernateDao.countAll(BaseHibernateDao.java:114)
at mon.service.impl.BaseService.countAll(BaseService.java:60)
at mon.service.impl.BaseService$$FastClassByCGLIB$$5b04dd69.invoke(&generated&)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:618)
at cn.javass.demo.service.impl.UserServiceImpl$$EnhancerByCGLIB$$7d46c567.countAll(&generated&)
at cn.javass.ssonline.spider.service.impl.UserServiceTest2.testCreate(UserServiceTest2.java:20)
如果将注解放在具体类上或具体类的实现方法上才会起作用。
java代码:
package mon.service.
public abstract class BaseService&M extends java.io.Serializable, PK extends java.io.Serializable& implements IBaseService&M, PK& {
@Transactional()
//放在抽象类上
public int countAll() {
return baseDao.countAll();
运行测试类,将发现成功了,因为我们的UserService继承该方法,但如果UserService覆盖该方法,如下所示,也将无法织入事务(报错):
java代码:
package cn.javass.demo.service.
public class UserServiceImpl extends BaseService&UserModel, Integer& implements UserService {
//没有@Transactional
public int countAll() {
return baseDao.countAll();
四、基于aspectj的
java代码:
&tx:annotation-driven transaction-manager=&txManager& mode=&aspectj& proxy-target-class=&true&/&
在此就不演示了,我们主要分析基于JDK动态代理和CGLIB类代理两种的区别。
五、结论:
基于JDK动态代理 ,可以将@Transactional放置在接口和具体类上。
基于CGLIB类代理,只能将@Transactional放置在具体类上。
因此 在实际开发时全部将@Transactional放到具体类上,而不是接口上。
1、& JDK动态代理
1.1、Spring使用JdkDynamicAopProxy实现代理:
java代码:
package org.springframework.aop.
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
//注意此处的method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
注意此处的method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的)
1.2、如果&tx:annotation-driven 中 proxy-target-class=&true& ,Spring将使用CGLIB动态代理,而内部通过Cglib2AopProxy实现代理,而内部通过DynamicAdvisedInterceptor进行拦截:
java代码:
package org.springframework.aop.
final class Cglib2AopProxy implements AopProxy, Serializable {
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
//注意此处的method 一定是具体类上的method(因此只用放置在具体类上的@Transactional是可以发现的)
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
1.3、Spring使用AnnotationTransactionAttributeSource通过查找一个类或方法是否有@Transactional注解事务来返回TransactionAttribute(表示开启事务):
java代码:
package org.springframework.transaction.
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
if (attr != null) {
而AnnotationTransactionAttributeSource又使用SpringTransactionAnnotationParser来解析是否有@Transactional注解:
java代码:
package org.springframework.transaction.
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
if (ann != null) {
return parseTransactionAnnotation(ann);
public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
&&& 此处使用AnnotationUtils.getAnnotation(ae, Transactional.class); 这个方法只能发现当前方法/类上的注解,不能发现父类的注解。 Spring还提供了一个 AnnotationUtils.findAnnotation()方法 可以发现父类/父接口中的注解(但spring没有使用该接口)。
&& 如果Spring此处换成AnnotationUtils.findAnnotation(),将可以发现父类/父接口中的注解。
这里还一个问题,描述如下:
在接口中删除@Transactional&& //开启默认事务
java代码:
package mon.
public interface IBaseService&M extends java.io.Serializable, PK extends java.io.Serializable& {
public int countAll();
在具体类中添加@Transactional
java代码:
package mon.service.
public abstract class BaseService&M extends java.io.Serializable, PK extends java.io.Serializable& implements IBaseService&M, PK& {
@Transactional()
//开启默认事务
public int countAll() {
return baseDao.countAll();
&&&&我们之前说过,基于JDK动态代理时, method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的),但现在我们放在具体类上,那么Spring是如何发现的呢??
&&& 还记得发现TransactionAttribute是通过AnnotationTransactionAttributeSource吗?具体看步骤1.3:
而AnnotationTransactionAttributeSource 继承AbstractFallbackTransactionAttributeSource
java代码:
package org.springframework.transaction.
public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
public TransactionAttribute getTransactionAttribute(Method method, Class&?& targetClass) {
//第一次 会委托给computeTransactionAttribute
//计算TransactionAttribute的
private TransactionAttribute computeTransactionAttribute(Method method, Class&?& targetClass) {
// Ignore CGLIB subclasses - introspect the actual user class.
Class&?& userClass = ClassUtils.getUserClass(targetClass);
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
//①此处将查找当前类覆盖的方法
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
// If we are dealing with method with generic parameters, find the original method.
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// First try is the method in the target class.
TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
if (txAtt != null) {
return txA
//找类上边的注解
// Second try is the transaction attribute on the target class.
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAtt != null) {
return txA
//②如果子类覆盖的方法没有 再直接找当前传过来的
if (specificMethod != method) {
// Fallback is to look at the original method.
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txA
// Last fallback is the class of the original method.
return findTransactionAttribute(method.getDeclaringClass());
& & & &//①此处将查找子类覆盖的方法
&&&&&& Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
&&&&&&&&// ClassUtils.getMostSpecificMethod
&&&&&& public static Method getMostSpecificMethod(Method method, Class&?& targetClass) {
&&&&&& Method specificMethod = null;
&&&&&& if (method != null && isOverridable(method, targetClass) &&
&&&&&&&&&&&&& targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
&&&&&&&&&& try {
&&&&&&&&&&&&& specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
&&&&&&&&&& } catch (AccessControlException ex) {
&&&&&&&&&&&&& // security settings are disallowi leave
&&&&&&&&&&&&& // 'specificMethod' null and fall back to 'method' below
&&&&&&&&&& }
&&&&&& return (specificMethod != null ? specificMethod : method);
&&& 可以看出将找到当前类的那个方法。因此我们放置在BaseService countAll方法上的@Transactional起作用了。
& & & //②如果子类覆盖的方法没有 再直接找当前传过来的
&&&&&& if (specificMethod != method) {
&&&&&&&&&& // Fallback is to look at the original method.
&&& &&&&&& txAtt = findTransactionAttribute(method);
&&&&&&&&&& if (txAtt != null) {
&&&&&&&&&&&&& return txA
&&&&&&&&&& }
&&&&&&&&&& // Last fallback is the class of the original method.
&&&&&&&&&& return findTransactionAttribute(method.getDeclaringClass());
&&&&&& 查找子类失败时直接使用传过来的方法。
因此,建议大家使用基于Schema风格的事务(不用考虑这么多问题,也不用考虑是类还是方法)。而@Transactional建议放置到具体类上,不要放置到接口。
作者原创【】
很明显 jar冲突啊 删掉一个
什么浏览器,版本
北京-大雄 20:43:41 网站的规模扩大后 主要面临两个问题
怎么保证大并发的访问2. 怎么让项目容易维护和更新 北京-疯狂小强 20:44:14 分布式存储 镇江-金山寺 20:44:31 提高服务器的硬件设置 北京-疯狂小强 20:44:27 数据库拆分 北京-瞌睡虫 20:44:39 应该先从 最简单 最省钱的开始说。 北京-大雄 20:45:01 我们先说第二个问题 项目庞大后 如果都是一个应用 那么每次的更新必然会影响到整个项目 对于项目参与人员来说 就是一个天灾了 北京-大雄 20:45:31 比如说淘宝
如果真个淘宝就是一个项目
那么其维护成本不可想象 镇江-金山寺 20:45:53 分开搞 北京-疯狂小强 20:45:51 分布式存储本来就是为了降低运营成本提高用户访问速度的,由其是跨国或跨地区的高并发 镇江-金山寺 20:46:02 模块分拆 北京-疯狂小强 20:45:59 不是的 北京-大雄 20:46:02 把项目拆分成不同的子项目 或者子模块 北京-瞌睡虫 20:46:05 先上 内存缓存。更换更好的http服务。 然后数据库读写分离。 应用拆分 多服务器。 再有钱了,自己实现内存缓存。。。再有钱了。自己实现文件存储。 北京-疯狂小强 20:46:09 模块不分拆的 北京-大雄 20:46:48 这样不同的子项目可以分别部署到不同的服务器上 这样开发和维护就方便了 北京-疯狂小强 20:47:09 存这和内存没有太大的关系,
是你的服务器部署的方式,
客户端配置要求不能太高 北京-longnian 20:47:36 最主要的是获得了 可维护性 和 可扩展性 北京-大雄 20:47:34 但是这样存在一个问题 单点故障任何一个子应用出现问题 那么整个服务就受到影响了 北京-longnian 20:47:44 而且尽量的保证单一职责 镇江-金山寺 20:48:03 模块分拆,体现的是专业化思想 北京-疯狂小强 20:48:06 大体上是两种吧,一种是中小弄项目用的,比如图片处理服务器,数据库服务器等 这样分开 北京-瞌睡虫 20:48:07 大项目 出现问题。一般是 数据库瓶颈 会先与 并发瓶颈连接耗尽的情形 发生 北京-longnian 20:48:29 单点故障 : 这个其实应该只考虑核心模块 如用户系统 北京-大雄 20:47:34 但是这样存在一个问题 单点故障任何一个子应用出现问题 那么整个服务就受到影响了 北京-longnian 20:48:44 比如偏远业务模块 紧迫性没那么高 北京-瞌睡虫 20:48:52 子应用 也可以上集群啊。 北京-大雄 20:48:51 这时候就可能会出现 多台服务器提供同样的服务 这样任何一太服务器出现问题 都不会影响到整体服务 北京-疯狂小强 20:49:00 还有一种是将整个网站拆分开无数个独立的小模块,分别存放在不同的空间 北京-瞌睡虫 20:49:13 同一个 新浪新闻 服务。后台 好多服务器提供 新闻应用呢 北京-大雄 20:49:17 所以在分布式上应用集群是比较常见的做法 北京-大雄 20:50:34 现在说第一个问题 怎么应对大规模的访问呢 北京-疯狂小强 20:50:38 未来是云计算的天下, 随着用户客户端硬件的不断升级,将数据的存储一部分可以分担到用户的客户端 北京-大雄 20:50:55 我们知道 任何一个http服务器都有一个连接数的概念 北京-瞌睡虫 20:51:12 最开始 先 让静态 动态处理分开。。现在一般是上 nginx 镇江-金山寺 20:51:47 是不是有个专门的接入服务器,然后转发.. 北京-大雄 20:51:49 这个连接数是一定的 我们的做法就是尽可能快得处理用户的请求 这样连接就回空闲出来提供别人访问 北京-大雄 20:54:07 我们网站一般都有一个域名对应一个主机
那么所有的请求都是由这个主机来负责的北京-疯狂小强 20:54:17 其实这个问题,也无需太过讨论了,很明朗,就像现在流行云计算,而云计算的主体概念是分布式,但是部署云服务器又是一个又一个集群北京-大雄 20:54:24 我们想不考虑动态dns轮询多台主机的情况北京-大雄 20:56:50 那么这个域名对应的主机 的处理是有限的 再提供一定连接数的情况下 只能是尽可能快得处理请求 北京-大雄 20:57:01 有更多的空闲来处理请求北京-大雄 20:57:58 这时候 就可以把应用部署在不同的服务器上形成集群 有专门的硬件或者软件来把请求分发到不同的服务器上处理北京-大雄 20:58:57 这个我们一般使用反向代理北京-大雄 20:59:19 比如说用nginx北京-大雄 21:01:59 在集群情况下
每一台服务器上的应用都是一样的那么如果用户的请求时有状态的
比说说用session保存登录状态那么每次请求都可能是有不同的服务器来处理的 该如何保存这个状态呢北京-大雄 21:02:34 我相信大家对session复制这个词不陌生吧北京-大雄 21:03:04 把所有的session都复制在每一台服务器上 这样就不会发生session失效的问题了北京-大雄 21:03:56 但是这样存在的一个问题就是
每一台服务器都要保存别的服务器的session
如果节点很庞大 那么就session的开销就很大了北京-大雄 21:04:37 一定要注意讨论是我说的前提啊 是有状态的请求
如果没有状态 那么就不用考虑了北京-大雄 21:05:38 对于使用session设计的系统 如果要使用集群 有两种解决方案北京-大雄 21:06:04 使用硬件或者软件
把相同IP的客户分发到同一台服务器上北京-大雄 21:06:37 这样 用户的访问始终在一台服务器上 就不用session复制了北京-大雄 21:07:10 还有一种方式
: 使用分布式缓存 比如memcached来存储session数据北京-大雄 21:07:26 这样就和大家说的使用sesson服务器是一样的了北京-大雄 21:07:46 但大家没有搞清楚的是你的设计决定了你的方案北京-大雄 21:09:13 如果你的系统就是用session设计的 那么你怎么能使用缓存统一存贮呢 除非改代码北京-瞌睡虫 21:09:52 还好 我不怎么喜欢用session存数据。。北京-大雄 21:10:03 如果不重新开发软件 就只能使用我说的方案一
相同的ip请求用同一个服务器处理北京-大雄 21:10:08 或者说session复制北京-瞌睡虫 21:10:44 session复制 tomcat 是不是直接配配 就行。。貌似 不太靠谱啊。北京-瞌睡虫 21:14:19 同一ip 归属到同一服务器。。一开始就得划分好。 北京-瞌睡虫 21:15:07 万一。你划分了半天。。过来的连接8成 都到其中一台了。。那不就是白干了。 北京-大雄 21:15:33 问题是 用户可能会双线接入 或者使用代理 同一用户每次请求的ip地址可能不同 北京-大雄 21:15:38 这样就麻烦了 北京-瞌睡虫 21:15:45 恩。。更麻烦。 北京-大雄 21:16:08 不用提前划分IP 北京-瞌睡虫 21:16:11 双线接入的系统 有些 机房 防火墙那 就有问题了。 北京-瞌睡虫 21:16:25 不提前划分。你就得记住。你之前的ip 都去哪了。 北京-瞌睡虫 21:16:31 这得有专门的软件或硬件吧。 北京-大雄 21:16:49 就是你第一次请求时ip在那个服务器处理 以后都有一个服务器处理 北京-大雄 21:17:27 至于服务器资源的负载 先不用你考虑 负载均衡软件会根据服务器资源分配的 北京-大雄 21:18:35 我们现在简单提下session的机制我们请求服务器 会生成一个sessionID 这个是存储在用户浏览器的cookie中的 北京-大雄 21:20:57 使用cookie来记录你的用户状态 这样每次从cookie中取出你的用户名或者标识 把请求分发到同一服务器即可 北京-大雄 21:21:12 当然cookie中也可能是你的ip地址 北京-longnian 21:22:37 1、最简单的就是使用cookie模拟2、复杂点的可以考虑 修改底层的session实现机制(但需要保证 sessionid在一级域名下) 北京-大雄 21:22:50 我们说的SNA架构 就是一种脱离session的设计 北京-大雄 21:24:13 我们在cookie中只保留一个用户标识 那么不管是那个服务器 取到标识再获取用户权限等就可以了 北京-longnian 21:27:14 session 也是通过cookie实现只是这个具有强制性(默认存储到当前服务器 默认加到当前服务器域名下)
如果不具有强制性(这个是最简单的解决方案) 北京-大雄 21:27:21 只要你不在用户cookie存储用户密码等信息就可以了 北京-大雄 21:28:10 @TONNY
现在规模大点的公司都使用硬件F5来处理 北京-大雄 21:28:22 使用F5的IP记忆功能即可实现 北京-大雄 21:29:06 在这种架构中 其实存在一些单点的 北京-大雄 21:29:37 缓存服务器或者数据库服务器都是单点北京-大雄 21:30:01 这个可以采用双机热备来解决北京-大雄 21:30:12 比如数据库的主从复制等北京-大雄 21:30:57 memcached是不提供集群方案的 因为它的定位就是一个缓存 既然是缓存 数据时可以被允许丢失的北京-longnian 21:31:45 不一定使用memcached
可以考虑持久性的缓存北京-大雄 21:31:42 我们可以自己实现一个集群比如写cached的时候 同时向多个cached服务器写入数据北京-大雄 21:32:08 持久性的缓存也是存在机器故障等因素的北京-longnian 21:32:47 这样 又到 cache的复制问题上了北京-longnian 21:32:55 其实可以采用 一致性hash北京-longnian 21:33:03 丢失部分数据是允许的北京-大雄 21:33:45 所以在集群环境下的软件设计 是没有教科书的解决方案的这个要考虑应用的很多因素 综合去处理北京-大雄 21:34:08 大家知道这些基本概念后 再设计的时候就有章可循了北京-大雄 21:35:19 对于一个大的应用来说
分布式和集群是很难划分边界的 大家理解的角度不一样只要做到心中有底就可以了北京-大雄 21:35:40 设计时 存在单点的地方要谨慎设计北京-longnian 21:36:02 对 很大的话题北京-longnian 21:36:06 能不分布式 就别北京-longnian 21:36:10 给自己找麻烦北京-大雄 21:35:59 没有两全其美的方案北京-大雄 21:36:22 只能是在一个容忍度内取舍北京-longnian 21:36:50 CAP
北京-longnian 21:36:54 哈哈哈北京-longnian 21:36:59 一个思想北京-大雄 21:36:55 呵呵
nosql的核心 cap北京-大雄 21:37:25 好了
我就讲这么多吧 大家可以提问 北京-大雄 21:37:47 讲得很粗糙 大家有不明白的地方可以提出来镇江-金山寺 21:38:26 基本思想是明白了北京-MyEclipse 21:38:21 面试的时候会问:你给我讲讲Session吧。 该怎么组织啊?镇江-金山寺 21:39:11 session就是记录用户状态的的一种机制北京-longnian 21:39:16 Session : 在服务器端为用户保存数据
北京-longnian 21:39:33 session 能进行有状态的会话北京-longnian 21:39:55 java session 实现机制:通过在cookie中加入 JSESSIONID北京-大雄 21:39:42 session的理解其实就是一个map的理解北京-大雄 21:40:09 服务器上有个map&sessionID,
session&北京-大雄 21:40:31 这样根据sessionID取到当前用户的session北京-MyEclipse 21:40:37 Session实际上是保存在内存里的,但必须要去持久化才能保存。北京-大雄 21:40:55 而当前用户的session又是一个map《attribute, value》北京-大雄 21:41:25 呵呵 这个要分语言的php默认存储是文件形式北京-MyEclipse 21:41:35 Java。北京-大雄 21:41:49 java中 默认是在内存北京-大雄 21:42:07 要不要实现序列化 要看你的需要深圳-叶儿 21:45:28 我打断下各位。memcache不提供集群方案,那么一些用memcache集群的做法都是假的?用程序去控制?如果是,请问有方案么。北京-大雄 21:45:48 是的北京-大雄 21:45:57 memcached是不提供集群方案的北京-longnian 21:46:39 memcached 是客户端 分布式的北京-longnian 21:46:52 常常采用 一致性 hash算法北京-大雄 21:46:35 这个都是自己控制的 基本核心和我说的差不多 要么同时保存在多个服务器 要么散列到多个服务器北京-TONNY 21:46:49 memcached
弄个服务器就可以了北京-大雄 21:57:15 现在的memcached服务端提供集群了吗北京-longnian 21:57:47 没有集群啊北京-longnian 21:57:49 是分布式的啊北京-longnian 21:58:46 通过在客户端配置服务器列表 采用一致性hash算法+(逻辑节点思想) 将key分配到相应的服务器北京-longnian 21:58:52 这个是在客户端做的北京-大雄 21:58:30 既然是一台memcached服务器
怎么会有均匀分布到各个服务器北京-longnian 21:59:22 均匀 是
指 逻辑上均匀北京-longnian 21:59:29 多台memcache北京-大雄 21:59:10 既然是这样的话 就是memcached服务端集群了啊北京-longnian 21:59:40 每台memcache 存的数据不一样北京-longnian 21:59:49 所以我认为不是集群北京-大雄 21:59:32 明白你的意思了北京-大雄 21:59:41 就和表分区差不多北京-longnian 22:00:10 嗯北京-大雄 22:00:01 这就明白了北京-大雄 22:00:41 这个确实称为分布式比较合适北京-大雄 22:01:03 不同服务器存储的数据不一样 不能算到集群的范畴里北京-大雄 22:01:36 这个就和同一服务器 启动多个memcached进程是一样的北京-longnian 22:02:09 对对结束!
& & &&&俗话说,一个系 统的伸缩性的好坏取决于应用的状态如何管理。为什么这么说呢?咱们试想一下,假如我们在session中保存了大量与客户端的状态信 息的话,那么当保存状态信息的server宕机的时候,我们怎么办?通常来说,我们都是通过集群来解决这个问题,而通常 所说的集群,不仅有负载均衡,更重要的是要有失效恢复failover,比如tomcat采 用的集群节点广播复制,jboss采 用的配对复制等session状 态复制策略,但是集群中的状态恢复也有其缺点,那就是严重影响了系统的伸缩性,系统不能通过增加更多的机器来达到良好的水平伸缩,因为集群节点间session的 通信会随着节点的增多而开销增大,因此要想做到应用本身的伸缩性,我们需要保证应用的无状态性,这样集群中的各个节点来说都是相同的,从而是的系统更好的 水平伸缩。
&&&&&&&&OK, 上面说了无状态的重要性,那么具体如何实现无状态呢?此时一个session框架就会发挥作用了。幸运的是淘 宝已经具有了此类框架。淘宝的session框架采用的是client cookie实现,主要将状态 保存到了cookie里 面,这样就使得应用节点本身不需要保存任何状态信息,这样在系统用户变多的时候,就可以通过增加更多的应用节点来达到水平扩展的目的.但 是采用客户端cookie的 方式来保存状态也会遇到限制,比如每个cookie一般不能超过4K的大小,同时很多浏览器都限制一个站点最 多保存20个cookie.淘 宝cookie框 架采用的是“多值cookie”, 就是一个组合键对应多个cookie的 值,这样不仅可以防止cookie数 量超过20, 同时还节省了cookie存 储有效信息的空间,因为默认每个cookie都会有大约50个字节的元信息来描述cookie。
&&&&&&&&除 了淘宝目前的session框 架的实现方式以外,其实集中式session管理来完成,说具体点就是多个无状态的应用节点连接一个session&服 务器,session服 务器将session保 存到缓存中,session服 务器后端再配有底层持久性数据源,比如数据库,文件系统等等。
PHP的发明者Rasmus Lerdorf同时也发明了SNA(shared nothing architecture)的概念&
什么意思呢?&
对Web Servers做scaling时如果将session等状态保持在各个节点上,这样状态的复制就很成问题,所以SNA告诉你在节点上不要保存状态,session这种东西可以存在数据库或内存缓存中嘛,然后cookie或URL中带一个加密string用来查询session状态就可以完美解决session状态了。&
但显然这给数据库增加了压力,但是数据库的scaling能力比Web Servers做session复制要好多了。况且我们在数据库前加一memcached将大量的数据库Reads的工作做了,而数据库只用处理数量不多的Writes工作,这大大提高了系统的性能。每个Web Server节点里不用cache,而是外部分布式memcached或session server,这同时避免了cache-coherence问题。&
PHP默认就是SNA的,所以许多人就天真的认为PHP比Java、ASP.NET的scaling能力要好。而Java、ASP.NET默认允许分享数据允许缓存,所以需要程序员自己处理哪些情况下使用SNA,It’s easy to write non-scalable software on those platforms.&
所以说PHP的fans们不要再在自己用甜言蜜语构建的城堡里安然自得了,看看Wikipedia上Scalability的定义:&
Scalability is the capability of a system to increase performance under an increased load when resources (typically hardware) are added.&
简单的说,Scalability是与开发环境开发语言没有丁点关系的,所以说“PHP性能更好更适合做大型网站”真是有点无理取闹!
& & & Shared Nothing Architecture(无分享架构)是一个分布式的架构,每个节点都是独立的。典型的SNA系统会集中存储状态的信息,如:数据库中,内存cache中;不在节点上保存状态的信息。
&&&对于server集群,若将session等状态保存在各个节点上,那么各个节点的session复制会极大的影响性能;若采用SNA,保持每个节点的无状态性,不再使用session来保持全局的状态,而是将session直接放在数据库中,在数据库前再加一层分布式Cache(推荐使用memcached),这样将可极大的提高性能,当改变session中的对象时,同步到cache和数据库。
&& 以往集群架构都采用Session共享模式进行设计,而后PHP等方面提出了SNA架构,主张Session不共享。SNA架构思想,无论对企业应用还是大型互联网站,极大提高了web应用的吞吐量和性能。&&& 一般SNA架构以集成分布式Cache例如 memcached 的方案居多,此处姑且称为 Cache模式。&&& 我结合公司电信项目的情况,以及思考,总结另一种方案,供参考。&&& SNA思想的关键就是每个集群内web server实例不互相共享session,Cache模式主张session数据都放到分布式缓存中,意味着,逻辑上集群内还是要共享session信息;这种考虑源于负载均衡时,同一个IP发来的两个请求,可能走到不同的 Web Server上。&&& 因此,只要同一IP的两个请求转发到同一个 Web& server实例,那么就可以不需要全局的 session信息缓存。&&& 1) 我所在的移动项目下,采用 F5硬件负载均衡器,使用IP记忆机制实现了这一点。因此,各 web server实例的session无需共享,仍然保存在自己的session内存中,节省了网络开销和Cache命中查找时间。&&& F5很贵,因此对于网站一般负担不起,但可以采用软件负载来做到这一点。&&& 切分模式的SNA架构:&&& 2) IP Memory(IP记忆):负载服务器记录 客户端IP -& ServerID 的关系,模拟F5;&&& 3) (Dispatch by Rule)按规则转发:IP记忆需要维护一张路由table, 因此,需要消耗一定内存,以及映射关系查找的时间;&&&&&& 我们将客户端的所有IP看作一个集合 IP Set,按固定规则将其平均分配集群的server实例上去,这样就可以节省路由table的开销。 关键是分配算法,可以考虑的有:&&&&&& 2.1) 简单数值法: IP各节加总 = X, 假定集群实例个数为 N,编号1-N, 那么每次请求选择的目标server id = X mod N。&&&&&& 2.2) hash值法: 有的系统可能想基于 userid 进行请求分配, 那么可以采用 X = hashCode(userid), serverID = X mod N;&&&&&& 具体情况下, 可以灵活选择使用那个数据项判断请求分配的逻辑。这个思想参考了& memcached 的集群管理思想。&&&&&&& 4) stickySession方式。&&&&&& 一般apache等采用这种方式做负载均衡。但必须结合 jvmRoute。 第一次被分配的 web server必须返回一个 jvmRoute在response中,并由 apache 送到客户端浏览器,第二次请求发起时,request信息中将包含 JSESSIONID 和 对应的 jvmRoute, apache根据次找到对应的 server,完成 stickySession机制。&结论: 切分模式的SNA架构,基于规则进行请求转发,可以省去分布式Cache的使用,更进一步的提升系统吞吐量和响应性。
简单说,分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。
例如:如果一个任务由10个子任务组成,每个子任务单独执行需1小时,则在一台服务器上执行改任务需10小时。
采用分布式方案,提供10台服务器,每台服务器只负责处理一个子任务,不考虑子任务间的依赖关系,执行完这个任务只需一个小时。
而采用集群方案,同样提供10台服务器,每台服务器都能独立处理这个任务。假设有10个任务同时到达,10个服务器将同时工作,10小后,10个任务同时完成,这样,整身来看,还是1小时内完成一个任务!
1. 两大关键特性
集群是一组协同工作的服务实体,用以提供比单一服务实体更具扩展性与可用性的服务平台。在客户端看来,一个集群就象是一个服务实体,但事实上集群由一组服务实体组成。与单一服务实体相比较,集群提供了以下两个关键特性:
& 可扩展性--集群的性能不限于单一的服务实体,新的服务实体可以动态地加入到集群,从而增强集群的性能。
& 高可用性--集群通过服务实体冗余使客户端免于轻易遇到out of service的警告。在集群中,同样的服务可以由多个服务实体提供。如果一个服务实体失败了,另一个服务实体会接管失败的服务实体。集群提供的从一个出 错的服务实体恢复到另一个服务实体的功能增强了应用的可用性。
2. 两大能力
为了具有可扩展性和高可用性特点,集群的必须具备以下两大能力:
& 负载均衡--负载均衡能把任务比较均衡地分布到集群环境下的计算和网络资源。
& 错误恢复--由于某种原因,执行某个任务的资源出现故障,另一服务实体中执行同一任务的资源接着完成任务。这种由于一个实体中的资源不能工作,另一个实体中的资源透明的继续完成任务的过程叫错误恢复。
负载均衡和错误恢复都要求各服务实体中有执行同一任务的资源存在,而且对于同一任务的各个资源来说,执行任务所需的信息视图(信息上下文)必须是一样的。
3. 两大技术
实现集群务必要有以下两大技术:
& 集群地址--集群由多个服务实体组成,集群客户端通过访问集群的集群地址获取集群内部各服务实体的功能。具有单一集群地址(也叫单一影像)是集群的一个基 本特征。维护集群地址的设置被称为负载均衡器。负载均衡器内部负责管理各个服务实体的加入和退出,外部负责集群地址向内部服务实体地址的转换。有的负载均 衡器实现真正的负载均衡算法,有的只支持任务的转换。只实现任务转换的负载均衡器适用于支持ACTIVE-STANDBY的集群环境,在那里,集群中只有 一个服务实体工作,当正在工作的服务实体发生故障时,负载均衡器把后来的任务转向另外一个服务实体。
& 内部通信--为了能协同工作、实现负载均衡和错误恢复,集群各实体间必须时常通信,比如负载均衡器对服务实体心跳测试信息、服务实体间任务执行上下文信息的通信。
具有同一个集群地址使得客户端能访问集群提供的计算服务,一个集群地址下隐藏了各个服务实体的内部地址,使得客户要求的计算服务能在各个服务实体之间分布。内部通信是集群能正常运转的基础,它使得集群具有均衡负载和错误恢复的能力。
Linux集群主要分成三大类( 高可用集群, 负载均衡集群,科学计算集群),高可用集群( High Availability Cluster),负载均衡集群(Load Balance Cluster),科学计算集群(High Performance Computing Cluster)
具体包括:
Linux High Availability 高可用集群:普通两节点双机热备,多节点HA集群,RAC, shared, share-nothing集群等;Linux Load Balance 负载均衡集群:LVS等....;Linux High Performance Computing 高性能科学计算集群:Beowulf 类集群....;分布式存储;其他类linux集群:如Openmosix, rendering farm 等..
1. 高可用集群(High Availability Cluster)
常见的就是2个节点做成的HA集群,有很多通俗的不科学的名称,比如&双机热备&, &双机互备&, &双机&.
高可用集群解决的是保障用户的应用程序持续对外提供服务的能力。 (请注意高可用集群既不是用来保护业务数据的,保护的是用户的业务程序对外不间断提供服务,把因软件/硬件/人为造成的故障对业务的影响降低到最小程度)。
2. 负载均衡集群(Load Balance Cluster)
负载均衡系统:集群中所有的节点都处于活动状态,它们分摊系统的工作负载。一般Web服务器集群、数据库集群和应用服务器集群都属于这种类型。
负载均衡集群一般用于相应网络请求的网页服务器,数据库服务器。这种集群可以在接到请求时,检查接受请求较少,不繁忙的服务器,并把请求转到这些服务器上。从检查其他服务器状态这一点上看,负载均衡和容错集群很接近,不同之处是数量上更多。
3. 科学计算集群(High Performance Computing Cluster)
高性能计算(High Perfermance Computing)集群,简称HPC集群。这类集群致力于提供单个计算机所不能提供的强大的计算能力。
高性能计算分类
高吞吐计算(High-throughput Computing)
有一类高性能计算,可以把它分成若干可以并行的子任务,而且各个子任务彼此间没有什么关联。象在家搜寻外星人( SETI@HOME -- Search for Extraterrestrial Intelligence at Home )就是这一类型应用。这一项目是利用Internet上的闲置的计算资源来搜寻外星人。SETI项目的服务器将一组数据和数据模式发给Internet上 参加SETI的计算节点,计算节点在给定的数据上用给定的模式进行搜索,然后将搜索的结果发给服务器。服务器负责将从各个计算节点返回的数据汇集成完整的 数据。因为这种类型应用的一个共同特征是在海量数据上搜索某些模式,所以把这类计算称为高吞吐计算。所谓的Internet计算都属于这一类。按照 Flynn的分类,高吞吐计算属于SIMD(Single Instruction/Multiple Data)的范畴。
分布计算(Distributed Computing)
另一类计算刚好和高吞吐计算相反,它们虽然可以给分成若干并行的子任务,但是子任务间联系很紧密,需要大量的数据交换。按照Flynn的分类,分布式的高性能计算属于MIMD(Multiple Instruction/Multiple Data)的范畴。
4. 分布式(集群)与集群的联系与区别
分布式是指将不同的业务分布在不同的地方。而集群指的是将几台服务器集中在一起,实现同一业务。分布式中的每一个节点,都可以做集群。而集群并不一定就是分布式的。
举例:就比如新浪网,访问的人多了,他可以做一个群集,前面放一个响应服务器,后面几台服务器完成同一业务,如果有业务访问的时候,响应服务器看哪台服务器的负载不是很重,就将给哪一台去完成。
而分布式,从窄意上理解,也跟集群差不多, 但是它的组织比较松散,不像集群,有一个组织性,一台服务器垮了,其它的服务器可以顶上来。
分布式的每一个节点,都完成不同的业务,一个节点垮了,哪这个业务就不可访问了。
请使用除360外的浏览器下载
java hello
public abstract class AbstractModel implements java.io.Serializable {.....
从定义看,该类应为final。
怎么可能是final呢,,这是抽象类啊
感谢了 找好久了
呵呵,欢迎 来私塾在线这个家庭吧
minstrel写
谢谢老师,我一直在跟踪看你的博客,有你的引导,希望我能啃动sping这个大山。
客气,写了有用就很高兴
1、接口是什么
是一种规范 一种标准2、接口能干什么
接口中只描述能什么事情,不关心如何去实现和谁实现,因此:
对于实现:约束,约束类的行为,开闭原则(对扩展开放,对修改关闭);
对于客户:解耦,最少知识原则;
当没有实现时,可以很轻松通过桩/伪实现/模拟对象测试;
很容易更换实现,对现有代码无影响。3、类也能实现:但类不彻底,不严格约束,我们会犯错误,语义不一样。
4、抽象类是什么
不能直接实例化的类:5、抽象类能干什么
既要约束子类行为,又要为子类提供公共行为实现。
抽象类中除了能描述能什么事情,还可以有一些实现,因此:
对于子类:自动继承父亲的一些通用行为,减少代码重复,复用 一般我们应该这样设计
1 先定义接口
2 如果子类有共性实现则实现一个抽象类
3 具体类继承抽象类
讨论内容如下::
北京-大雄() 14:03:50 基本概念就不解释了 可以去看下servlet的接口和抽象类 这个写的很好 什么时候用抽象类 什么时候用接口 都很清楚南京-风行天下() 14:04:00 用法上的区别
北京-大雄() 14:04:17 不是用法上的区别 是用途上的区别
北京-大雄() 14:05:19 先从j2ee开发为切入点吧一般我们开发j2ee 都习惯划分成三层
表现成 业务层 持久层北京-大雄() 14:06:17 我们对应成action
dao 严格来说到并不对应持久层 但我们一般用dao实现持久层的代码 就对应成dao吧 这个是大前提 北京-大雄() 14:07:29 action没有好说的
一般我们习惯对service和dao中都写一个接口 再去实现 那么到底有没有意义呢北京-大雄() 14:07:35 我们先说dao北京--海淀() 14:07:42 我感觉 其实接口 不是 为了应对修改用户的业务需求而设计的北京-大雄() 14:08:13 接口并不是为了修改
而是为了扩展北京-大雄() 14:08:36 对修改关闭
对扩展开放 开闭原则北京-大雄() 14:09:30 先说dao
以实体User为例
我们习惯写一个UserDao的接口 在写一个UserDao的实现北京-大雄() 14:12:07 那么应不应该去写这个UserDao的接口呢我的个人观点:应该去写一个UserDao的接口
我们做原型的时候 可以快速写一个实现 不用去考虑并发 性能原型通过后 再认真考虑个方便的东西 写一个UserDao的实现 最终修改一下配置文件就可以了
北京-大雄() 14:14:15 应用需要更好的性能是
再去实现一个UserDao的实现 增加缓存等东西 这样每次都不用去修改别人的代码 北京--海淀() 14:14:48 接口 是一种规范 是在充分把握业务的情况下的一种高屋建瓴的 代码实现北京-大雄() 14:14:59 即使实现出错 也不会发生你修改别人的代码后无法恢复的问题
svn版本先不考虑北京-大雄() 14:15:41 这样 对于应用层来说
只要知道UserDao的接口有什么方法就成
不去关注UserDao具体怎么实现 北京-大雄() 14:18:00 有了UserDao的接口 不管是用jdbc还是hibernate实现userDao
都不会对service有影响北京-大雄() 14:18:54 service只关注UserDao这个接口有什么方法
不去关注UserDao具体怎么实现北京-大雄() 14:19:59 说到这里 引申一下
我们很多的Dao都有curd这样的操作北京-大雄() 14:20:22 这时候就可以定义一个泛型的通用dao四川—勇敢的心 14:20:43 我赞成只用泛型北京-大雄() 14:20:59 你的dao继承这个通用dao 这样就可以省去很多代码四川—勇敢的心 14:21:14 这样就不用每一个对象都有增删改查。北京-大雄() 14:21:38 这就是实现和继承用途不同的一个例子
北京-小A() 14:26:47 比如我们还可以实现一个CommonService 和 CommonDao然后在我们的Model中直接创建CRUD方法 直接委托给CommonService 但简单的CRUD时使用起来很方便北京-小A() 14:27:37 我们定义个 AbstractModel 里边实现这些CRUD北京-小A() 14:27:44 子Model 继承即可北京-大雄() 14:27:24 对
这样可以节省很多代码
北京-小A() 14:28:03 public abstract class AbstractModel implements java.io.Serializable {
private static final long serialVersionUID = 9483936L;
public String toString() {
return ToStringBuilder.reflectionToString(this);
public void save() {
ICommonService commonService = SpringContextUtil.getBean(&CommonService&);
commonService.save(this);
public void delete() {
ICommonService commonService = SpringContextUtil.getBean(&CommonService&);
commonService.deleteObject(this);
public void update() {
ICommonService commonService = SpringContextUtil.getBean(&CommonService&);
commonService.update(this);
}}北京-大雄() 14:28:23 现在先直说dao的设计北京-大雄() 14:28:45 待会再讨论service的设计
说太多容易把大家绕晕北京-小A() 14:29:34 DAO本质的功能 还是对 Service层 隐藏 数据放到哪 和 数据从哪里来的问题北京-小A() 14:30:10 这样service 只关心自己的业务北京-小A() 14:30:16 单一职责北京-大雄() 14:32:03 这样就可以设计一个泛型的通用dao接口
再去实现这个通用dao你的dao接口继承通用dao接口
CommonDao&T&
CommonDaoImpl&T& implments CommonDao&T&
extends CommonDao&T&
UserDaoImpl
CommonDaoImpl
implments UserDao北京-大雄() 14:32:41 通用dao
CommonDao中有基本的curd
这样 自己的dao就不需要写了北京-大雄() 14:48:07 现在说到service的接口设计了北京-大雄() 14:48:38 那么在servie成 有么有必要设计接口呢北京-大雄() 14:51:12 我的建议:如果servie有多个实现 那么接口就一定要存在如果service只有一个实现,那么接口就不一定要存在了举个具体的例子:我的售票流程是一个接口,但不同客运站的售票实现是不一样的 这样就可以分别取实现售票流程这个接口
而不是去if else判断北京-大雄() 14:51:38 还有些service的接口
就一个实现 这样的可以不用接口北京-大雄() 14:52:20 结合设计模式
能更好理解接口的设计北京--海淀() 14:52:25 service层 是面对服务的 是对dao层的 一个组装北京-小A() 14:53:22 其实最好要接口没有接口其实很多问题:
不能使用JDK动态代理
北京-大雄() 14:53:21 不能使用jdk动态代理 这个不是理由了 用cglib就行了 北京-大雄() 14:53:42 但是没有接口 使用mock等测试比较麻烦
北京-小A() 14:58:20 不管项目多简单都要定义接口:
接口让我们从全局出发考虑问题 而不是淹没在细节
接口是很好的文档
万一哪天需求变动 可以很容易更换时间
接口在模块间测试时很有用
接口在分模块开发时 能加快开发速度(只要有接口就可以并行开发)北京-小A() 14:58:33 而且接口的语义在哪 北京-小A() 14:58:52 更好的描述了 需要做什么事情 而不需要考虑实现细节河北--?V?() 14:58:45 分工开发的时候也可以通过定义接口把整个项目的框架搭建起来吧? 北京-大雄() 14:59:16 我比较赞成使用接口 这样系统架构的时候就可以快速的出来一个原型北京-大雄() 14:59:26 而不是淹没在具体的细节中北京-小A() 15:00:18 分工开发时 A组 可以先定义好接口
先不管实现B组 直接进行接口的伪实现进行开发北京-大雄() 15:00:06 小A的CommonService和CommonDao的设计比较好 可以省去很多代码北京-大雄() 15:03:31 快速迭代开发 使用接口的优势很明显北京-大雄() 15:03:52 而且后期扩展和维护也很方便北京-小A() 15:05:45 现在有几个子系统1、积分子系统2、积分商城子系统北京-大雄() 15:05:28 网络上说的话 有些时候说不清楚北京-小A() 15:05:57 积分商城要依赖于 积分子系统的北京-小A() 15:06:04 为了并行开发北京-小A() 15:06:11 应该叫 积分子系统 先定义好接口北京-小A() 15:06:27 在积分商城中可以使用伪实现北京-小A() 15:06:31 来同步开发北京--海淀() 15:06:17 对 这是一方面 北京-大雄() 15:06:18 对
可以并行开发北京--海淀() 15:06:27 就和我们的
消费系统一样北京-小A() 15:07:13 最明显的好处 还是解耦北京-小A() 15:07:18 尤其 大系统北京-小A() 15:07:24 会分 很多子系统北京--海淀() 15:07:09 因为整个公司所有的 终端都再使用北京-小A() 15:07:32 子系统直接面向 接口编程北京-大雄() 15:07:35 除了这种子系统的方式
有时候我们是分模块开发的北京--海淀() 15:07:51 但是 实际情况 跟多的 是多个系统之间的 一种协作调用 北京-大雄() 15:07:51 这种用接口优势更明显
正在看 目前实现 是你看过的视频 或正在看的视频
=============广告==============================&
私塾在线独家Spring3开发实战视频活动促销
私塾在线老师出品,品质值得信赖
还有免费课程:
=============广告==============================
一 开发环境
1、动态web工程
2、部分依赖
java代码:
hibernate-release-4.1.0.Final.zip
hibernate-validator-4.2.0.Final.jar
spring-framework-3.1.1.RELEASE-with-docs.zip
proxool-0.9.1.jar
log4j 1.2.16
slf4j -1.6.1
mysql-connector-java-5.1.10.jar
hamcrest 1.3.0RC2
ehcache 2.4.3
3、为了方便学习,暂没有使用maven构建工程
二 工程主要包括内容
1、springMVC + spring3.1.1 + hibernate4.1.0集成
2、通用DAO层 和 Service层
3、二级缓存 Ehcache
4、REST风格的表现层
5、通用分页(两个版本)
5.1、首页 上一页,下一页 尾页 跳转
5.2、上一页 1 2 3 4 5 下一页
6、数据库连接池采用proxool
7、spring集成测试 &&&
8、表现层的 java validator框架验证(采用hibernate-validator-4.2.0实现)
9、视图采用JSP,并进行组件化分离
三 TODO LIST& 将本项目做成脚手架方便以后新项目查询
1、Service层进行AOP缓存(缓存使用Memcached实现)
2、单元测试(把常见的桩测试、伪实现、模拟对象演示一遍 区别集成测试)
3、监控功能
后台查询hibernate二级缓存 hit/miss率功能&&&&&&
& &后台查询当前服务器状态功能(如 线程信息、服务器相关信息)
4、spring RPC功能
5、spring集成 quartz 进行任务调度
6、spring集成 java mail进行邮件发送
7、DAO层将各种常用框架集成进来(方便查询)
8、把工作中经常用的东西 融合进去,作为脚手架,方便以后查询
四 集成重点及常见问题
1、spring-config.xml 配置文件:
1.1、该配置文件只加载除表现层之外的所有bean,因此需要如下配置:
java代码:
&context:component-scan base-package=&cn.javass&&
&context:exclude-filter type=&annotation& expression=&org.springframework.stereotype.Controller&/&
&/context:component-scan&
通过exclude-filter 把所有 @Controller注解的表现层控制器组件排除
1.2、国际化消息文件配置
java代码:
&!-- 国际化的消息资源文件 --&
&bean id=&messageSource& class=&org.springframework.context.support.ReloadableResourceBundleMessageSource&&
&property name=&basenames&&
&!-- 在web环境中一定要定位到classpath 否则默认到当前web应用下找
&value&classpath:messages&/value&
&/property&
&property name=&defaultEncoding& value=&UTF-8&/&
&property name=&cacheSeconds& value=&60&/&
此处basenames内一定是 classpath:messages ,如果你写出“messages”,将会到你的web应用的根下找 即你的messages.properties一定在 web应用/messages.propertis。
1.3、hibernate的sessionFactory配置 需要使用org.springframework.orm.hibernate4.LocalSessionFactoryBean,其他都是类似的,具体看源代码。
1.4、&aop:aspectj-autoproxy expose-proxy=&true&/& 实现@AspectJ注解的,默认使用AnnotationAwareAspectJAutoProxyCreator进行AOP代理,它是BeanPostProcessor的子类,在容器启动时Bean初始化开始和结束时调用进行AOP代理的创建,因此只对当容器启动时有效,使用时注意此处。
1.5、声明式容器管理事务
建议使用声明式容器管理事务,而不建议使用注解容器管理事务(虽然简单),但太分布式了,采用声明式容器管理事务一般只对service层进行处理。
java代码:
&tx:advice id=&txAdvice& transaction-manager=&txManager&&
&tx:attributes&
&tx:method name=&save*& propagation=&REQUIRED& /&
&tx:method name=&add*& propagation=&REQUIRED& /&
&tx:method name=&create*& propagation=&REQUIRED& /&
&tx:method name=&insert*& propagation=&REQUIRED& /&
&tx:method name=&update*& propagation=&REQUIRED& /&
&tx:method name=&merge*& propagation=&REQUIRED& /&
&tx:method name=&del*& propagation=&REQUIRED& /&
&tx:method name=&remove*& propagation=&REQUIRED& /&
&tx:method name=&put*& propagation=&REQUIRED& /&
&tx:method name=&use*& propagation=&REQUIRED&/&
&!--hibernate4必须配置为开启事务 否则 getCurrentSession()获取不到--&
&tx:method name=&get*& propagation=&REQUIRED& read-only=&true& /&
&tx:method name=&count*& propagation=&REQUIRED& read-only=&true& /&
&tx:method name=&find*& propagation=&REQUIRED& read-only=&true& /&
&tx:method name=&list*& propagation=&REQUIRED& read-only=&true& /&
&tx:method name=&*& read-only=&true& /&
&/tx:attributes&
&/tx:advice&
&aop:config expose-proxy=&true&&
&!-- 只对业务逻辑层实施事务 --&
&aop:pointcut id=&txPointcut& expression=&execution(* cn.javass..service..*.*(..))& /&
&aop:advisor advice-ref=&txAdvice& pointcut-ref=&txPointcut&/&
&/aop:config&
此处一定注意 使用 hibernate4,在不使用OpenSessionInView模式时,在使用getCurrentSession()时会有如下问题:
当有一个方法list 传播行为为Supports,当在另一个方法getPage()(无事务)调用list方法时会抛出org.hibernate.HibernateException: No Session found for current thread 异常。
这是因为getCurrentSession()在没有session的情况下不会自动创建一个,不知道这是不是Spring3.1实现的bug,欢迎大家讨论下。
因此最好的解决方案是使用REQUIRED的传播行为。
二、spring-servlet.xml:
2.1、表现层配置文件,只应加装表现层Bean,否则可能引起问题。
java代码:
&!-- 开启controller注解支持 --&
&!-- 注:如果base-package=cn.javass 则注解事务不起作用--&
&context:component-scan base-package=&cn.javass.demo.web.controller&&
&context:include-filter type=&annotation& expression=&org.springframework.stereotype.Controller&/&
&/context:component-scan&
此处只应该加载表现层组件,如果此处还加载dao层或service层的bean会将之前容器加载的替换掉,而且此处不会进行AOP织入,所以会造成AOP失效问题(如事务不起作用),再回头看我们的1.4讨论的。
2.2、&mvc:view-controller path=&/& view-name=&forward:/index&/& 表示当访问主页时自动转发到index控制器。
2.3、静态资源映射
java代码:
&!-- 当在web.xml 中
DispatcherServlet使用
&url-pattern&/&/url-pattern& 映射时,能映射静态资源 --&
&mvc:default-servlet-handler/&
&!-- 静态资源映射 --&
&mvc:resources mapping=&/images/**& location=&/WEB-INF/images/& /&
&mvc:resources mapping=&/css/**& location=&/WEB-INF/css/& /&
&mvc:resources mapping=&/js/**& location=&/WEB-INF/js/& /&
以上是配置文件部分,接下来来看具体代码。
三、通用DAO层Hibernate4实现
为了减少各模块实现的代码量,实际工作时都会有通用DAO层实现,以下是部分核心代码:
java代码:
public abstract class BaseHibernateDao&M extends java.io.Serializable, PK extends java.io.Serializable& implements IBaseDao&M, PK& {
protected static final Logger LOGGER = LoggerFactory.getLogger(BaseHibernateDao.class);
private final Class&M& entityC
private final String HQL_LIST_ALL;
private final String HQL_COUNT_ALL;
private final String HQL_OPTIMIZE_PRE_LIST_ALL;
private final String HQL_OPTIMIZE_NEXT_LIST_ALL;
private String pkName =
@SuppressWarnings(&unchecked&)
public BaseHibernateDao() {
this.entityClass = (Class&M&) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Field[] fields = this.entityClass.getDeclaredFields();
for(Field f : fields) {
if(f.isAnnotationPresent(Id.class)) {
this.pkName = f.getName();
Assert.notNull(pkName);
//TODO @Entity name not null
HQL_LIST_ALL = &from & + this.entityClass.getSimpleName() + & order by & + pkName + & desc&;
HQL_OPTIMIZE_PRE_LIST_ALL = &from & + this.entityClass.getSimpleName() + & where & + pkName + & & ? order by & + pkName + & asc&;
HQL_OPTIMIZE_NEXT_LIST_ALL = &from & + this.entityClass.getSimpleName() + & where & + pkName + & & ? order by & + pkName + & desc&;
HQL_COUNT_ALL = & select count(*) from & + this.entityClass.getSimpleName();
@Autowired
@Qualifier(&sessionFactory&)
private SessionFactory sessionF
public Session getSession() {
//事务必须是开启的,否则获取不到
return sessionFactory.getCurrentSession();
Spring3.1集成Hibernate4不再需要HibernateDaoSupport和HibernateTemplate了,直接使用原生API即可。
四、通用Service层代码 此处省略,看源代码,有了通用代码后CURD就不用再写了。
java代码:
@Service(&UserService&)
public class UserServiceImpl extends BaseService&UserModel, Integer& implements UserService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
private UserDao userD
@Autowired
@Qualifier(&UserDao&)
public void setBaseDao(IBaseDao&UserModel, Integer& userDao) {
this.baseDao = userD
this.userDao = (UserDao) userD
public Page&UserModel& query(int pn, int pageSize, UserQueryModel command) {
return PageUtil.getPage(userDao.countQuery(command) ,pn, userDao.query(pn, pageSize, command), pageSize);
五、表现层 Controller实现
采用SpringMVC支持的REST风格实现,具体看代码,此处我们使用了java Validator框架 来进行 表现层数据验证
在Model实现上加验证注解
java代码:
@Pattern(regexp = &[A-Za-z0-9]{5,20}&, message = &{username.illegal}&) //java validator验证(用户名字母数字组成,长度为5-10)
@NotEmpty(message = &{email.illegal}&)
@Email(message = &{email.illegal}&) //错误消息会自动到MessageSource中查找
@Pattern(regexp = &[A-Za-z0-9]{5,20}&, message = &{password.illegal}&)
@DateFormat( message=&{register.date.error}&)//自定义的验证器
private Date registerD
在Controller中相应方法的需要验证的参数上加@Valid即可
java代码:
@RequestMapping(value = &/user/add&, method = {RequestMethod.POST})
public String add(Model model, @ModelAttribute(&command&) @Valid UserModel command, BindingResult result)
六、Spring集成测试
使用Spring集成测试能很方便的进行Bean的测试,而且使用@TransactionConfiguration(transactionManager = &txManager&, defaultRollback = true)能自动回滚事务,清理测试前后状态。
java代码:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {&classpath:spring-config.xml&})
@Transactional
@TransactionConfiguration(transactionManager = &txManager&, defaultRollback = true)
public class UserServiceTest {
AtomicInteger counter = new AtomicInteger();
@Autowired
private UserService userS
其他部分请直接看源码,欢迎大家讨论。
补充spring3.1.1源代码分析当 传播行为为 Support时报&org.hibernate.HibernateException: No Session found for current thread 异常:
spring3.1开始 不提供(没有这个东西了)Hibernate4的 DaoSupport和Template,,而是直接使用原生的Hibernate4 API&
如在 Hibernate3中 HibernateTemplate中有如下代码&
protected&Session&getSession()&{&&
&&&&&&&&if&(isAlwaysUseNewSession())&{&&
&&&&&&&&&&&&return&SessionFactoryUtils.getNewSession(getSessionFactory(),&getEntityInterceptor());&&
&&&&&&&&}&&
&&&&&&&&else&if&(isAllowCreate())&{&&
&&&&&&&&&&&&return&SessionFactoryUtils.getSession(&&
&&&&&&&&&&&&&&&&&&&&getSessionFactory(),&getEntityInterceptor(),&getJdbcExceptionTranslator());&&
&&&&&&&&}&&
&&&&&&&&else&if&(SessionFactoryUtils.hasTransactionalSession(getSessionFactory()))&{&&
&&&&&&&&&&&&return&SessionFactoryUtils.getSession(getSessionFactory(),&false);&&
&&&&&&&&}&&
&&&&&&&&else&{&&
&&&&&&&&&&&&try&{&&
&&&&&&&&&&&&&&&&return&getSessionFactory().getCurrentSession();&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&catch&(HibernateException&ex)&{&&
&&&&&&&&&&&&&&&&throw&new&DataAccessResourceFailureException(&Could&not&obtain&current&Hibernate&Session&,&ex);&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
但我们使用的是Hibernate4原生API,使用SpringSessionContext获取session,而这个isAllowCreate选项默认为false&
public&Session&currentSession()&throws&HibernateException&{&&
&&&&try&{&&
&&&&&&&&return&(org.hibernate.classic.Session)&SessionFactoryUtils.doGetSession(this.sessionFactory,&false);&&
&&&&catch&(IllegalStateException&ex)&{&&
&&&&&&&&throw&new&HibernateException(ex.getMessage());&&
SessionFactoryUtils类&
public&static&Session&doGetSession(SessionFactory&sessionFactory,&boolean&allowCreate)&&
&&&&&&&&throws&HibernateException,&IllegalStateException&{&&
&&&&return&doGetSession(sessionFactory,&null,&null,&allowCreate);&&
可否认为这是集成Hibernate4的bug,或者采用OpenSessionInView模式解决或使用Required传播行为。
原创内容,转载请注明私塾在线【】
前往页面...
选择一个版面
软件设计专版
Web前端技术
学习问题讨论
面试、就业
版权所有 Copyright(C) 私塾在线学习网

我要回帖

更多关于 打印服务器客户端 的文章

 

随机推荐