<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    走自己的路

    路漫漫其修遠(yuǎn)兮,吾將上下而求索

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      50 隨筆 :: 4 文章 :: 118 評(píng)論 :: 0 Trackbacks

    應(yīng)用項(xiàng)目大致的體系結(jié)構(gòu):

        
     

     該異常處理框架滿足的要求:

     

    • 完整的異常組織結(jié)構(gòu)
    • 異常的統(tǒng)一處理
    • 可配置,受管式,方便使用

     

    完整的異常組織結(jié)構(gòu):

    • 用戶可以方便的定義自己的異常,但所有UncheckedException需要繼承BaseAppRuntimeException,所有的checked Exception可以繼承BaseAppException,或者需要拋出且不需要check時(shí)用WrapperredAppException封裝后拋出
    • 合理地使用checked異常
    • Exception有唯一的error code,這樣用戶報(bào)告異常后,可以根據(jù)異常號(hào)找到相應(yīng)Exception,把exception直接顯示給用戶也沒有太大的意義,如何紀(jì)錄exception那就是下文講到的ExceptionHandler的職責(zé)了。
    • 如果是第三方包括jdk中的異常,需要封裝成BaseAppException或者BaseAppRuntimeException后拋出

                                         

       

     

    統(tǒng)一的異常處理

    異常統(tǒng)一在框架中進(jìn)行處理,不需要在上層應(yīng)用的代碼中去處理拋出的異常。為了盡量捕捉到所有的異常,將異常處理放在了ActionBroker中,這樣凡是action以后拋出的異常都可以捕捉到,因?yàn)?/span>webservice只是簡單的調(diào)用action類的方法,一般不會(huì)出現(xiàn)異常。當(dāng)我們捕捉到異常后,需要進(jìn)行異常處理,定義了ExceptionHandler接口,用接口抽象出異常處理類的具體實(shí)現(xiàn)。

     


                            
     

    USFContextFactory: 創(chuàng)建ExceptionContext的工廠

     1package com.ldd600.exception.context;
     2
     3public class CoreContextFactory {
     4    private static CoreContextFactory instance;
     5
     6    private volatile ExceptionContext exceptionContext;
     7
     8    private Object exceptionContextLock = new Object();
     9
    10    private CoreContextFactory() {
    11
    12    }

    13
    14    public static synchronized CoreContextFactory getInstance() {
    15        if (null == instance) {
    16            instance = new CoreContextFactory();
    17        }

    18        return instance;
    19    }

    20
    21    public ExceptionContext getExceptionContext() {
    22        ExceptionContext tempExpContext = exceptionContext;
    23        if (tempExpContext == null
    24            synchronized (exceptionContextLock) {
    25                tempExpContext = exceptionContext;
    26                if (tempExpContext == null)
    27                    exceptionContext = tempExpContext = new ExceptionContext();
    28            }

    29        }

    30        return tempExpContext;
    31    }

    32}

    33



       

    ExceptionContext: 存放全局的exception信息

     

      1package com.ldd600.exception.context;
      2
      3import java.util.ArrayList;
      4import java.util.Collection;
      5import java.util.Collections;
      6import java.util.HashMap;
      7import java.util.List;
      8import java.util.Map;
      9import java.util.Set;
     10
     11import org.springframework.util.Assert;
     12
     13import com.ldd600.exception.base.BaseAppRuntimeException;
     14import com.ldd600.exception.base.ConfigException;
     15import com.ldd600.exception.base.handler.ExceptionHandler;
     16import com.ldd600.exception.config.ExceptionDefinition;
     17
     18public class ExceptionContext {
     19    private Map<Class<?>, ExceptionDefinition> exceptionMap;
     20
     21    private Map<String, ExceptionHandler> handlers = new HashMap<String, ExceptionHandler>();
     22
     23    ExceptionContext() {
     24        exceptionMap = new HashMap<Class<?>, ExceptionDefinition>();
     25    }

     26
     27    public boolean containsException(Class<?> expClazz) {
     28        return (exceptionMap.containsKey(expClazz));
     29    }

     30    
     31    public void addExceptionHander(Class<?> expClazz, Class<? extends ExceptionHandler> handlerClazz) {
     32        try {
     33            ExceptionDefinition definition = getRealExceptionDefinition(expClazz);
     34            if (null == definition) {
     35                throw new IllegalArgumentException(expClazz.getName() + "not in the context, please configure or add it to the context first!!");
     36            }
     
     37            ExceptionHandler handler = handlers.get(handlerClazz.getName());
     38            if (null == handler) {
     39                handler = handlerClazz.newInstance();
     40                handlers.put(handlerClazz.getName(), handler);
     41            }

     42            
     43            definition.getHandlerNames().add(handlerClazz.getName());
     44        }
     catch (Exception ex) {
     45            throw new ConfigException("Add exception handler to context failure!", ex);
     46        }

     47    }

     48    
     49    public void addExceptionHandler(Class<?> expClazz, String errorCode, Class<? extends ExceptionHandler> handlerClazz) {
     50        Assert.hasLength(errorCode, expClazz + " errorCode must not be null or empty string!");
     51        ExceptionDefinition definition = getRealExceptionDefinition(expClazz);
     52        if(null == definition) {
     53            definition = new ExceptionDefinition(errorCode);
     54            exceptionMap.put(expClazz, definition);
     55        }

     56        addExceptionHander(expClazz, handlerClazz);
     57    }

     58    
     59    
     60    
     61    public void addExceptionHandlers(Class<?> expClazz, Class<? extends ExceptionHandler> handlerClazzes) {
     62        for(Class<? extends ExceptionHandler> handlerClazz : handlerClazzes) {
     63            addExceptionHander(expClazz, handlerClazz);
     64        }

     65    }

     66
     67    public void removeExceptionHandler(Class<?> expClazz, Class<? extends ExceptionHandler> handlerClazz) {
     68        Assert.isTrue(containsException(expClazz));
     69        String handlerName = handlerClazz.getName();
     70        getExceptionDefinition(expClazz).getHandlerNames().remove(handlerName);
     71        Collection<ExceptionDefinition> definitons = exceptionMap.values();
     72        boolean isClearHandler = true;
     73        for (ExceptionDefinition expDefinition : definitons) {
     74            if (expDefinition.getHandlerNames().contains(handlerName)) {
     75                isClearHandler = false;
     76                break;
     77            }

     78        }

     79
     80        if (isClearHandler) {
     81            handlers.remove(handlers.get(handlerName));
     82        }

     83    }

     84
     85    public void setExceptionDefinition(Class<?> expClazz, ExceptionDefinition definition) {
     86        exceptionMap.put(expClazz, definition);
     87    }

     88
     89    public ExceptionDefinition getExceptionDefinition(Class<?> expClazz) {
     90        if (containsException(expClazz)) {
     91            return exceptionMap.get(expClazz);  
     92        }
     else if (BaseAppRuntimeException.class.isAssignableFrom(expClazz.getSuperclass())) {
     93            return getExceptionDefinition(expClazz.getSuperclass());
     94        }
     else {
     95            return null;
     96        }

     97    }

     98    
     99    public ExceptionDefinition getRealExceptionDefinition(Class<?> expClazz) {
    100        return exceptionMap.get(expClazz);
    101    }

    102
    103    public List<ExceptionHandler> getExceptionHandlers(Class<?> expClazz){
    104        ExceptionDefinition definition = getExceptionDefinition(expClazz);
    105        if (null != definition) {
    106            Set<String> handlerNames = definition.getHandlerNames();
    107            List<ExceptionHandler> handlerList = new ArrayList<ExceptionHandler>(handlerNames.size());
    108            for (String handlerName : handlerNames) {
    109                ExceptionHandler handler = handlers.get(handlerName);
    110                handlerList.add(handler);
    111            }

    112            List<ExceptionHandler> resultHandlerList = new ArrayList<ExceptionHandler>(handlerList);
    113            return resultHandlerList;
    114        }
     else {
    115            return Collections.<ExceptionHandler> emptyList();
    116        }

    117    }

    118    
    119    public String getErrorCode(Class<?> expClazz){
    120        ExceptionDefinition definition = getExceptionDefinition(expClazz);
    121        if (null != definition) {
    122            return definition.getErrorCode();
    123        }
     else {
    124            return "";
    125        }

    126    }

    127    
    128    
    129}

    130
     

    ExceptionDefinition: Exception信息單元

     

     1package com.ldd600.exception.config;
     2
     3import java.util.LinkedHashSet;
     4import java.util.Set;
     5
     6public class ExceptionDefinition {
     7    private String errorCode;
     8
     9    private Set<String> handlerNames = new LinkedHashSet<String> ();
    10
    11    ExceptionDefinition() {
    12        
    13    }

    14    
    15    public ExceptionDefinition(String errorCode) {
    16        this.errorCode = errorCode;
    17    }

    18    
    19    public String getErrorCode() {
    20        return errorCode;
    21    }

    22
    23    public void setErrorCode(String errorCode) {
    24        this.errorCode = errorCode;
    25    }

    26
    27    public Set<String> getHandlerNames() {
    28        return handlerNames;
    29    }

    30}

    31
     

    ExceptionDefiniton定義了和某個(gè)exception相關(guān)的具體信息,根據(jù)exceptionclass name可以從exceptionContext中的exceptionMap得到指定的exception的相關(guān)信息,這些信息是在系統(tǒng)初始化時(shí)讀取到exceptionContext中的。并且避免了exception handler的重復(fù)初始化。

     

    可配置,受管式,方便使用

     采取兩種配置方式,exception的相關(guān)信息比如它的errorCode exceptionHandlers可以配置在外部的xml文件中,也可以用annotation標(biāo)注。對于exception的處理是有繼承性質(zhì)的,如果某個(gè)exception沒有在exceptionContext中注冊,就使用它的父類的配置信息。如果無任何父類在exceptionContext中注冊,就使用默認(rèn)機(jī)制進(jìn)行處理。

     

    XML 方案:

                因?yàn)?/span>spring2.0支持自定義schema功能,我們可以方便地采用自己的schema只要實(shí)現(xiàn)NamespaceHandlerBeanDefinitionPaser,后面一個(gè)比較重要,可以將自定義xml文件中的相關(guān)類注冊到spring的上下文中,成為spring bean

    Xml schema:

    <xsd:complexType name="exceptionType">
            
    <xsd:sequence>
                
    <xsd:element name="level" default="error" minOccurs="0">
                    
    <xsd:simpleType>
                        
    <xsd:restriction base="xsd:string">
                            
    <xsd:enumeration value="error" />
                            
    <xsd:enumeration value="warning" />
                            
    <xsd:enumeration value="info" />
                            
    <xsd:enumeration value="confirmation" />
                        
    </xsd:restriction>
                    
    </xsd:simpleType>
                
    </xsd:element>
                
    <xsd:element name="handler" maxOccurs="unbounded">
                    
    <xsd:simpleType>
                        
    <xsd:restriction base="xsd:string" />
                    
    </xsd:simpleType>
                
    </xsd:element>
            
    </xsd:sequence>
            
    <xsd:attribute name="errorCode">
                
    <xsd:simpleType>
                    
    <xsd:restriction base="xsd:string">
                        
    <xsd:whiteSpace value="preserve" />
                        
    <xsd:pattern value="LDD600-+\d{1,5}.*" />
                    
    </xsd:restriction>
                
    </xsd:simpleType>
            
    </xsd:attribute>
            
    <xsd:attribute name="class" type="xsd:string" use="required" />
        
    </xsd:complexType>

     

    Annotation方案:

                JDK1.5以上就有了annotation,可以簡化我們的配置,使得配置信息和代碼聯(lián)系在一起,增加了代碼的可讀性。如何在spring中注冊自定義的annotation和用annotation標(biāo)注的class,可以參考文章2和文章:   。對于每個(gè)注冊了的classExceptionalAnnotationBeanPostProcessorparse具體的annotation信息(對于annotationparse方法還會(huì)在以后繼續(xù)改進(jìn))。

     1package com.ldd600.exception.annotation;
     2
     3import java.lang.annotation.Documented;
     4import java.lang.annotation.ElementType;
     5import java.lang.annotation.Retention;
     6import java.lang.annotation.RetentionPolicy;
     7import java.lang.annotation.Target;
     8
     9import com.ldd600.exception.base.handler.ExceptionHandler;
    10
    11@Target({ElementType.TYPE})
    12@Retention(RetentionPolicy.RUNTIME)
    13@Documented
    14public @interface Exceptional {
    15    String errorCode();
    16    Class<? extends ExceptionHandler>[] handlers();
    17}

    18


     1package com.ldd600.exception.processor;
     2
     3import org.springframework.beans.BeansException;
     4import org.springframework.beans.factory.config.BeanPostProcessor;
     5
     6import com.ldd600.exception.annotation.Exceptional;
     7import com.ldd600.exception.base.BaseAppException;
     8import com.ldd600.exception.base.BaseAppRuntimeException;
     9import com.ldd600.exception.config.ExceptionDefinition;
    10import com.ldd600.exception.context.ExceptionContext;
    11import com.ldd600.exception.context.CoreContextFactory;
    12
    13public class ExceptionalAnnotationBeanPostProcessor implements BeanPostProcessor {
    14
    15    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    16       if(bean instanceof BaseAppRuntimeException || bean instanceof BaseAppException) {
    17           Exceptional exceptional = bean.getClass().getAnnotation(Exceptional.class);
    18           if(null != exceptional) {
    19               ExceptionContext ctx = CoreContextFactory.getInstance().getExceptionContext();
    20               if(!ctx.containsException(bean.getClass())) {
    21                   ExceptionDefinition expDefinition = new ExceptionDefinition(exceptional.errorCode());
    22                   ctx.setExceptionDefinition(bean.getClass(), expDefinition);
    23               }

    24               ctx.addExceptionHandlers(bean.getClass(), exceptional.handlers());
    25               return null;
    26           }

    27       }

    28       return bean;
    29    }

    30
    31    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    32            return bean;
    33    }

    34
    35}

    36
     

    結(jié)果測試:

      1package com.ldd600.exception.test;
      2
      3import org.jmock.Expectations;
      4import org.jmock.Mockery;
      5import org.springframework.beans.factory.BeanFactory;
      6
      7import com.ldd600.exception.action.BusinessAction;
      8import com.ldd600.exception.base.BaseAppException;
      9import com.ldd600.exception.base.BaseAppRuntimeException;
     10import com.ldd600.exception.base.ConfigException;
     11import com.ldd600.exception.base.handler.ConsoleHandler;
     12import com.ldd600.exception.context.CoreContextFactory;
     13import com.ldd600.exception.dto.DefaultRequest;
     14import com.ldd600.exception.dto.DefaultResponse;
     15import com.ldd600.exception.dto.Request;
     16import com.ldd600.exception.dto.Response;
     17import com.ldd600.exception.webservice.ActionBrokerImpl;
     18
     19public class ExceptionTest extends DependencyInjectionExceptionTestCase {
     20    Mockery context = new Mockery();
     21    ActionBrokerImpl broker = new ActionBrokerImpl();
     22    final Request request = new DefaultRequest();
     23    final Response response = new DefaultResponse();
     24
     25    @Override
     26    protected String[] getConfigLocations() {
     27        return new String[] "applicationContext.xml" };
     28    }

     29
     30    public void testExceptionThrow() {
     31        final BusinessAction<Response, Request> action = context
     32                .mock(BusinessAction.class);
     33        final BeanFactory beanFactory = context.mock(BeanFactory.class);
     34        assertThrowing(new Closure() {
     35            public void run() throws Throwable {
     36                context.checking(new Expectations() {
     37                    {
     38                        allowing(beanFactory).getBean("action");
     39                        will(returnValue(action));
     40                        one(action).execute(request, response);
     41                        will(throwException(new BaseAppException()));
     42                    }

     43                }
    );
     44                broker.setExceptionHandler(new ConsoleHandler());
     45                broker.setBeanFactory(beanFactory);
     46                broker.execute("action", request, response);
     47            }

     48
     49        }
    , BaseAppException.class);
     50    }

     51
     52    public void testExceptionalAutoLoad() throws BaseAppException {
     53        final BeanFactory beanFactory = context.mock(BeanFactory.class);
     54        final BusinessAction<Response, Request> action = context
     55                .mock(BusinessAction.class);
     56        context.checking(new Expectations() {
     57            {
     58                allowing(beanFactory).getBean("action");
     59                will(returnValue(action));
     60                one(action).execute(request, response);
     61                will(throwException(new ConfigException()));
     62            }

     63        }
    );
     64        broker.setBeanFactory(beanFactory);
     65        broker.execute("action", request, response);
     66        assertEquals(CoreContextFactory.getInstance().getExceptionContext()
     67                .getErrorCode(ConfigException.class), "LDD600-00002");
     68        context.assertIsSatisfied();
     69    }

     70
     71    public void testRuntimeException() {
     72        final BusinessAction<Response, Request> action = context
     73                .mock(BusinessAction.class);
     74        final BeanFactory beanFactory = context.mock(BeanFactory.class);
     75        assertThrowing(new Closure() {
     76            public void run() throws Throwable {
     77                context.checking(new Expectations() {
     78                    {
     79                        allowing(beanFactory).getBean("action");
     80                        will(returnValue(action));
     81                        one(action).execute(request, response);
     82                        will(throwException(new BaseAppRuntimeException()));
     83                    }

     84                }
    );
     85                broker.setExceptionHandler(new ConsoleHandler());
     86                broker.setBeanFactory(beanFactory);
     87                broker.execute("action", request, response);
     88            }

     89
     90        }
    , BaseAppRuntimeException.class);
     91        // test config
     92        assertEquals(CoreContextFactory.getInstance().getExceptionContext()
     93                .getErrorCode(BaseAppRuntimeException.class), "LDD600-00001");
     94        // test handler
     95        assertFalse(response.isSuccess());
     96        assertEquals(response.getErrorCode(), CoreContextFactory.getInstance()
     97                .getExceptionContext().getErrorCode(
     98                        BaseAppRuntimeException.class));
     99        context.assertIsSatisfied();
    100    }

    101
    102    public void testCheckedException() {
    103        final BusinessAction<Response, Request> action = context
    104                .mock(BusinessAction.class);
    105        final BeanFactory beanFactory = context.mock(BeanFactory.class);
    106        assertThrowing(new Closure() {
    107            public void run() throws Throwable {
    108                context.checking(new Expectations() {
    109                    {
    110                        allowing(beanFactory).getBean("action");
    111                        will(returnValue(action));
    112                        one(action).execute(request, response);
    113                        will(throwException(new ExceptionFaker()));
    114                    }

    115                }
    );
    116                broker.setBeanFactory(beanFactory);
    117                broker.execute("action", request, response);
    118            }

    119
    120        }
    , ExceptionFaker.class);
    121        // test config
    122        assertEquals(CoreContextFactory.getInstance().getExceptionContext()
    123                .getErrorCode(ExceptionFaker.class), "LDD600-00003");
    124        // test handler
    125        assertFalse(response.isSuccess());
    126        assertEquals(response.getErrorCode(), CoreContextFactory.getInstance()
    127                .getExceptionContext().getErrorCode(
    128                        ExceptionFaker.class));
    129        context.assertIsSatisfied();
    130    }

    131}

    132


    參考資料:
     

    文章1http://www.onjava.com/pub/a/onjava/2006/01/11/exception-handling-framework-for-j2ee.html

    文章2http://sannotations.sourceforge.net/

    本文源代碼:源代碼下載



    posted on 2008-06-30 18:27 叱咤紅人 閱讀(7743) 評(píng)論(8)  編輯  收藏 所屬分類: Spring

    評(píng)論

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2008-06-30 20:12 藍(lán)劍
    不錯(cuò),很有參考價(jià)值!  回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2008-07-01 11:48 ken.wug
    我只要service的異常,可以實(shí)現(xiàn)嗎?
    一般action是表現(xiàn)層的一些業(yè)務(wù)邏輯,這類一場,我會(huì)直接包裝后,提示用戶。  回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2008-07-01 14:01 ldd600
    service的異常,需要繼續(xù)向上拋出。
    我想有兩種方法吧:
    1.你可以用不同種類的異常區(qū)分action和service,然后用不同的handler處理異常,一個(gè)提示給用戶,另一個(gè)自己記錄
    2.可以在action和service之間加一個(gè)broker  回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2008-07-01 14:02 ldd600
    感謝您的回復(fù)! :)
    @ken.wug
    service的異常,需要繼續(xù)向上拋出。
    我想有兩種方法吧:
    1.你可以用不同種類的異常區(qū)分action和service,然后用不同的handler處理異常,一個(gè)提示給用戶,另一個(gè)自己記錄
    2.可以在action和service之間加一個(gè)broker  回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2008-07-03 08:59 龐永慶
    ★☆★誠招Java書籍作者(兼職)★☆★

    Java中又分為很多方面,可以寫基礎(chǔ)入門書,或者Web Eclipse 等多個(gè)方向。

    大部分作者都是異地兼職來寫書,異地兼職來做是完全沒有問題的。
    作者可以得到如下報(bào)酬:
    (1)豐厚的稿酬。
    (2)書籍的署名權(quán),和由此帶來的宣傳效益。
    (3)能夠很大程度的提高作者的人氣和聲望。
    如果你有這方面的興趣,可以通過下面的聯(lián)系方式和我聯(lián)系
    books_522008@yahoo.com.cn
    MSN:pyq_19852008@hotmail.com
    QQ:878 298 915 注明Java
    我會(huì)盡快和你聯(lián)系
      回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2008-07-09 10:07 pl
    收藏下~  回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架[未登錄] 2008-12-23 11:25 aaa
    太復(fù)雜了  回復(fù)  更多評(píng)論
      

    # re: 在Spring基礎(chǔ)上實(shí)現(xiàn)自己的異常處理框架 2014-04-11 17:45 最代碼
    最代碼網(wǎng)站上整理了你的代碼,地址:
    http://www.zuidaima.com/share/1774096228535296.htm
    有問題請回復(fù)  回復(fù)  更多評(píng)論
      


    只有注冊用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲色丰满少妇高潮18p| 西西大胆无码视频免费| 亚洲AV成人片色在线观看高潮| 中文字幕无线码免费人妻| 伊人久久亚洲综合| 丝袜足液精子免费视频| 久久亚洲精品视频| 国产成人久久AV免费| 亚洲va在线va天堂va四虎| 99蜜桃在线观看免费视频网站| 亚洲视频一区网站| 国产精品永久免费10000| 亚洲一区精彩视频| 日韩视频在线免费| 免费人成大片在线观看播放电影| 亚洲成A人片在线观看中文| 一级毛片完整版免费播放一区| 在线日韩日本国产亚洲| 中文字幕免费不卡二区| 亚洲国产美国国产综合一区二区| 91精品国产免费久久国语麻豆| 亚洲欧洲日本国产| 毛片免费视频观看| 国产亚洲蜜芽精品久久| 亚洲综合国产精品第一页| 在线观看免费播放av片| 亚洲综合视频在线观看| 久久久久久久91精品免费观看| 亚洲精华国产精华精华液好用 | 色在线亚洲视频www| 精品少妇人妻AV免费久久洗澡| 国产亚洲精品免费| 综合亚洲伊人午夜网| 久久久久国色av免费看| 亚洲一卡2卡3卡4卡国产网站| 日日夜夜精品免费视频| 亚洲精品国产日韩无码AV永久免费网 | 亚洲Av永久无码精品三区在线 | 99爱免费观看视频在线| 久久精品国产亚洲av麻豆图片| 国产男女猛烈无遮档免费视频网站|