`

[转]TOMCAT源码分析——SESSION管理分析(上)

 
阅读更多

前言

  对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录、身份、权限及状态等信息。对于使用Tomcat作为Web容器的大部分开发人员而言,Tomcat是如何实现Session标记用户和管理Session信息的呢?

概述

SESSION

  Tomcat内部定义了Session和HttpSession这两个会话相关的接口,其类继承体系如图1所示。

图1  Session类继承体系

图1中额外列出了Session的类继承体系,这里对他们逐个进行介绍。

Session:Tomcat中有关会话的基本接口规范,图1列出了它定义的主要方法,表1对这些方法进行介绍。

表1  Session接口说明

方法 描述
getCreationTime()/setCreationTime(time : long)  获取与设置Session的创建时间
getId()/setId(id : String)   获取与设置Session的ID
getThisAccessedTime() 获取最近一次请求的开始时间
getLastAccessedTime() 获取最近一次请求的完成时间
getManager()/setManager(manager : Manager)  获取与设置Session管理器
getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 获取与设置Session的最大访问间隔
getSession() 获取HttpSession
isValid()/setValid(isValid : boolean)  获取与设置Session的有效状态
access()/endAccess()  开始与结束Session的访问
expire() 设置Session过期

HttpSession:在HTTP客户端与HTTP服务端提供的一种会话的接口规范,图1列出了它定义的主要方法,表2对这些方法进行介绍。

表2  HttpSession接口说明

方法 描述
getCreationTime() 获取Session的创建时间
getId() 获取Session的ID
getLastAccessedTime() 获取最近一次请求的完成时间
getServletContext()  获取当前Session所属的ServletContext
getMaxInactiveInterval()/setMaxInactiveInterval(interval : int) 获取与设置Session的最大访问间隔
getAttribute(name : String) /setAttribute(name : String, value : Object) 获取与设置Session作用域的属性
removeAttribute(name : String) 清除Session作用域的属性
invalidate() 使Session失效并解除任何与此Session绑定的对象

ClusterSession:集群部署下的会话接口规范,图1列出了它的主要方法,表3对这些方法进行介绍。

表3  ClusterSession接口说明

方法 描述
isPrimarySession() 是否是集群的主Session
setPrimarySession(boolean primarySession) 设置集群主Session

StandardSession:标准的HTTP Session实现,本文将以此实现为例展开。

在部署Tomcat集群时,需要使集群中各个节点的会话状态保持同步,目前Tomcat提供了两种同步策略:

  • ReplicatedSession:每次都把整个会话对象同步给集群中的其他节点,其他节点然后更新整个会话对象。这种实现比较简单方便,但会造成大量无效信息的传输。
  • DeltaSession:对会话中增量修改的属性进行同步。这种方式由于是增量的,所以会大大降低网络I/O的开销,但是实现上会比较复杂因为涉及到对会话属性操作过程的管理。

SESSION管理器

  Tomcat内部定义了Manager接口用于制定Session管理器的接口规范,目前已经有很多Session管理器的实现,如图2所示。

图2  Session管理器的类继承体系

对应图2中的内容我们下面逐个描述:

Manager:Tomcat对于Session管理器定义的接口规范,图2已经列出了Manager接口中定义的主要方法,表4详细描述了这些方法的作用。

表4  Manager接口说明

方法 描述
getContainer()/setContainer(container : Container)  获取或设置Session管理器关联的容器,一般为Context容器
getDistributable()/setDistributable(distributable : boolean)   获取或设置Session管理器是否支持分布式
getMaxInactiveInterval()/setMaxInactiveInterval(interval : int)   获取或设置Session管理器创建的Session的最大非活动时间间隔
getSessionIdLength()/setSessionIdLength(idLength : int)  获取或设置Session管理器创建的Session ID的长度
getSessionCounter()/setSessionCounter(sessionCounter : long)   获取或设置Session管理器创建的Session总数
getMaxActive()/setMaxActive(maxActive : int)  获取或设置当前已激活Session的最大数量
getActiveSessions()   获取当前激活的所有Session
getExpiredSessions()/setExpiredSessions(expiredSessions : long)  获取或设置当前已过期Session的数量
getRejectedSessions()/setRejectedSessions(rejectedSessions : int)  获取或设置已拒绝创建Session的数量
getSessionMaxAliveTime()/setSessionMaxAliveTime(sessionMaxAliveTime : int)   获取或设置已过期Session中的最大活动时长
getSessionAverageAliveTime()/setSessionAverageAliveTime(sessionAverageAliveTime : int)  获取或设置已过期Session的平均活动时长
add(session : Session)/remove(session : Session)  给Session管理器增加或删除活动Session
changeSessionId(session : Session)  给Session设置新生成的随机Session ID
createSession(sessionId : String)  基于Session管理器的默认属性配置创建新的Session
findSession(id : String)  返回sessionId参数唯一标记的Session
findSessions()  返回Session管理器管理的所有活动Session
load()/unload()  从持久化机制中加载Session或向持久化机制写入Session
backgroundProcess()  容器接口中定义的为具体容器在后台处理相关工作的实现,Session管理器基于此机制实现了过期Session的销毁

ManagerBase:封装了Manager接口通用实现的抽象类,未提供对load()/unload()等方法的实现,需要具体子类去实现。所有的Session管理器都继承自ManagerBase。

ClusterManager:在Manager接口的基础上增加了集群部署下的一些接口,所有实现集群下Session管理的管理器都需要实现此接口。

PersistentManagerBase:提供了对于Session持久化的基本实现。

PersistentManager:继承自PersistentManagerBase,可以在Server.xml的<Context>元素下通过配置<Store>元素来使用。PersistentManager可以将内存中的Session信息备份到文件或数据库中。当备份一个Session对象时,该Session对象会被复制到存储器(文件或者数据库)中,而原对象仍然留在内存中。因此即便服务器宕机,仍然可以从存储器中获取活动的Session对象。如果活动的Session对象超过了上限值或者Session对象闲置了的时间过长,那么Session会被换出到存储器中以节省内存空间。

 

StandardManager:不用配置<Store>元素,当Tomcat正常关闭,重启或Web应用重新加载时,它会将内存中的Session序列化到Tomcat目录下的/work/Catalina/host_name/webapp_name/SESSIONS.ser文件中。当Tomcat重启或应用加载完成后,Tomcat会将文件中的Session重新还原到内存中。如果突然终止该服务器,则所有Session都将丢失,因为StandardManager没有机会实现存盘处理。

ClusterManagerBase:提供了对于Session的集群管理实现。

DeltaManager:继承自ClusterManagerBase。此Session管理器是Tomcat在集群部署下的默认管理器,当集群中的某一节点生成或修改Session后,DeltaManager将会把这些修改增量复制到其他节点。

BackupManager:没有继承ClusterManagerBase,而是直接实现了ClusterManager接口。是Tomcat在集群部署下的可选的Session管理器,集群中的所有Session都被全量复制到一个备份节点。集群中的所有节点都可以访问此备份节点,达到Session在集群下的备份效果。

  为简单起见,本文以StandardManager为例讲解Session的管理。StandardManager是StandardContext的子组件,用来管理当前Context的所有Session的创建和维护。如果你已经阅读或者熟悉了《Tomcat源码分析——生命周期管理》一文的内容,那么你就知道当StandardContext正式启动,也就是StandardContext的startInternal方法(见代码清单1)被调用时,StandardContext还会启动StandardManager。

代码清单1

复制代码
    @Override
    protected synchronized void startInternal() throws LifecycleException {
               
        // 省略与Session管理无关的代码
                       
                // Acquire clustered manager
                Manager contextManager = null;
                if (manager == null) {
                    if ( (getCluster() != null) && distributable) {
                        try {
                            contextManager = getCluster().createManager(getName());
                        } catch (Exception ex) {
                            log.error("standardContext.clusterFail", ex);
                            ok = false;
                        }
                    } else {
                        contextManager = new StandardManager();
                    }
                } 
                
                // Configure default manager if none was specified
                if (contextManager != null) {
                    setManager(contextManager);
                }

                if (manager!=null && (getCluster() != null) && distributable) {
                    //let the cluster know that there is a context that is distributable
                    //and that it has its own manager
                    getCluster().registerManager(manager);
                }
     // 省略与Session管理无关的代码
            
            try {
                // Start manager
                if ((manager != null) && (manager instanceof Lifecycle)) {
                    ((Lifecycle) getManager()).start();
                }
    
                // Start ContainerBackgroundProcessor thread
                super.threadStart();
            } catch(Exception e) {
                log.error("Error manager.start()", e);
                ok = false;
            }
            
     // 省略与Session管理无关的代码
    }
复制代码

从代码清单1可以看到StandardContext的startInternal方法中涉及Session管理的执行步骤如下:

  1. 创建StandardManager;
  2. 如果Tomcat结合Apache做了分布式部署,会将当前StandardManager注册到集群中;
  3. 启动StandardManager;

StandardManager的start方法用于启动StandardManager,实现见代码清单2。

代码清单2

复制代码
    @Override
    public synchronized final void start() throws LifecycleException {
        
        //省略状态校验的代码if (state.equals(LifecycleState.NEW)) {
            init();
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        setState(LifecycleState.STARTING_PREP);

        try {
            startInternal();
        } catch (LifecycleException e) {
            setState(LifecycleState.FAILED);
            throw e;
        }

        if (state.equals(LifecycleState.FAILED) ||
                state.equals(LifecycleState.MUST_STOP)) {
            stop();
        } else {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            if (!state.equals(LifecycleState.STARTING)) {
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            }
            
            setState(LifecycleState.STARTED);
        }
    }
复制代码

从代码清单2可以看出启动StandardManager的步骤如下:

  1. 调用init方法初始化StandardManager;
  2. 调用startInternal方法启动StandardManager;

STANDARDMANAGER的初始化

   经过上面的分析,我们知道启动StandardManager的第一步就是调用父类LifecycleBase的init方法,关于此方法已在《Tomcat源码分析——生命周期管理》一文详细介绍,所以我们只需要关心StandardManager的initInternal。StandardManager本身并没有实现initInternal方法,但是StandardManager的父类ManagerBase实现了此方法,其实现见代码清单3。

代码清单3

复制代码
    @Override
    protected void initInternal() throws LifecycleException {
        
        super.initInternal();
        
        setDistributable(((Context) getContainer()).getDistributable());

        // Initialize random number generation
        getRandomBytes(new byte[16]);
    }
复制代码

阅读代码清单3,我们总结下ManagerBase的initInternal方法的执行步骤:

  1. 将容器自身即StandardManager注册到JMX(LifecycleMBeanBase的initInternal方法的实现请参考《Tomcat源码分析——生命周期管理》一文);
  2. 从父容器StandardContext中获取当前Tomcat是否是集群部署,并设置为ManagerBase的布尔属性distributable;
  3. 调用getRandomBytes方法从随机数文件/dev/urandom中获取随机数字节数组,如果不存在此文件则通过反射生成java.security.SecureRandom的实例,用它生成随机数字节数组。

注意:此处调用getRandomBytes方法生成的随机数字节数组并不会被使用,之所以在这里调用实际是为了完成对随机数生成器的初始化,以便将来分配Session ID时使用。

我们详细阅读下getRandomBytes方法的代码实现,见代码清单4。

代码清单4

复制代码
    protected void getRandomBytes(byte bytes[]) {
        // Generate a byte array containing a session identifier
        if (devRandomSource != null && randomIS == null) {
            setRandomFile(devRandomSource);
        }
        if (randomIS != null) {
            try {
                int len = randomIS.read(bytes);
                if (len == bytes.length) {
                    return;
                }
                if(log.isDebugEnabled())
                    log.debug("Got " + len + " " + bytes.length );
            } catch (Exception ex) {
                // Ignore
            }
            devRandomSource = null;
            
            try {
                randomIS.close();
            } catch (Exception e) {
                log.warn("Failed to close randomIS.");
            }
            
            randomIS = null;
        }
        getRandom().nextBytes(bytes);
    }
复制代码

代码清单4中的setRandomFile方法(见代码清单5)用于从随机数文件/dev/urandom中获取随机数字节数组。

代码清单5

复制代码
    public void setRandomFile( String s ) {
        // as a hack, you can use a static file - and generate the same
        // session ids ( good for strange debugging )
        if (Globals.IS_SECURITY_ENABLED){
            randomIS = AccessController.doPrivileged(new PrivilegedSetRandomFile(s));
        } else {
            try{
                devRandomSource=s;
                File f=new File( devRandomSource );
                if( ! f.exists() ) return;
                randomIS= new DataInputStream( new FileInputStream(f));
                randomIS.readLong();
                if( log.isDebugEnabled() )
                    log.debug( "Opening " + devRandomSource );
            } catch( IOException ex ) {
                log.warn("Error reading " + devRandomSource, ex);
                if (randomIS != null) {
                    try {
                        randomIS.close();
                    } catch (Exception e) {
                        log.warn("Failed to close randomIS.");
                    }
                }
                devRandomSource = null;
                randomIS=null;
            }
        }
    }
复制代码

代码清单4中的getRandom方法(见代码清单6)通过反射生成java.security.SecureRandom的实例,并用此实例生成随机数字节数组。

代码清单6

复制代码
    public Random getRandom() {
        if (this.random == null) {
            // Calculate the new random number generator seed
            long seed = System.currentTimeMillis();
            long t1 = seed;
            char entropy[] = getEntropy().toCharArray();
            for (int i = 0; i < entropy.length; i++) {
                long update = ((byte) entropy[i]) << ((i % 8) * 8);
                seed ^= update;
            }
            try {
                // Construct and seed a new random number generator
                Class<?> clazz = Class.forName(randomClass);
                this.random = (Random) clazz.newInstance();
                this.random.setSeed(seed);
            } catch (Exception e) {
                // Fall back to the simple case
                log.error(sm.getString("managerBase.random", randomClass),
                        e);
                this.random = new java.util.Random();
                this.random.setSeed(seed);
            }
            if(log.isDebugEnabled()) {
                long t2=System.currentTimeMillis();
                if( (t2-t1) > 100 )
                    log.debug(sm.getString("managerBase.seeding", randomClass) + " " + (t2-t1));
            }
        }
        
        return (this.random);

    }
复制代码

根据以上的分析,StandardManager的初始化主要就是执行了ManagerBase的initInternal方法。

STANDARDMANAGER的启动

  调用StandardManager的startInternal方法用于启动StandardManager,见代码清单7。

 代码清单7

复制代码
    @Override
    protected synchronized void startInternal() throws LifecycleException {

        // Force initialization of the random number generator
        if (log.isDebugEnabled())
            log.debug("Force random number initialization starting");
        generateSessionId();
        if (log.isDebugEnabled())
            log.debug("Force random number initialization completed");

        // Load unloaded sessions, if any
        try {
            load();
        } catch (Throwable t) {
            log.error(sm.getString("standardManager.managerLoad"), t);
        }

        setState(LifecycleState.STARTING);
    }
复制代码

 从代码清单7可以看出启动StandardManager的步骤如下:

步骤一 调用generateSessionId方法(见代码清单8)强制初始化随机数生成器;

注意:此处调用generateSessionId方法的目的不是为了生成Session ID,而是为了强制初始化随机数生成器。

代码清单8

复制代码
    protected synchronized String generateSessionId() {

        byte random[] = new byte[16];
        String jvmRoute = getJvmRoute();
        String result = null;

        // Render the result as a String of hexadecimal digits
        StringBuilder buffer = new StringBuilder();
        do {
            int resultLenBytes = 0;
            if (result != null) {
                buffer = new StringBuilder();
                duplicates++;
            }

            while (resultLenBytes < this.sessionIdLength) {
                getRandomBytes(random);
                random = getDigest().digest(random);
                for (int j = 0;
                j < random.length && resultLenBytes < this.sessionIdLength;
                j++) {
                    byte b1 = (byte) ((random[j] & 0xf0) >> 4);
                    byte b2 = (byte) (random[j] & 0x0f);
                    if (b1 < 10)
                        buffer.append((char) ('0' + b1));
                    else
                        buffer.append((char) ('A' + (b1 - 10)));
                    if (b2 < 10)
                        buffer.append((char) ('0' + b2));
                    else
                        buffer.append((char) ('A' + (b2 - 10)));
                    resultLenBytes++;
                }
            }
            if (jvmRoute != null) {
                buffer.append('.').append(jvmRoute);
            }
            result = buffer.toString();
        } while (sessions.containsKey(result));
        return (result);

    }
复制代码

步骤二  加载持久化的Session信息。为什么Session需要持久化?由于在StandardManager中,所有的Session都维护在一个ConcurrentHashMap中,因此服务器重启或者宕机会造成这些Session信息丢失或失效,为了解决这个问题,Tomcat将这些Session通过持久化的方式来保证不会丢失。下面我们来看看StandardManager的load方法的实现,见代码清单9所示。

代码清单9

复制代码
    public void load() throws ClassNotFoundException, IOException {
        if (SecurityUtil.isPackageProtectionEnabled()){
            try{
                AccessController.doPrivileged( new PrivilegedDoLoad() );
            } catch (PrivilegedActionException ex){
                Exception exception = ex.getException();
                if (exception instanceof ClassNotFoundException){
                    throw (ClassNotFoundException)exception;
                } else if (exception instanceof IOException){
                    throw (IOException)exception;
                }
                if (log.isDebugEnabled())
                    log.debug("Unreported exception in load() "
                        + exception);
            }
        } else {
            doLoad();
        }
    }
复制代码

如果需要安全机制是打开的并且包保护模式打开,会通过创建PrivilegedDoLoad来加载持久化的Session,其实现如代码清单10所示。

代码清单10

复制代码
    private class PrivilegedDoLoad
        implements PrivilegedExceptionAction<Void> {

        PrivilegedDoLoad() {
            // NOOP
        }

        public Void run() throws Exception{
           doLoad();
           return null;
        }
    }
复制代码

从代码清单10看到实际负责加载的方法是doLoad,根据代码清单9知道默认情况下,加载Session信息的方法也是doLoad。所以我们只需要看看doLoad的实现了,见代码清单11。

代码清单11

复制代码
    protected void doLoad() throws ClassNotFoundException, IOException {
        if (log.isDebugEnabled())
            log.debug("Start: Loading persisted sessions");

        // Initialize our internal data structures
        sessions.clear();

        // Open an input stream to the specified pathname, if any
        File file = file();
        if (file == null)
            return;
        if (log.isDebugEnabled())
            log.debug(sm.getString("standardManager.loading", pathname));
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        ObjectInputStream ois = null;
        Loader loader = null;
        ClassLoader classLoader = null;
        try {
            fis = new FileInputStream(file.getAbsolutePath());
            bis = new BufferedInputStream(fis);
            if (container != null)
                loader = container.getLoader();
            if (loader != null)
                classLoader = loader.getClassLoader();
            if (classLoader != null) {
                if (log.isDebugEnabled())
                    log.debug("Creating custom object input stream for class loader ");
                ois = new CustomObjectInputStream(bis, classLoader);
            } else {
                if (log.isDebugEnabled())
                    log.debug("Creating standard object input stream");
                ois = new ObjectInputStream(bis);
            }
        } catch (FileNotFoundException e) {
            if (log.isDebugEnabled())
                log.debug("No persisted data file found");
            return;
        } catch (IOException e) {
            log.error(sm.getString("standardManager.loading.ioe", e), e);
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException f) {
                    // Ignore
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException f) {
                    // Ignore
                }
            }
            throw e;
        }

        // Load the previously unloaded active sessions
        synchronized (sessions) {
            try {
                Integer count = (Integer) ois.readObject();
                int n = count.intValue();
                if (log.isDebugEnabled())
                    log.debug("Loading " + n + " persisted sessions");
                for (int i = 0; i < n; i++) {
                    StandardSession session = getNewSession();
                    session.readObjectData(ois);
                    session.setManager(this);
                    sessions.put(session.getIdInternal(), session);
                    session.activate();
                    if (!session.isValidInternal()) {
                        // If session is already invalid,
                        // expire session to prevent memory leak.
                        session.setValid(true);
                        session.expire();
                    }
                    sessionCounter++;
                }
            } catch (ClassNotFoundException e) {
                log.error(sm.getString("standardManager.loading.cnfe", e), e);
                try {
                    ois.close();
                } catch (IOException f) {
                    // Ignore
                }
                throw e;
            } catch (IOException e) {
                log.error(sm.getString("standardManager.loading.ioe", e), e);
                try {
                    ois.close();
                } catch (IOException f) {
                    // Ignore
                }
                throw e;
            } finally {
                // Close the input stream
                try {
                    ois.close();
                } catch (IOException f) {
                    // ignored
                }

                // Delete the persistent storage file
                if (file.exists() )
                    file.delete();
            }
        }

        if (log.isDebugEnabled())
            log.debug("Finish: Loading persisted sessions");
    }
复制代码

 从代码清单11看到StandardManager的doLoad方法的执行步骤如下:

  1. 清空sessions缓存维护的Session信息;
  2. 调用file方法返回当前Context下的Session持久化文件,比如:D:\workspace\Tomcat7.0\work\Catalina\localhost\host-manager\SESSIONS.ser;
  3. 打开Session持久化文件的输入流,并封装为CustomObjectInputStream;
  4. 从Session持久化文件读入持久化的Session的数量,然后逐个读取Session信息并放入sessions缓存中。

至此,有关StandardManager的启动就介绍到这里,我将会在《TOMCAT源码分析——SESSION管理分析(下)》一文讲解Session的分配、追踪、销毁等内容。

如需转载,请标明本文作者及出处——作者:jiaan.gja,本文原创首发:博客园,原文链接:http://www.cnblogs.com/jiaan-geng/p/4913616.html 
道生一,一生二,二生三,三生万物。
分享到:
评论

相关推荐

    Tomcat集群——使用MSM管理集群Session

    【标题】:“Tomcat集群——使用MSM管理集群Session” 在分布式系统中,尤其是在基于Java的Web应用中,实现session的共享是确保用户状态在不同服务器之间无缝切换的关键。Tomcat,作为流行的开源Servlet容器,提供...

    tomcat6源码分析

    《Tomcat6源码分析——深入理解Web服务器的运行机制》 Tomcat6作为Apache软件基金会的Jakarta项目的一部分,是一款广泛使用的Java Servlet容器,它实现了Java Servlet和JavaServer Pages(JSP)规范,为开发和部署...

    TOMCAT源码分析(启动框架).pdf

    ### TOMCAT源码分析——启动框架详解 #### 一、前言 TOMCAT作为一款广泛使用的开源Java Servlet容器,其内部实现复杂且强大。本文旨在深入剖析TOMCAT的启动框架及其整体架构,帮助读者更好地理解其工作原理。...

    JSP源码——图书管理系统(java+mssql).zip

    《JSP源码——图书管理系统(java+mssql)》是一个基于Java技术与Microsoft SQL Server数据库的Web应用程序,旨在实现对图书信息的高效管理。这个系统涵盖了图书的增删改查、用户管理、借阅与归还等功能,是学习JSP...

    JSP源码——THB2B.zip

    源码分析: 1. **目录结构**:源码的目录结构通常包含Web-INF、WEB-INF/classes、WEB-INF/lib、jsp页面、静态资源文件夹等。Web-INF下存放的是应用配置和类文件,classes存储编译后的Java类,lib存放依赖的jar包,...

    JSP源码——[新闻文章]永恒文章管理系统(YHCMS) v2.0 源码版_yhcms_v20_src.zip

    【JSP源码详解——永恒文章管理系统YHCMS v2.0】 JSP(JavaServer Pages)是一种基于Java技术的动态网页开发技术,它允许开发者将静态HTML与动态Java代码结合,以创建交互式Web应用。永恒文章管理系统YHCMS v2.0是...

    JSP源码——[影音娱乐]彩森视频网络电台DQUS版_dqus.zip

    JSP源码可能包括处理用户登录、注册、权限验证的逻辑,以及防止未授权访问的措施,如会话管理(session management)和cookie。 6. **性能优化** 大型的影音娱乐平台需要考虑性能优化,这可能涉及到缓存策略、负载...

    JSP源码——[信息办公]玉玺学生信息管理系统_webapps.zip

    【JSP源码——[信息办公]玉玺学生信息管理系统_webapps.zip】是一个包含JSP技术的源代码项目,主要用于实现学生信息管理的信息化办公系统。这个系统可能涵盖了对学生数据的增删查改、统计分析等功能,对于理解和学习...

    Tomcat与JavaWeb开发技术详解(孙卫琴)

    《Tomcat与JavaWeb开发技术详解》一书深入剖析了JavaWeb开发中的核心组件——Tomcat服务器,旨在帮助开发者理解并掌握如何有效地使用Tomcat进行Web应用的部署和管理。Tomcat作为开源的轻量级应用服务器,是JavaEE...

    httpSession

    6. **源码解析**:深入到具体的Web容器(如Tomcat、Jetty)的源代码中,理解它们如何处理session的创建、存储和销毁。 7. **工具使用**:可能介绍了一些辅助管理session的工具或库,如使用浏览器开发者工具查看...

    JSP实例开发源码——家庭理财系统(java+applet).zip

    【JSP实例开发源码——家庭理财系统(java+applet)】 这个压缩包包含的是一个基于JSP技术的家庭理财系统实例的源代码。JSP(Java Server Pages)是一种动态网页开发技术,它允许开发者在HTML页面中嵌入Java代码,以...

    WEB服务器工作机制由浅至深(6):【How Tomcat Works】第12章StandardContext翻译分析

    每个Web应用程序在Tomcat中都有一个对应的StandardContext实例,它管理着Web应用的上下文、类加载器、session配置、过滤器链等关键元素。 2. **Web应用程序上下文** Web应用程序上下文是Tomcat识别不同Web应用的...

    TP312_Ch700_JSP项目开发实践课本源码——第三部分

    《TP312_Ch700_JSP项目开发实践课本源码——第三部分》是针对JSP(JavaServer Pages)项目开发的一个学习资源,主要涵盖了课程的最后三个章节。这个压缩包包含了一系列的源代码文件,旨在帮助学习者深入理解和实践...

    学生管理系统源码,学生管理系统源码php,Java源码.zip.zip

    《学生管理系统源码详解——PHP与Java实现》 学生管理系统是一种常见的信息管理软件,它用于高效地管理和跟踪学生的个人信息、成绩、出勤等数据。在本文中,我们将深入探讨两个主流编程语言——PHP和Java实现的学生...

    求精要诀——JavaEE编程开发案例精讲 源代码

    3. **EJB(Enterprise JavaBeans)**:可能包含实体Bean(Entity Beans)、会话Bean(Session Beans)等,演示企业级服务如事务管理、安全性、持久化等。 4. **JPA(Java Persistence API)**:可能展示了如何使用...

    JSP源码——网上购物系统(JavaBean+Servlet+jsp).zip

    完成编码后,系统需要在Web服务器如Tomcat上进行部署,配置合适的环境变量和数据库连接。同时,进行全面的单元测试和集成测试,确保所有功能正常运行。 总的来说,这个基于JavaBean、Servlet和JSP的网上购物系统,...

    how tomcat works和jetty-src和tomcat7-src

    标题 "how tomcat works" 和 "jetty-src" 以及 "tomcat7-src" 提到的是两个著名的Java Web服务器——Apache Tomcat和Jetty的源代码。这些源代码是理解Web服务器工作原理的重要资源,尤其是对于Java开发者和系统管理...

    LambdaProbe Tomcat Manager

    LambdaProbe的名字来源于其核心概念——它像一个“探针”一样深入到Tomcat内部,提供实时的性能数据和管理操作。 首先,我们来探讨一下LambdaProbe的主要特点。它提供了多种监控指标,包括但不限于: 1. **服务器...

    有一个JSP网站源码 信息管理系统(完整实用)

    **JSP网站源码——信息管理系统详解** JSP(JavaServer Pages)是一种基于Java技术的动态网页开发标准,它允许开发者将HTML、CSS、JavaScript与Java代码混合编写,以实现服务器端的动态网页生成。本套"信息管理系统...

    JavaWeb宿舍管理系统项目源码 下载 项目分管理员、宿舍管理员以及学生三个类型。.zip

    本项目是一个基于JavaWeb技术实现的宿舍管理系统,旨在提供一个高效、便捷的宿舍管理平台,以满足不同角色——管理员、宿舍管理员和学生的使用需求。系统设计了三个主要用户类型,分别为管理员、宿舍管理员和学生,...

Global site tag (gtag.js) - Google Analytics