精华内容
下载资源
问答
  • php 广告加载类

    万次阅读 2013-08-07 00:04:16
    php 广告加载类,支持异步与同步加载。需要使用Jquery ADLoader.class.php <?php /** 广告加载管理类 * Date: 2013-08-04 * Author: fdipzone * Ver: 1.0 * * Func: * public load 加载广告集合 * public setConfig...

    php 广告加载类,支持异步与同步加载。需要使用Jquery

    ADLoader.class.php

    <?php
    /** 广告加载管理类
    *   Date:   2013-08-04
    *   Author: fdipzone
    *   Ver:    1.0
    *
    *   Func:
    *   public  load         加载广告集合
    *   public  setConfig    广告配置
    *   private getAds       根据channel创建广告集合
    *   private genZoneId    zoneid base64_encode 处理
    *   private genHtml      生成广告html
    *   private checkBrowser 检查是否需要同步加载的浏览器
    */
    
    class ADLoader{ // class start
    
        private static $_ads = array();     // 广告集合
        private static $_step = 300;        // 广告加载间隔
        private static $_async = true;      // 是否异步加载
        private static $_config = array();  // 广告设置文件
        private static $_jsclass = null;    // 广告JS class
    
    
        /** 加载广告集合
        * @param String  $channel 栏目,对应config文件
        * @param int     $step    广告加载间隔
        * @param boolean $async   是否异步加载
        */
        public static function load($channel='', $step='', $async=''){
            if(isset($step) && is_numeric($step) && $step>0){
                self::$_step = $step;
            }
    
            if(isset($async) && is_bool($async)){
                self::$_async = $async;
            }
    
            // 判断浏览器,如IE强制使用同步加载
            if(!self::checkBrowser()){
                self::$_async = false;
            }
    
            self::getAds($channel);
            self::genZoneId();
    
            return self::genHtml();
        }
    
    
        /** 设置config
        * @param String $config  广告配置
        * @param String $jsclass js class文件路径
        */
        public static function setConfig($config=array(), $jsclass=''){
            self::$_config = $config;
            self::$_jsclass = $jsclass;
        }
    
    
        /** 根据channel创建广告集合
        * @param String $channel 栏目
        */
        private static function getAds($channel=''){
            $AD_Config = self::$_config;
            if($AD_Config!=null){
                self::$_ads = isset($AD_Config[$channel])? $AD_Config[$channel] : $AD_Config['default'];
            }
        }
    
    
        /** zoneid base64_encode 处理 */
        private static function genZoneId(){
    
            // 同步加载广告不需要处理zoneid
            if(!self::$_async){
                return ;
            }
    
            $ads = self::$_ads;
            for($i=0,$len=count($ads); $i<$len; $i++){
                if(isset($ads[$i]['zoneId'])){
                    $ads[$i]['zoneId'] = base64_encode('var zoneid='.$ads[$i]['zoneId'].';');
                }
            }
            self::$_ads = $ads;
        }
    
    
        /** 生成广告html */
        private static function genHtml(){
            $ads = self::$_ads;
            $html = array();
            if(self::$_jsclass!=null && $ads){
                array_push($html, '<script type="text/javascript" src="'.self::$_jsclass.'"></script>');
    
    			// 同步需要预先加载
                if(!self::$_async){
    				foreach($ads as $ad){
    					array_push($html, '<div id="'.$ad['domId'].'_container" style="display:none">');
    					array_push($html, '<script type="text/javascript">');
    					array_push($html, 'ADLoader.preload('.json_encode($ad).');');
    					array_push($html, '</script>');
    					array_push($html, '</div>');
    				}
                }
    
    			array_push($html, '<script type="text/javascript">');
                array_push($html, 'var ads='.json_encode($ads).';');
                array_push($html, '$(document).ready(function(){ ADLoader.load(ads, '.self::$_step.', '.intval(self::$_async).'); });');
                array_push($html, '</script>');
            }
            return implode("\r\n", $html);
        }
    
    
        /** 判断是否需要强制同步加载的浏览器 */
        private static function checkBrowser(){
            $user_agent = $_SERVER['HTTP_USER_AGENT'];
            if(strstr($user_agent,'MSIE')!=''){
                return false;
            }
            return true;
        }
    
    } // class end
    
    ?>

    ADConfig.php

    <?php
    /** 广告配置文件 **/
    
    return array(
    
        'case_openx' => array(
            array(
                'type' => 'openx',
                'domId' => 'ad_728x90',
                'zoneId' => 452
            ),
            array(
                'type' => 'openx',
                'domId' => 'ad_300x250',
                'zoneId' => 449
            ),
            array(
                'type' => 'openx',
                'domId' => 'ad_l2_300x250',
                'zoneId' => 394
            ),
        ),
    
        'case_url' => array(
            array(
                'type' => 'url',
                'domId' => 'ad_728x90',
                'url' => 'adurl.php?zoneid=452'
            ),
            array(
                'type' => 'url',
                'domId' => 'ad_300x250',
                'url' => 'adurl.php?zoneid=449'
            ),
            array(
                'type' => 'url',
                'domId' => 'ad_l2_300x250',
                'url' => 'adurl.php?zoneid=394'
            )
        ),
    
        'case_sync_openx' => array(
            array(
                'type' => 'openx',
                'domId' => 'ad_728x90',
                'zoneId' => 452
            ),
            array(
                'type' => 'openx',
                'domId' => 'ad_300x250',
                'zoneId' => 449
            ),
            array(
                'type' => 'openx',
                'domId' => 'ad_l2_300x250',
                'zoneId' => 394
            ),
        ),
    
        'default' => array(
            array(
                'type' => 'openx',
                'domId' => 'ad_728x90',
                'zoneId' => 452
            ),
            array(
                'type' => 'openx',
                'domId' => 'ad_300x250',
                'zoneId' => 449
            ),
            array(
                'type' => 'openx',
                'domId' => 'ad_l2_300x250',
                'zoneId' => 394
            ),
        ),
    
    );
    
    ?>
    ADLoader.js
    /** 异步加载广告
    *   Date:   2013-08-04
    *   Author: fdipzone
    *   Ver:    1.0
    */
    var ADLoader = (function(){
    
        var _ads = [],     // 广告集合
            _step = 300,   // 广告加载间隔
            _async = true, // 是否异步加载
            _loaded = 0;   // 已经加载的广告数
    
        
        /** loadAd 循环加载广告
        * @param int c 第几个广告
        */
        function loadAD(c){
            if(_loaded>=_ads.length){
                return ;
            }
    
            if($('#'+_ads[c].domId).length>0){ // 判断dom是否存在
    
                if(_async){ // 异步执行
    
                    crapLoader.loadScript(getScript(_ads[c]), _ads[c].domId, {
                        success: function(){
                            completeAd();
                        }
                    });
                
                }else{ // 将同步加载的广告显示
    
                    var ad_container = $('#'+_ads[c].domId+'_container');
                    ad_container.find('embed').attr('wmode','transparent').end().find('object').each(function(k, v){
                        v.wmode = 'transparent'; // 将flash变透明
                    });
                    $('#'+_ads[c].domId)[0].appendChild(ad_container[0]);
                    ad_container.show();
                    
                    completeAd();
    
                }
    
            }else{ // dom不存在
                completeAd();
            }
        }
    
        /** 加载完广告后处理 */
        function completeAd(){
            _loaded ++;
            setTimeout(function(){
                loadAD(_loaded);
            }, _step);        
        }
    
        /** 获取广告
        * @param Array ad 广告参数
        */
        function getScript(ad){
            var ret = null;
    
            switch(ad.type){
                case 'openx':  // openx code ad
                    ret = 'data:text/javascript;base64,' + ad.zoneId + 'dmFyIG0zX3UgPSAobG9jYXRpb24ucHJvdG9jb2w9PSdodHRwczonPydodHRwczovL2Fkcy5ubWcuY29tLmhrL3d3dy9kZWxpdmVyeS9hanMucGhwJzonaHR0cDovL2Fkcy5ubWcuY29tLmhrL3d3dy9kZWxpdmVyeS9hanMucGhwJyk7CnZhciBtM19yID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpKjk5OTk5OTk5OTk5KTsKaWYgKCFkb2N1bWVudC5NQVhfdXNlZCkgZG9jdW1lbnQuTUFYX3VzZWQgPSAnLCc7CmRvY3VtZW50LndyaXRlICgiPHNjciIrImlwdCB0eXBlPSd0ZXh0L2phdmFzY3JpcHQnIHNyYz0nIittM191KTsKZG9jdW1lbnQud3JpdGUgKCI/em9uZWlkPSIgKyB6b25laWQpOwpkb2N1bWVudC53cml0ZSAoJyZhbXA7Y2I9JyArIG0zX3IpOwppZiAoZG9jdW1lbnQuTUFYX3VzZWQgIT0gJywnKSBkb2N1bWVudC53cml0ZSAoIiZhbXA7ZXhjbHVkZT0iICsgZG9jdW1lbnQuTUFYX3VzZWQpOwpkb2N1bWVudC53cml0ZSAoZG9jdW1lbnQuY2hhcnNldCA/ICcmYW1wO2NoYXJzZXQ9Jytkb2N1bWVudC5jaGFyc2V0IDogKGRvY3VtZW50LmNoYXJhY3RlclNldCA/ICcmYW1wO2NoYXJzZXQ9Jytkb2N1bWVudC5jaGFyYWN0ZXJTZXQgOiAnJykpOwpkb2N1bWVudC53cml0ZSAoIiZhbXA7bG9jPSIgKyBlc2NhcGUod2luZG93LmxvY2F0aW9uKSk7CmlmIChkb2N1bWVudC5yZWZlcnJlcikgZG9jdW1lbnQud3JpdGUgKCImYW1wO3JlZmVyZXI9IiArIGVzY2FwZShkb2N1bWVudC5yZWZlcnJlcikpOwppZiAoZG9jdW1lbnQuY29udGV4dCkgZG9jdW1lbnQud3JpdGUgKCImY29udGV4dD0iICsgZXNjYXBlKGRvY3VtZW50LmNvbnRleHQpKTsKaWYgKGRvY3VtZW50Lm1tbV9mbykgZG9jdW1lbnQud3JpdGUgKCImYW1wO21tbV9mbz0xIik7CmRvY3VtZW50LndyaXRlICgiJz48XC9zY3IiKyJpcHQ+Iik7';
                    break;
               
                case 'url': // url ad
                    ret = ad.url;
                    break;
            }
    
            return ret;
        }
    
        /** 同步加载广告
        * @param Array ad 广告参数
        */
        function writeAd(ad){
            switch(ad.type){
                case 'openx':
                    var m3_u = (location.protocol=='https:'?'https://ads.nmg.com.hk/www/delivery/ajs.php':'http://ads.nmg.com.hk/www/delivery/ajs.php');
                    var m3_r = Math.floor(Math.random()*99999999999);
                    if (!document.MAX_used) document.MAX_used = ',';
                    document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
                    document.write ("?zoneid=" + ad.zoneId);
                    document.write ('&cb=' + m3_r);
                    if (document.MAX_used != ',') document.write ("&exclude=" + document.MAX_used);
                    document.write (document.charset ? '&charset='+document.charset : (document.characterSet ? '&charset='+document.characterSet : ''));
                    document.write ("&loc=" + escape(window.location));
                    if (document.referrer) document.write ("&referer=" + escape(document.referrer));
                    if (document.context) document.write ("&context=" + escape(document.context));
                    if (document.mmm_fo) document.write ("&mmm_fo=1");
                    document.write ("'><\/scr"+"ipt>");
                    break;
                 case 'url':
                    document.write ('<script type="text/javascript" src="' + ad.url + '"></script>');
                    break;
            }
        }
    
    
        obj = {
    
            /** 加载广告
            * @param Array   ads   广告集合
            * @param int     step  广告加载间隔
            * @param boolean async true:异步加载 false:同步加载
            */
            load: function(ads, step, async){
                _ads = ads;
    
                if(typeof(step)!='undefined'){
                    _step = step;
                }
    
                if(typeof(async)!='undefined'){
                    _async = async;
                }
    
                loadAD(_loaded);
            },
    
            /** 预加载广告 */
            preload: function(ad){
                if($('#'+ad.domId).length>0){   // 判断dom是否存在
                    writeAd(ad);
                }
            }
    
        }
    
        return obj;
    
    }());
    
    
    /* crapLoader */
    var crapLoader = (function() {
        
        var isHijacked = false,
            queue = [],
            inputBuffer = [],
            writeBuffer = {},
            loading = 0,
            elementCache = {},
            returnedElements = [],
            splitScriptsRegex = /(<script[\s\S]*?<\/script>)/gim,
            globalOptions = {
                autoRelease: true,
                parallel: true,
                debug: false
            },
            defaultOptions = {
                charset: undefined,
                success: undefined,
                func: undefined,
                src: undefined,
                timeout: 3000
            },publ,
            head = document.getElementsByTagName("head")[0] || document.documentElement,
            support = {
                scriptOnloadTriggeredAccurately: false,
                splitWithCapturingParentheses: ("abc".split(/(b)/)[1]==="b")
            };
    
    
        
        function checkQueue () {
            if(queue.length) {
                loadScript( queue.shift() );
            } else if(loading === 0 && globalOptions.autoRelease) {
                debug("Queue is empty. Auto-releasing.");
                publ.release();
            }
        }
    
        function checkWriteBuffer (obj) {
            var buffer = writeBuffer[obj.domId],
                returnedEl;
    
            if(buffer && buffer.length) {
                writeHtml( buffer.shift(), obj );
    
            } else {
                while (returnedElements.length > 0) {
                    returnedEl = returnedElements.pop();
                    var id = returnedEl.id;
                    var elInDoc = getElementById(id);
                    if (!elInDoc) { continue; }
                    var parent = elInDoc.parentNode;
                    elInDoc.id = id + "__tmp";
                    parent.insertBefore(returnedEl, elInDoc);
                    parent.removeChild(elInDoc);
                }
                finished(obj);
            }
        }
    
        function debug (message, obj) {
            if(!globalOptions.debug || !window.console) { return; }
            var objExtra = "";
            if(obj) {
                objExtra = "#"+obj.domId+" ";
                var depth = obj.depth;
                while(depth--) { objExtra += "    "; }
            }
            console.log("crapLoader " + objExtra + message);
        }
    
        function extend (t, s) {
            var k;
            if(!s) { return t; }
            for(k in s) {
                t[k] = s[k];
            }
            return t;
        }
    
        function finished (obj) {
            if(obj.success && typeof obj.success === "function") {
                obj.success.call( document.getElementById(obj.domId) );
            }
    
            checkQueue();
        }
    
        function flush (obj) {
            var domId = obj.domId,
               outputFromScript,
               htmlPartArray;
    
            outputFromScript = stripNoScript( inputBuffer.join("") );
            inputBuffer = [];
    
            htmlPartArray = separateScriptsFromHtml( outputFromScript );
    
            if(!writeBuffer[domId]) {
                writeBuffer[domId] = htmlPartArray;
            } else {
                Array.prototype.unshift.apply(writeBuffer[domId], htmlPartArray);
            }
            checkWriteBuffer(obj);
        }
    
        function getCachedElById (domId) {
            return elementCache[domId] || (elementCache[domId] = document.getElementById(domId));
        }
    
        function getElementById (domId) {
            return ( publ.orgGetElementById.call ?
                publ.orgGetElementById.call(document, domId) :
                publ.orgGetElementById(domId) );
        }
    
        function getElementByIdReplacement (domId) {
            var el = getElementById(domId),
                html, frag, div, found;
    
            function traverseForElById(domId, el) {
                var children = el.children, i, l, child;
                if(children && children.length) {
                    for(i=0,l=children.length; i<l; i++) {
                        child = children[i];
                        if(child.id && child.id === domId) { return child; }
                        if(child.children && child.children.length) {
                            var tmp = traverseForElById(domId, child);
                            if (tmp) return tmp;
                        }
                    }
                }
            }
    
            function searchForAlreadyReturnedEl(domId) {
                var i, l, returnedEl;
                for(i=0,l=returnedElements.length; i<l; i++) {
                    returnedEl = returnedElements[i];
                    if(returnedEl.id === domId) { return returnedEl; }
                }
            }
    
            if(el) { return el; }
    
            if (returnedElements.length) {
                found = searchForAlreadyReturnedEl(domId);
                if (found) {
                    return found;
                }
            }
    
            if(inputBuffer.length) {
                html = inputBuffer.join("");
                frag = document.createDocumentFragment();
                div = document.createElement("div");
                div.innerHTML = html;
                frag.appendChild(div);
                found = traverseForElById(domId, div);
                if (found) {
                    returnedElements.push(found);
                }
                return found;
            }
        }
    
        var globalEval = (function () {
            return (window.execScript ? function(code, language) {
                window.execScript(code, language || "JavaScript");
            } : function(code, language) {
                if(language && !/^javascript/i.test(language)) { return; }
                window.eval.call(window, code);
            });
        }());
    
        function isScript (html) {
            return html.toLowerCase().indexOf("<script") === 0;
        }
    
        function runFunc (obj) {
            obj.func();
            obj.depth++;
            flush(obj);
        }
    
        function loadScript (obj) {
            loading++;
            // async loading code from jQuery
            var script = document.createElement("script");
            if(obj.type) { script.type = obj.type; }
            if(obj.charset) { script.charset = obj.charset; }
            if(obj.language) { script.language = obj.language; }
    
            logScript(obj);
    
            var done = false;
            // Attach handlers for all browsers
            script.onload = script.onreadystatechange = function() {
                loading--;
                script.loaded = true;
                if ( !done && (!this.readyState ||
                        this.readyState === "loaded" || this.readyState === "complete") ) {
                    done = true;
                    script.onload = script.onreadystatechange = null;
                    debug("onload " + obj.src, obj);
                    flush(obj);
                }
            };
    
            script.loaded = false;
            script.src = obj.src;
            obj.depth++;
    
            // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
            // This arises when a base node is used (#2709 and #4378).
            head.insertBefore( script, head.firstChild );
            setTimeout(function() {
                if(!script.loaded) { throw new Error("SCRIPT NOT LOADED: " + script.src); }
            }, obj.timeout);
        }
    
        function logScript (obj, code, lang) {
            debug((code ?
                "Inline " + lang + ": " + code.replace("\n", " ").substr(0, 30) + "..." :
                "Inject " + obj.src), obj);
        }
    
        function separateScriptsFromHtml (htmlStr) {
            return split(htmlStr, splitScriptsRegex);
        }
    
        function split (str, regexp) {
            var match, prevIndex=0, tmp, result = [], i, l;
    
            if(support.splitWithCapturingParentheses) {
                tmp = str.split(regexp);
            } else {
                // Cross browser split technique from Steven Levithan
                // http://blog.stevenlevithan.com/archives/cross-browser-split
                tmp = [];
                while(match = regexp.exec(str)) {
                    if(match.index > prevIndex) {
                        result.push(str.slice(prevIndex, match.index));
                    }
    
                    if(match.length > 1 && match.index < str.length) {
                        Array.prototype.push.apply(tmp, match.slice(1));
                    }
    
                    prevIndex = regexp.lastIndex;
                }
    
                if(prevIndex < str.length) {
                    tmp.push(str.slice(prevIndex));
                }
    
            }
    
            for(i=0, l=tmp.length; i<l; i=i+1) {
                if(tmp[i]!=="") { result.push(tmp[i]); }
            }
    
            return result;
        }
    
        function stripNoScript (html) {
            return html.replace(/<noscript>[\s\S]*?<\/noscript>/ig, "");
        }
    
        function trim (str) {
            if(!str) { return str; }
            return str.replace(/^\s*|\s*$/gi, "");
        }
    
        function writeHtml (html, obj) {
            if( isScript(html) ) {
                var dummy = document.createElement("div");
                dummy.innerHTML = "dummy<div>" + html + "</div>"; // trick for IE
                var script = dummy.children[0].children[0];
                var lang = script.getAttribute("language") || "javascript";
                if(script.src) {
                    obj.src = script.src;
                    obj.charset = script.charset;
                    obj.language = lang;
                    obj.type = script.type;
                    loadScript(obj);
                } else {
                    var code = trim( script.text );
                    if(code) {
                        logScript( obj, code, lang);
                        globalEval( code, lang);
                    }
                    flush(obj);
                }
            } else {
                var container = getCachedElById(obj.domId);
                if(!container) {
                    throw new Error("crapLoader: Unable to inject html. Element with id '" + obj.domId + "' does not exist");
                }
                
                html = trim(html); // newline before <object> cause weird effects in IE
                if(html) {
                    container.innerHTML += html;
                }
                checkWriteBuffer(obj);
            }
        }
    
        function writeReplacement (str) {
            inputBuffer.push(str);
            debug("write: " + str);
        }
    
        function openReplacement () {
            // document.open() just returns the document when called from a blocking script:
            // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-open
            return document;
        }
    
        function closeReplacement () {
            // document.close() does nothing when called from a blocking script:
            // http://www.whatwg.org/specs/web-apps/current-work/#dom-document-close
        }
    
        publ = {
            hijack: function(options) {
                if(isHijacked) { return; }
                isHijacked = true;
                extend(globalOptions, options);
                if(globalOptions.parallel && !support.scriptOnloadTriggeredAccurately) {
                    globalOptions.parallel = false;
                    debug("Browsers onload is not reliable. Disabling parallel loading.");
                }
    
                document.write = document.writeln = writeReplacement;
                document.open = openReplacement;
                document.close = closeReplacement;
                document.getElementById = getElementByIdReplacement;
            },
    
            release: function() {
                if(!isHijacked) { return; }
                isHijacked = false;
                document.write = this.orgWrite;
                document.writeln = this.orgWriteLn;
                document.open = this.orgOpen;
                document.close = this.orgClose;
                document.getElementById = this.orgGetElementById;
                elementCache = {};
            },
    
            handle: function(options) {
                if(!isHijacked) {
                    debug("Not in hijacked mode. Auto-hijacking.");
                    this.hijack();
                }
                var defaultOptsCopy = extend({}, defaultOptions);
                var obj = extend(defaultOptsCopy, options);
                obj.depth = 0;
    
                if (!obj.domId) {
                    obj.domId = "craploader_" + new Date().getTime();
                    var span = document.createElement("span");
                    span.id = obj.domId;
                    document.body.appendChild(span);
                }
    
                if (options.func) {
                    runFunc(obj);
                    return;
                }
    
                if(globalOptions.parallel) {
                    setTimeout(function() {
                        loadScript(obj);
                    }, 1);
                } else {
                    queue.push(obj);
                    setTimeout(function() {
                        if(loading === 0) {
                            checkQueue();
                        }
                    }, 1);
                }
            },
    
            loadScript: function(src, domId, options) {
                if (typeof domId !== "string") {
                    options = domId;
                    domId = undefined;
                }
                this.handle(extend({
                    src:    src,
                    domId:  domId
                }, options));
            },
    
            runFunc: function(func, domId, options) {
                if (typeof domId !== "string") {
                    options = domId;
                    domId = undefined;
                }
                this.handle( extend({
                    domId:  domId,
                    func:     func
                }, options) );
            },
    
            orgGetElementById   : document.getElementById,
            orgWrite            : document.write,
            orgWriteLn          : document.writeln,
            orgOpen             : document.open,
            orgClose            : document.close,
            _olt                : 1,
            _oltCallback        : function() {
                support.scriptOnloadTriggeredAccurately = (publ._olt===2);
            }
        };
    
        return publ;
    }());
    demo.php
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
     <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <title> AD Loader </title>
      <style type="text/css">
      .banner1{margin:10px; border:1px solid #CCCCCC; width:728px; height:90px;}
      .banner2{margin:10px; border:1px solid #CCCCCC; width:300px; height:250px;}
      </style>
      <script type="text/javascript" src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
     </head>
    
     <body>
      <div class="banner1" id="ad_728x90"></div>
      <div class="banner2" id="ad_300x250"></div>
      <div class="banner2" id="ad_l2_300x250"></div>
       
      <?php
        function showAD($channel='', $step='', $async=''){
          include('ADLoader.class.php');
          $ad_config = include('ADConfig.php');
          ADLoader::setConfig($ad_config, 'ADLoader.js');
          return ADLoader::load($channel, $step, $async);
        }
    
        echo showAD('case_openx'); // 异步加载
        //echo showAD('case_url');   // url方式异步加载
        //echo showAD('case_sync_openx', 300, false); // 同步加载
      ?>
    
     </body>
    </html>

    adurl.php

    <?php
    $zoneid = isset($_GET['zoneid'])? intval($_GET['zoneid']) : 0;
    if($zoneid){
    ?>
    var zoneid = <?=$zoneid ?>;
    var m3_u = (location.protocol=='https:'?'https://ads.nmg.com.hk/www/delivery/ajs.php':'http://ads.nmg.com.hk/www/delivery/ajs.php');
    var m3_r = Math.floor(Math.random()*99999999999);
    if (!document.MAX_used) document.MAX_used = ',';
    document.write ("<scr"+"ipt type='text/javascript' src='"+m3_u);
    document.write ("?zoneid=" + zoneid);
    document.write ('&cb=' + m3_r);
    if (document.MAX_used != ',') document.write ("&exclude=" + document.MAX_used);
    document.write (document.charset ? '&charset='+document.charset : (document.characterSet ? '&charset='+document.characterSet : ''));
    document.write ("&loc=" + escape(window.location));
    if (document.referrer) document.write ("&referer=" + escape(document.referrer));
    if (document.context) document.write ("&context=" + escape(document.context));
    if (document.mmm_fo) document.write ("&mmm_fo=1");
    document.write ("'><\/scr"+"ipt>");
    <? } ?>
    源码下载地址:点击查看

    展开全文
  • PHP类的自动加载

    千次阅读 2018-10-17 17:35:58
    PHP类的自动加载__autoload、spl_autoload_register

    __autoload

    尝试加载未定义的类,PHP7.2弃用此函数。假定同一目录下有foo.php和test.php两个文件。

    PHP5.3以前__autoload函数抛出的异常不能被catch语句块捕获并会导致一个致命错误。PHP5.3起能够thrown自定义的异常,随后自定义异常类即可使用。__autoload函数可以递归的自动加载自定义异常类。

    //foo.php
    <?php
    class Foo{
    	function __construct(){
    		echo __CLASS__ ." in ". __FILE__ ."<br/>";
    	}
    }
    
    class Bar{
    	function __construct(){
    		echo __CLASS__ ." in ". __FILE__ ."<br/>";
    	}
    }
    interface ITest{
    	public function test();
    }
    ?>
    //test.php
    <?php
    function __autoload($className){
        $filename = "./". $className .".php";
        include_once($filename);
    }
    $foo=new Foo();
    $bar=new Bar();
    ?>

    要注意的是如果test.php中最后两行顺序颠倒的话会产生致命错误,因为Bar定义在foo.php中,无法通过__autoload加载。

    spl_autoload_register

    将函数注册到SPL的__autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。spl_autoload_register创建了一个autoload函数的队列,按定义时的顺序逐个执行。而__autoload函数只可以定义一次。如果程序中已经实现了__autoload函数,必须显式的将其注册到__autoload函数队列中。因为spl_autoload_register函数会将Zend Engine中的__autoload函数取代为spl_autoload或spl_autoload_call。

    最多接受三个参数,第一个参数为要注册的自动装载函数,默认为spl_autoload();第二个参数为要注册的自动装载函数无法成功注册时,spl_autoload_register是否抛出异常,默认为true;第三个参数为PHP5.3新增的标识参数,表示要注册的自动装载函数是否添加到函数队列之首,默认为false。注册成功返回true,失败返回false。

    PHP5.3起,还支持匿名函数和命名空间。

    //test2.php
    <?php
    function my_autoload($className){
    	$filename = "./". $className .".php";
        include_once($filename);
    }
    spl_autoload_register('my_autoload');
    
    //PHP5.3支持匿名函数
    spl_autoload_register(function($className){
    	$filename = "./". $className .".php";
        include_once($filename);
    },true,true);
    
    
    $foo=new Foo();
    $bar=new Bar();
    
    class MyTest implements ITest{
    	public function test(){
    		echo "mytest</br>";
    	}
    }
    $t=new MyTest();
    $t->test();
    ?>
    //test3.php
    <?php
    namespace ns;
    
    function my_autoload($className){
    	$filename = "./". $className .".php";
        include_once($filename);
    }
    spl_autoload_register(__NAMESPACE__ .'\my_autoload');
    
    $foo=new \Foo();
    $bar=new \Bar();
    
    ?>

     

    展开全文
  • Java类加载机制与Tomcat类加载器架构

    万次阅读 热门讨论 2017-02-26 10:58:11
    Java类加载机制 类加载器 虚拟机设计团队把类加载阶段中的“通过一个的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的。实现这个动作的...

    Java类加载机制

    类加载器

            虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为类加载器

          类加载器可以说是Java语言的一项创新,也是Java语言流行的重要原因之一,它最初是为了满足Java Applet的需求而开发出来的。虽然目前Java Applet技术基本上已经“死掉”,但类加载器却在类层次划分、OSGi、热部署、代码加密等领域大放异彩,成为了Java技术体系中一块重要的基石,可谓是失之桑榆,收之东隅。

          类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等

    双亲委派模型

          从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。

          从Java开发人员的角度来看, 类加载器还可以划分得更细致一些, 绝大部分Java程序都会使用到以下3种系统提供的类加载器。

    1)启动类加载器(Bootstrap ClassLoader):前面已经介绍过,这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用

    2)扩展类加载器(Extension ClassLoader):这个加载器由sun.misc.Launcher.ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器

    3)应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher.AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Class Path)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器

          我们的应用程序都是由这3种类加载器互相配合进行加载的,如果有必要,还可以加入自己定义的类加载器。这些类加载器之间的关系一般如下图所示。

     

          图中展示的类加载器之间的这种层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。类加载器的双亲委派模型在JDK 1.2期间被引入并被广泛应用于之后几乎所有的Java程序中,但它并不是一个强制性的约束模型,而是Java设计者推荐给开发者的一种类加载器实现方式。

          双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载

          使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有使用双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个称为java.lang.Object的类,并放在程序的Class Path中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。如果读者有兴趣的话,可以尝试去编写一个与rt.jar类库中已有类重名的Java类,将会发现可以正常编译,但永远无法被加载运行。

          双亲委派模型对于保证Java程序的稳定运作很重要,但它的实现却非常简单,实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法之中,如以下代码所示,逻辑清晰易懂:

    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    	//首先检查请求的类是否已经被加载过了
    	Class c = findLoadedClass(name);
    	if (c== null) {
    		try {
    			if(parent !=null) {
    				c = parent.loadClass(name,false);
    			} else {
    				c = findBootstrapClassOrNull(name);
    			}
    		} catch (ClassNotFoundException e) {
    		//如果父类加载器抛出ClassNotFoundException,说明父类加载器无法完成加载请求
    		}
    		if(c == null) {
    			//在父类加载器无法加载的时候,再调用本身的findClass方法来进行类加载
    			c = findClass(name);
    		}
    	} 
    	if (resolve) {
    		resolveClass(c);
    	}
    	return c;
    }
    

           先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。双亲委派的具体逻辑就实现在这个loadClass()方法之中,JDK 1.2之后已不提倡用户再去覆盖loadClass()方法,而应当把自己的类加载逻辑写到findClass()方法中,在loadClass()方法的逻辑里如果父类加载失败,则会调用自己的findClass()方法来完成加载,这样就可以保证新写出来的类加载器是符合双亲委派规则的。

    自定义一个java.lang.Object类

            尝试在工程中创建自己的java.lang.Object类

    package java.lang;
     
    /**
     * 自己创建的java.lang.Object
     */
    public class Object {
        static {
            System.out.println("hello");
        }
        public static void main(String[] args) {
            Object o = new Object();
        }
    }

    运行main方法,发现打印报错:

          我们知道,类加载的过程会遵循双亲委派原则,当一个类首次被加载时,会依次向parent类加载器委托, 直到最顶层的BootstrapClassLoader。java.lang.Object属于系统类,会由BootstrapClassLoader优先加载,最终加载的还是系统原生的java.lang.Object类,因此会报找不到main方法的错误。

           有了这一前提,我们需要通过自定义类加载器绕过双亲委派机制,实现自定义类的加载。MyClassLoader为自定义类加载器,它继承于ClassLoader,覆写了loadClass方法,实现自定义加载。

    public class MyClassLoader extends ClassLoader {
    
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            if (name == null || "".equals(name)) {
                throw new ClassNotFoundException();
            }
            InputStream is = null;
            try {
                String className = "/" + name.replace('.', '/') + ".class";
                System.out.println(className);
                // 在classpath路径下加载java/lang/Object.class文件
                is = getClass().getResourceAsStream(className);
                System.out.println(is);
                if (is == null) {
                    throw new ClassNotFoundException();
                }
                byte[] bytes = new byte[is.available()];
                is.read(bytes);
                // 调用父类classLoader的defineClass方法
                // 将字节数组转换为Class实例
                return defineClass(name, bytes, 0, bytes.length);
            } catch (Exception e) {
                e.printStackTrace();
                throw new ClassNotFoundException();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
     
        public static void main(String[] args) {
            MyClassLoader myClassLoader = new MyClassLoader();
            try {
                Class<?> clazz = Class.forName("java.lang.Object", true, myClassLoader);
                System.out.println("自定义类加载器:" + clazz.newInstance().getClass().getClassLoader());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    运行main函数:

    提示:禁止使用包名:“java.lang”。跟进defineClass的源码:

    protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
        return defineClass(name, b, off, len, null);
    }
    
    protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain) throws ClassFormatError {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }
    
    private ProtectionDomain preDefineClass(String name,ProtectionDomain pd) {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);
        // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
        // relies on the fact that spoofing is impossible if a class has a name
        // of the form "java.*"
        if ((name != null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }
        if (name != null) checkCerts(name, pd.getCodeSource());
        return pd;
    }

          在preDefineClass()这个方法的逻辑中是禁止包以“java.”开头命名的,并且该方法为final方法,禁止覆写,说明了我们无法通过自定义类加载器加载以“java.”开头的类。将自定义java.lang.Object修改成myjava.lang.Object后,直接运行。

    package myjava.lang;
    
    /**
     * @description 自定义的Object类,路径不能以java.开始
     */
    public class Object {
    
        static {
            System.out.println(Object.class.getName());
        }
    
        public static void main(String[] args) {
            Object object = new Object();
    
        }
    }
    

    结果如下:

    打破双亲委派模型

          上文提到过双亲委派模型并不是一个强制性的约束模型,而是Java设计者推荐给开发者的类加载器实现方式。在Java的世界中大部分的类加载器都遵循这个模型,但也有例外。

          SPI的全名为Service Provider Interface,主要是应用于厂商自定义组件或插件中,在java.util.ServiceLoader的文档里有比较详细的介绍。简单的总结下java SPI机制的思想:我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块、xml解析模块、jdbc模块等方案。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。 Java SPI就是提供这样的一个机制:为某个接口寻找服务实现的机制。 有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。

          Java 提供了很多服务SPI,允许第三方为这些接口提供实现。这些SPI的接口由Java 核心库来提供,而这些 SPI 的实现则是由各供应商来完成。终端只需要将所需的实现作为Java应用所依赖的jar包包含进类路径(CLASSPATH)就可以了。问题在于SPI接口中的代码经常需要加载具体的实现类:SPI的接口是Java核心库的一部分,是由启动类加载器来加载的;而SPI的实现类是由系统类加载器来加载的。启动类加载器是无法找到 SPI的实现类的(因为它只加载 Java 的核心库),按照双亲委派模型,启动类加载器无法委派系统类加载器去加载类。也就是说,类加载器的双亲委派模式无法解决这个问题
     

          双亲委派模型的一次“被破坏”是由这个模型自身的缺陷所导致的,双亲委派很好地解决了各个类加载器的基础类的统一问题(越基础的类由越上层的加载器进行加载)并防止类的重复加载基础类之所以称为基础,是因为它们总是作为被用户代码调用的API,但世事往往没有绝对的完美,如果基础类又要调用回用户的代码,那该怎么办?这并非是不可能的事情,一个典型的例子便是JNDI服务,JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK 1.3时放进去的rt.jar),但JNDI的目的就是对资源进行集中管理和查找,它需要调用由独立厂商实现并部署在应用程序的Class Path下的JNDI接口实现(SPI,Service Provider Interface)代码,但启动类加载器不可能“认识”这些代码,因为启动类加载器的搜索范围中找不到用户应用程序类,那该怎么办?

          为了解决这个问题,Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个,如果在应用程序的全局范围内都没有设置过的话,那这个线程上下文加载器默认就是应用程序类加载器(Application ClassLoader)。

          有了线程上下文类加载器,就可以做一些“舞弊”的事情了,有了有线程上下文类加载器,JNDI服务使用这个线程上下文类加载器去加载所需要的SPI代码,也就是父类加载器请求子类加载器去完成类加载动作,去加载所需要的SPI代码,这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,实际上已经违背了双亲委派模型的一般性原则,但这也是无可奈何的事情。Java中所有涉及SPI的加载动作基本上都采用这种方式,例如JNDI、JDBC、JCE、JAXB和JBI等。

    SPI打破双亲委派的示例

           Java SPI的具体约定为:当服务的提供者提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件,该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要在代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。JDBC SPI Mysql的实现如下所示:

    来看下Mysql的驱动是如何被加载的,代码如下

    // 1.加载数据访问驱动
    Class.forName("com.mysql.jdbc.Driver");
     //2.连接到数据库上去
    Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=GBK", "root", "admin");


           Class.forName()触发了Mysql驱动的加载,我们看下com.mysql.jdbc.Driver这个类,它实现了java.sql.Driver接口,加载该类时肯定先执行静态代码块,向DriverManager中注册了一个mysql的Driver实现。

    package com.mysql.jdbc;
    
    import java.sql.DriverManager;
    import java.sql.SQLException;
    public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    
        public Driver() throws SQLException {
    
        }
    
        static {
            try {
                DriverManager.registerDriver(new Driver());
            } catch (SQLException var1) {
                throw new RuntimeException("Can't register driver!");
            }
        }
    }

           现在问题来了,Class.forName()加载类用的是调用者的Classloader,这个调用者DriverManager是在rt.jar中的,ClassLoader是启动类加载器,而com.mysql.jdbc.Driver肯定不在<JAVA_HOME>/lib下,所以肯定是无法加载com.mysql.jdbc.Driver这个类的。这就是双亲委派模型的局限性了,父级加载器无法加载子级类加载器路径中的类。那么,这个问题如何解决呢?按照之前的分析,com.mysql.jdbc.Driver类只有应用类加载器能加载,那么我们只要在DriverManager类中有方法获取应用程序类加载器,然后通过它去加载就可以了。来看java.sql.DriverManager类的loadInitialDrivers()方法,关键源码如下:

    private static void loadInitialDrivers() {
       
       	//...省略代码
    
        // If the driver is packaged as a Service Provider, load it.
        // Get all the drivers through the classloader
        // exposed as a java.sql.Driver.class service.
        // ServiceLoader.load() replaces the sun.misc.Providers()
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
    
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                // Do nothing
                }
                return null;
            }
        });
    
        //...省略代码
    }

         看英文注释就知道ServiceLoader.load()方法将各个厂商实现的驱动类都加载进来,ServiceLoader.load()方法的源码如下,看到这里就很明显了,用的就是线程上下文加载器。这样我们就可以成功的在rt.jar包中的DriverManager中成功的加载了放在第三方应用程序包中的类了。很明显,mysql驱动采用的这种SPI服务确确实实是破坏了双亲委派模型的,毕竟做到了父级类加载器加载了子级路径中的类。

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    模块化热部署打破双亲委派

          双亲委派模型的另一次“被破坏”是由于用户对程序动态性的追求而导致的,这里所说的“动态性”指的是当前一些非常“热门”的名词:代码热替换(HotSwap)、模块热部署(HotDeployment)等,说白了就是希望应用程序能像我们的计算机外设那样,接上鼠标、U盘,不用重启机器就能立即使用,鼠标有问题或要升级就换个鼠标,不用停机也不用重启。对于个人计算机来说,重启一次其实没有什么大不了的,但对于一些生产系统来说,关机重启一次可能就要被列为生产事故,这种情况下热部署就对软件开发者,尤其是企业级软件开发者具有很大的吸引力。Sun公司所提出的JSR-294、JSR-277规范在与JCP组织的模块化规范之争中落败给JSR-291(即OSGi R4.2),虽然Sun不甘失去Java模块化的主导权,独立在发展Jigsaw项目,但目前OSGi已经成为了业界“事实上”的Java模块化标准,而OSGi实现模块化热部署的关键则是它自定义的类加载器机制的实现。每一个程序模块(OSGi中称为Bundle)都有一个自己的类加载器,当需要更换一个Bundle时,就把Bundle连同类加载器一起换掉以实现代码的热替换。

          在OSGi环境下,类加载器不再是双亲委派模型中的树状结构,而是进一步发展为更加复杂的网状结构,当收到类加载请求时,OSGi将按照下面的顺序进行类搜索:

    1)将以java.*开头的类委派给父类加载器加载。

    2)否则,将委派列表名单内的类委派给父类加载器加载。

    3)否则,将Import列表中的类委派给Export这个类的Bundle的类加载器加载。

    4)否则,查找当前Bundle的Class Path,使用自己的类加载器加载。

    5)否则,查找类是否在自己的Fragment Bundle中,如果在,则委派给Fragment Bundle的类加载器加载。

    6)否则,查找Dynamic Import列表的Bundle,委派给对应Bundle的类加载器加载。

    7)否则,类查找失败。

          上面的查找顺序中只有开头两点仍然符合双亲委派规则,其余的类查找都是在平级的类加载器中进行的。

          只要有足够意义和理由,突破已有的原则就可认为是一种创新。正如OSGi中的类加载器并不符合传统的双亲委派的类加载器,并且业界对其为了实现热部署而带来的额外的高复杂度还存在不少争议,但在Java程序员中基本有一个共识:OSGi中对类加载器的使用是很值得学习的,弄懂了OSGi的实现,就可以算是掌握了类加载器的精髓。

    Tomcat的类加载器架构

          主流的Java Web服务器(也就是Web容器),如Tomcat、Jetty、WebLogic、WebSphere或其他笔者没有列举的服务器,都实现了自己定义的类加载器(一般都不止一个)。因为一个功能健全的Web容器,要解决如下几个问题:

          1)部署在同一个Web容器上两个Web应用程序使用的Java类库可以实现相互隔离。这是最基本的需求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相独立使用。

          2)部署在同一个Web容器上两个Web应用程序所使用的Java类库可以互相共享。这个需求也很常见,例如,用户可能有10个使用Spring组织的应用程序部署在同一台服务器上,如果把10份Spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费——这主要倒不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到Web容器的内存,如果类库不能共享,虚拟机的方法区就会很容易出现过度膨胀的风险

          3)Web容器需要尽可能地保证自身的安全不受部署的Web应用程序影响。目前,有许多主流的Java Web容器自身也是使用Java语言来实现的。因此,Web容器本身也有类库依赖的问题,一般来说,基于安全考虑,容器所使用的类库应该与应用程序的类库互相独立。

          4)支持JSP应用的Web容器,大多数都需要支持HotSwap功能。我们知道,JSP文件最终要编译成Java Class才能由虚拟机执行,但JSP文件由于其纯文本存储的特性,运行时修改的概率远远大于第三方类库或程序自身的Class文件。而且ASP、PHP和JSP这些网页应用也把修改后无须重启作为一个很大的“优势”来看待,因此“主流”的Web容器都会支持JSP生成类的热替换,当然也有“非主流”的,如运行在生产模式(Production Mode)下的WebLogic服务器默认就不会处理JSP文件的变化。

          由于存在上述问题,在部署Web应用时,单独的一个Class Path就无法满足需求了,所以各种Web容都“不约而同”地提供了好几个Class Path路径供用户存放第三方类库,这些路径一般都以“lib”或“classes”命名。被放置到不同路径中的类库,具备不同的访问范围和服务对象,通常,每一个目录都会有一个相应的自定义类加载器去加载放置在里面的Java类库。现在,就以Tomcat容器为例,看一看Tomcat具体是如何规划用户类库结构和类加载器的。

          在Tomcat目录结构中,有3组目录(“/common/*”、“/server/*”和“/shared/*”)可以存放Java类库,另外还可以加上Web应用程序自身的目录“/WEB-INF/*”,一共4组,把Java类库放置在这些目录中的含义分别如下:

          ①放置在/common目录中:类库可被Tomcat和所有的Web应用程序共同使用

          ②放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见。

          ③放置在/shared目录中:类库可被所有的Web应用程序共同使用,但对Tomcat自己不可见。

          ④放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见。

          为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,其关系如下图所示。

     
     

          上图中灰色背景的3个类加载器是JDK默认提供的类加载器,这3个加载器的作用已经介绍过了。而CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载/common/*、/server/*、/shared/*和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例每一个Web应用程序对应一个WebApp类加载器每一个JSP文件对应一个Jsp类加载器

           从图中的委派关系中可以看出,CommonClassLoader能加载的类都可以被Catalina ClassLoader和SharedClassLoader使用,而CatalinaClassLoader和Shared ClassLoader自己能加载的类则与对方相互隔离。WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。

          对于Tomcat的6.x版本,只有指定了tomcat/conf/catalina.properties配置文件的server.loader和share.loader项后才会真正建立Catalina ClassLoader和Shared ClassLoader的实例,否则在用到这两个类加载器的地方都会用Common ClassLoader的实例代替,而默认的配置文件中没有设置这两个loader项,所以Tomcat 6.x顺理成章地把/common、/server和/shared三个目录默认合并到一起变成一个/lib目录,这个目录里的类库相当于以前/common目录中类库的作用。这是Tomcat设计团队为了简化大多数的部署场景所做的一项改进,如果默认设置不能满足需要,用户可以通过修改配置文件指定server.loader和share.loader的方式重新启用Tomcat 5.x的加载器架构。

          Tomcat加载器的实现清晰易懂,并且采用了官方推荐的“正统”的使用类加载器的方式。如果读者阅读完上面的案例后,能完全理解Tomcat设计团队这样布置加载器架构的用意,那说明已经大致掌握了类加载器“主流”的使用方式,那么笔者不妨再提一个问题让读者思考一下:前面曾经提到过一个场景,如果有10个Web应用程序都是用Spring来进行组织和管理的话,可以把Spring放到Common或Shared目录下让这些程序共享。Spring要对用户程序的类进行管理,自然要能访问到用户程序的类,而用户的程序显然是放在/WebApp/WEB-INF目录中的,那么被CommonClassLoader或SharedClassLoader加载的Spring如何访问并不在其加载范围内的用户程序呢?如果研究过虚拟机类加载器机制中的双亲委派模型,相信读者可以很容易地回答这个问题。

          分析:如果按主流的双亲委派机制,显然无法做到让父类加载器加载的类访问子类加载器加载的类,上面在类加载器一节中提到过通过线程上下文方式传播类加载器。

          答案是使用线程上下文类加载器来实现的,使用线程上下文加载器,可以让父类加载器请求子类加载器去完成类加载的动作。看spring源码发现,spring加载类所用的Classloader是通过Thread.currentThread().getContextClassLoader()来获取的,而当线程创建时会默认setContextClassLoader(AppClassLoader),即线程上下文类加载器被设置为AppClassLoader,spring中始终可以获取到这个AppClassLoader(在Tomcat里就是WebAppClassLoader)子类加载器来加载bean,以后任何一个线程都可以通过getContextClassLoader()获取到WebAppClassLoader来getbean了。

          本篇博文内容取材自《深入理解Java虚拟机:JVM高级特性与最佳实践》

     

    展开全文
  • php自动加载类 Test

    千次阅读 2016-06-23 15:23:47
    php自动加载类 Test
    //自动加载1
    /*function __autoload($className){
        //echo $className;
        include_once './'.$className.".php";
    }*/
    
    //自动加载2
    
    spl_autoload_register('autoClass');
    
    function autoClass($className){
        include_once './'.$className.".php";
    }
    
    $obj = new Test();
    $obj -> index();
    展开全文
  • php命名空间自动加载

    千次阅读 2015-04-10 18:57:16
    php 5.3以后我们可以使用命名空间来自动加载导入文件,在很多框架中已经实现了的自动加载,只需要在全局非命名空间代码中use命名空间就可以直接实例化。 <?php use Package1\Test1; spl_autoload_register...
  • PHP类延迟加载机制原理

    千次阅读 2015-11-03 21:28:39
    PHP加载一个,必须使用require 或者 include把文件包含进来,才可以实例化,我们比较传统的方法在使用的时候就是一次性的把所需要的文件全部进行require,PHP5之前是这么干的,PHP5之后引入了一个函数spl_...
  • php类文件的自动加载机制

    千次阅读 2014-09-28 11:07:42
    通常情况,在使用php面向对象开发的过程中,一个的定义都是一个文件,这样子下来,当之间需要...在php5之后已经有了的自动加载机制,可以定义__autoload函数,在使用到某个未定义的,执行php会出错,但是
  • 的自动加载函数是个魔术方法,这个函数不需要调用是自动会加载调用的,PHP体系内部提供的方法,__ aotuload(),使用形式为: &lt;?php function __autoload($className){ require_once "./".$...
  • PHP自动加载机制

    万次阅读 2012-04-16 10:21:57
    PHP自动加载机制  在PHP开发过程中,如果希望从外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来。这个在小规模开发的时候,没什么大问题。但在大型的开发项目中,这么...
  • Thinkphp5类加载机制

    千次阅读 2017-03-05 15:48:56
    我一直对于thinkphp5的加载运行的时候做了什么,他是怎么自动加载类的,是和composer一样的吗—能否实现运行时再加载类,于是做了一下测试,追踪了整个加载流程。   以文件为单位进行讲解: 入口文件i
  • 简单实现PHP路由和自动加载

    千次阅读 2017-05-27 16:38:10
    项目目录如下 入口文件index.php  <?php define('WEBROOT', 'C:/Users/Administrator/Documents/NetBeansProjects/test'); require_once(WEBROOT.'/core/environment.php');...自动加载文件environment.php
  • composer类加载方式

    千次阅读 2017-02-18 15:45:30
    Composer作为管理包依赖的工具,本身也提供了对于类加载的支持。上面的博文有了对于composer类加载的四种方式的介绍。 今天简单看了一下其加载方式的实现。简单介绍之前,希望大家自己先使用composer构建一次...
  • php自动加载

    千次阅读 2011-11-08 18:37:43
    很多开发者写面向对象的应用程序时对每个的定义建立一个 PHP 源文件。一个很大的烦恼是不得不在每个脚本(每...通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的。 http://www.php.net/manual
  • 这周利用公司的PHP框架,开发,但是自动加载后,总是找不到该。本文将对记录如何尝试解决这个问题和最后的一些总结思考。 问题描述: 在ActivationService.php定义ActiveService,在Autoload.php注册回...
  • Rico's Blogs 潜心修炼,成为一个更好的人。 ...赠书 | 异步2周年,技术图书免费选 程序员8月书讯 项目管理+代码托管+文档协作,开发更流畅 ...深入理解Java类加载器(一):...Java类加载原理双亲委派模型启动类加载
  • ClassLoader targetClassLoader = null;// 外部参数 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(targetClassLoader...
  • PHP中的use、命名空间、引入类文件、自动加载类的理解 <div id="cnblogs_post_body" class="cnblogs-markdown"><p>use只是使用了命名空间, 但是要想调用类,必须要加载类文件,或者自动加载。即便是引入了...
  • 本文实例讲述了PHP MVC框架中的自动加载机制。分享给大家供大家参考,具体如下: 原文 实现的自动加载主要使用到了set_include_path和spl_autoload_register函数。 set_include_path用于提前设置好可能会...
  • php自动加载以及php简单路由功能

    千次阅读 2020-08-14 11:59:24
    项目中需要在workernman基础上自己完成基础框架,主要是将用户请求的api路由到具体控制器方法,同时用到的需要自动加载, 原理: 路由:url形如admin/admin/login ,将url分割,获取到具体的,方法,调用这个...
  • 今天写一个demo,遇到一个问题,平时框架用多了,下意识的就以为use就能引入,导致一直跑不成功,后来才意识到use与引用并不是一回事。...的引用创建一个文件ClassA.class.phpnamespace AreaA; class Clas
  • laravel 自动加载类

    千次阅读 2018-03-19 09:32:06
    1、需求因项目需要,在某个地方需要使用自定义的类,Strtopy.php 使用laravel自动加载类机制2、过程Strtopy.php所在目录 app/Lib/Strtopy.php Lib目录是自己建的目录在composer.json的autoload内的classmap项新增类...
  • 这个函数是一个自动加载类函数,啥事自动加载函数,顾名思义 ,那就是自己就会加载类的函数(原谅我废话了)我们先看下面的代码:<?php function __autoload($classname) { echo "helloworld"; } $obj = new Test1...
  • 浅谈PHP中自动加载类库机制

    千次阅读 2014-05-10 13:10:18
    php开发过程中
  • PHP中使用composer加载自定义 的

    千次阅读 2018-06-26 11:14:19
    最近打算用composer来自动加载自己的, 实践过程中遇到一些小难点, 解决方案记录如下:1.首先需要创建一个 存放 文件的目录 , 不建议放在vendor下, 2.修改composer.json文件, 加入/修改 "autoload":{ &...
  • 说说PHP5的自动加载类机制

    千次阅读 2008-09-15 19:41:00
    为了避免每次实例化时需要包含类文件的麻烦,PHP5提供了自动加载类的新特性:__autoload()方法。简单举例说明__autoload()的使用:function __autoload($class){ require "$class".".php";}$news = new news();$...
  • php加载外部文件的3.5个方法

    千次阅读 2015-06-26 16:16:42
    <?php /* 第一种方法 require:缺点就是如果加载文件过多,需要多个require */ // require('A.CLASS.PHP');... PHP5在new 一个没有存在的,会自动调用这个__autoload函数,____autoload只是去include_path寻找
  • php自动加载指定目录下的文件

    千次阅读 2018-10-10 12:54:40
    From: ... 网上找到的,非常有用,文件名: autoload.php &lt;?php /** * Created by PhpStorm. * User: zcm * Mail: zhouciming@163.com * Date: 2018/10/10 上午10:20 */ if(!...
  • PHP自动加载某一文件夹下的文件

    千次阅读 2018-08-22 16:53:35
    //自动加载当前文件夹外同级目录 spl_autoload_register(function ($class_name) {  require_once dirname(dirname(__FILE__))."/smsclass/".$class_name.".php";  });  $server = '...
  • 经过一系列debug,发现php文件的静态引入造成了bug的发生,而使用动态加载类文件将会有效的解决这个bug,具体代码如下:<?php define("TOP_AUTOLOADER_PATH", dirname(__FILE__));class Autoloader{ /** * 类库自动
  • php搭建mvc框架二(的自动加载

    万次阅读 2016-12-26 09:22:03
    首先呢这是在上一篇的基础上继续写的!! 目录: 自动加载类: ...spl_autoload_register() ...下面的就是在上一篇的代码基础上,写的自动加载类的代码展示。 <?php /* * 入口文件 * 定义常量 * 加载函数 *

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 246,315
精华内容 98,526
关键字:

php类加载