JavaWeb中的Struts2概述


       

    

        

            

                                

JavaWeb中的Struts2概述


            

            
        

    

    

        

                                

                                      
                            
                    
                                      
                    版权声明:本文为博主原创文章,未经博主允许不得转载。                    https://blog.csdn.net/u012605477/article/details/76129675                

                                            
                        

                

Struts2是一种基于MVC模式的javaWeb框架,本质上相当于Servlet。


所谓MVC,就是模型-视图-控制器。


Model(模型)表示应用程序核心(比如数据库记录列表)。


View(视图)显示数据(数据库记录)。


Controller(控制器)处理输入(写入数据库记录)


而Struts的作用实际上是作为控制器,建立模型层和视图层的数据交互(就是通常所说控制model和jsp之间的数据交互)


上面都是废话,你只要知道struts2能控制model和jsp之间的数据交互即可。


web程序想要使用struts2框架,除了要引用相关jar包外,还要在web.xml中对struts2进行配置,只有配置后,struts2才能对浏览器的请求进行一系列处理。


怎么在web应用中配置struts2?


首先在web.xml中添加struts2的使用,代码如下:



  1.     <!-- 配置Struts2 核心 Filter -->
  2.     <filter>
  3.         <filter-name>action2</filter-name>
  4.         <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
  5.     </filter>
  6.     <filter-mapping>
  7.         <filter-name>action2</filter-name>
  8.         <url-pattern>/*</url-pattern>
  9.     </filter-mapping>


上述代码使用了Filter,那我们就回顾一下Filter的作用。


回顾:Filter,中文名为过滤器,通过Filter可以对web服务器的资源进行管理,例如Jsp,Servlet, 静态图片文件等进行拦截,从而实现一些特殊的功能。


实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能


从代码层面来讲,只要创建一个实现了Filter接口的实现类,然后将这个类在web.xml进行配置,那么这个类就可以让用户在访问某个目标资源之前,对访问的请求和响应进行拦截。


更多介绍可以看我的另外一篇文章:http://blog.csdn.net/u012605477/article/details/75258358


注意:Struts2在web.xml中看来就是一个Filter,只不过其实现类StrutsPrepareAndExecuteFilter不是我们写的,是Struts2的。


为了验证StrutsPrepareAndExecuteFilter也实现了Filter接口,我们将其和我们自写的过滤器进行对比:


下面是自写的过滤器FilterA配置:



  1.     <filter>
  2.         <filter-name>FilterA</filter-name>
  3.         <filter-class>com.strategy.jpa.FilterA</filter-class>
  4.     </filter>
  5.     <filter-mapping>
  6.         <filter-name>FilterA</filter-name>
  7.         <url-pattern>/*</url-pattern>
  8.     </filter-mapping>
自写FilterA的代码:



  1. package com.strategy.jpa;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. public class FilterA implements Filter {
  10.     public void init(FilterConfig filterConfig) throws ServletException {
  11.     }
  12.     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  13.             throws IOException, ServletException {
  14.         
  15.         
  16.         chain.doFilter(request, response);
  17.         response.reset();
  18.         
  19.     }
  20.     public void destroy() {
  21.     }
  22. }
web中配置Struts2的代码:



  1. * $Id: DefaultActionSupport.java 651946 2008-04-27 13:41:38Z apetrelli $
  2. package org.apache.struts2.dispatcher.filter;
  3. import org.apache.logging.log4j.LogManager;
  4. import org.apache.logging.log4j.Logger;
  5. import org.apache.struts2.RequestUtils;
  6. import org.apache.struts2.StrutsStatics;
  7. import org.apache.struts2.dispatcher.Dispatcher;
  8. import org.apache.struts2.dispatcher.mapper.ActionMapping;
  9. import org.apache.struts2.dispatcher.ExecuteOperations;
  10. import org.apache.struts2.dispatcher.InitOperations;
  11. import org.apache.struts2.dispatcher.PrepareOperations;
  12. import javax.servlet.Filter;
  13. import javax.servlet.FilterChain;
  14. import javax.servlet.FilterConfig;
  15. import javax.servlet.ServletException;
  16. import javax.servlet.ServletRequest;
  17. import javax.servlet.ServletResponse;
  18. import javax.servlet.http.HttpServletRequest;
  19. import javax.servlet.http.HttpServletResponse;
  20. import java.io.IOException;
  21. import java.util.List;
  22. import java.util.regex.Pattern;
  23. /**
  24. * Handles both the preparation and execution phases of the Struts dispatching process.  This filter is better to use
  25. * when you don't have another filter that needs access to action context information, such as Sitemesh.
  26. */
  27. public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
  28.    private static final Logger LOG = LogManager.getLogger(StrutsPrepareAndExecuteFilter.class);
  29.    protected PrepareOperations prepare;
  30.    protected ExecuteOperations execute;
  31.    protected List<Pattern> excludedPatterns = ;
  32.    public void init(FilterConfig filterConfig) throws ServletException {
  33.        InitOperations init = new InitOperations();
  34.        Dispatcher dispatcher = ;
  35.        try {
  36.            FilterHostConfig config = new FilterHostConfig(filterConfig);
  37.            init.initLogging(config);
  38.            dispatcher = init.initDispatcher(config);
  39.            init.initStaticContentLoader(config, dispatcher);
  40.            prepare = new PrepareOperations(dispatcher);
  41.            execute = new ExecuteOperations(dispatcher);
  42.            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
  43.            postInit(dispatcher, filterConfig);
  44.        } finally {
  45.            if (dispatcher != ) {
  46.                dispatcher.cleanUpAfterInit();
  47.            }
  48.            init.cleanup();
  49.        }
  50.    }
  51.    /**
  52.     * Callback for post initialization
  53.     *
  54.     * @param dispatcher the dispatcher
  55.     * @param filterConfig the filter config
  56.     */
  57.    protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
  58.    }
  59.    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  60.        HttpServletRequest request = (HttpServletRequest) req;
  61.        HttpServletResponse response = (HttpServletResponse) res;
  62.        try {
  63.            String uri = RequestUtils.getUri(request);
  64.            if (excludedPatterns != && prepare.isUrlExcluded(request, excludedPatterns)) {
  65.                LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
  66.                chain.doFilter(request, response);
  67.            } else {
  68.                LOG.trace("Checking if {} is a static resource", uri);
  69.                boolean handled = execute.executeStaticResourceRequest(request, response);
  70.                if (!handled) {
  71.                    LOG.trace("Assuming uri {} as a normal action", uri);
  72.                    prepare.setEncodingAndLocale(request, response);
  73.                    prepare.createActionContext(request, response);
  74.                    prepare.assignDispatcherToThread();
  75.                    request = prepare.wrapRequest(request);
  76.                    ActionMapping mapping = prepare.findActionMapping(request, response, true);
  77.                    if (mapping == ) {
  78.                        LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
  79.                        chain.doFilter(request, response);
  80.                    } else {
  81.                        LOG.trace("Found mapping {} for {}", mapping, uri);
  82.                        execute.executeAction(request, response, mapping);
  83.                    }
  84.                }
  85.            }
  86.        } finally {
  87.            prepare.cleanupRequest(request);
  88.        }
  89.    }
  90.    public void destroy() {
  91.        prepare.cleanupDispatcher();
  92.    }
  93. }
对比可知,struts2也实现了Filter接口,只是他比我们自己写的FilterA多实现了一个接口StrutsStatics,其它的没有变化。


我们对上面的代码进行了解:


在Init方法里面,我们看到有一个config对象,该类对象的作用是对FilterConfig进行封装。


  1. package org.apache.struts2.dispatcher.filter;
  2. import org.apache.struts2.util.MakeIterator;
  3. import javax.servlet.FilterConfig;
  4. import javax.servlet.ServletContext;
  5. import java.util.Iterator;
  6. import org.apache.struts2.dispatcher.HostConfig;
  7. /**
  8. * Host configuration that wraps FilterConfig
  9. */
  10. public class FilterHostConfig implements HostConfig {
  11.    private FilterConfig config;
  12.    public FilterHostConfig(FilterConfig config) {
  13.        this.config = config;
  14.    }
  15.    public String getInitParameter(String key) {
  16.        return config.getInitParameter(key);
  17.    }
  18.    public Iterator<String> getInitParameterNames() {
  19.        return MakeIterator.convert(config.getInitParameterNames());
  20.    }
  21.    public ServletContext getServletContext() {
  22.        return config.getServletContext();
  23.    }
  24. }

然后又在Init方法里面通过创建的config对象,并调用initDispatcher方法创建了转发器对象dispatcher对象。以及通过initLogging方法初始化日志记录器。


  1.    private Dispatcher createDispatcher( HostConfig filterConfig ) {
  2.        Map<String, String> params = new HashMap<>();
  3.        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
  4.            String name = (String) e.next();
  5.            String value = filterConfig.getInitParameter(name);
  6.            params.put(name, value);
  7.        }
  8.        return new Dispatcher(filterConfig.getServletContext(), params);
  9.    }
  10.    public void cleanup() {
  11.        ActionContext.setContext();
  12.    }
  1.    public void initLogging( HostConfig filterConfig ) {
  2.        String factoryName = filterConfig.getInitParameter("loggerFactory");
  3.        if (factoryName != ) {
  4.            try {
  5.                Class cls = ClassLoaderUtil.loadClass(factoryName, this.getClass());
  6.                LoggerFactory fac = (LoggerFactory) cls.newInstance();
  7.                LoggerFactory.setLoggerFactory(fac);
  8.            } catch ( InstantiationException e ) {
  9.                System.err.println("Unable to instantiate logger factory: " + factoryName + ", using default");
  10.                e.printStackTrace();
  11.            } catch ( IllegalAccessException e ) {
  12.                System.err.println("Unable to access logger factory: " + factoryName + ", using default");
  13.                e.printStackTrace();
  14.            } catch ( ClassNotFoundException e ) {
  15.                System.err.println("Unable to locate logger factory class: " + factoryName + ", using default");
  16.                e.printStackTrace();
  17.            }
  18.        }
  19.    }

接下来调用了init对象initStaticContentLoader(config, dispatcher);方法加载一些静态资源。


  1.    public StaticContentLoader initStaticContentLoader( HostConfig filterConfig, Dispatcher dispatcher ) {
  2.        StaticContentLoader loader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
  3.        loader.setHostConfig(filterConfig);
  4.        return loader;
  5.    }


最重要的就是dispatcher,它主要将filter拦截到的请求转入struts2的请求处理模块,我们必须知道这一点。


其次是FilterConfig,它将我们在web.xml中的Filter的配置信息也保存到了dispatcher中。下面是该对象的构造方法。



  1.    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
  2.        this.servletContext = servletContext;
  3.        this.initParams = initParams;
  4.    }

接下来就是prepare和execute对象,和InitOperations类似,也是进行了封装一些操作,都是截取一部分重要的代码。


  1.    public PrepareOperations(Dispatcher dispatcher) {
  2.        this.dispatcher = dispatcher;
  3.    }
  4.    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
  5.        ActionContext ctx;
  6.        Integer counter = 1;
  7.        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
  8.        if (oldCounter != ) {
  9.            counter = oldCounter + 1;
  10.        }
  11.        
  12.        ActionContext oldContext = ActionContext.getContext();
  13.        if (oldContext != ) {
  14.            // detected existing context, so we are probably in a forward
  15.            ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));
  16.        } else {
  17.            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
  18.            stack.getContext().putAll(dispatcher.createContextMap(request, response, ));
  19.            ctx = new ActionContext(stack.getContext());
  20.        }
  21.        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
  22.        ActionContext.setContext(ctx);
  23.        return ctx;
  24.    }
execute对象
  1.    public ExecuteOperations(Dispatcher dispatcher) {
  2.        this.dispatcher = dispatcher;
  3.    }
  4.    public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
  5.        // there is no action in this request, should we look for a static resource?
  6.        String resourcePath = RequestUtils.getServletPath(request);
  7.        if ("".equals(resourcePath) && != request.getPathInfo()) {
  8.            resourcePath = request.getPathInfo();
  9.        }
  10.        StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
  11.        if (staticResourceLoader.canHandle(resourcePath)) {
  12.            staticResourceLoader.findStaticResource(resourcePath, request, response);
  13.            // The framework did its job here
  14.            return true;
  15.        } else {
  16.            // this is a normal request, let it pass through
  17.            return false;
  18.        }
  19.    }

从上面的代码可以看出,这两个对象封装了请求预处理和请求处理的操作,当处理请求时方法被调用


在上述的代码中有个很重要的方法未罗列出来,那就是Disptcher中得init方法,该方法初始读取一些配置文件,包含我们想要知道的读取struts2.xml的方法。


  1.    public void init() {
  2.        if (configurationManager == ) {
  3.            configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
  4.        }
  5.        try {
  6.            init_FileManager();
  7.            init_DefaultProperties(); // [1]
  8.            init_TraditionalXmlConfigurations(); // [2]
  9.            init_LegacyStrutsProperties(); // [3]
  10.            init_CustomConfigurationProviders(); // [5]
  11.            init_FilterInitParameters() ; // [6]
  12.            init_AliasStandardObjects() ; // [7]
  13.            Container container = init_PreloadConfiguration();
  14.            container.inject(this);
  15.            init_CheckWebLogicWorkaround(container);
  16.            if (!dispatcherListeners.isEmpty()) {
  17.                for (DispatcherListener l : dispatcherListeners) {
  18.                    l.dispatcherInitialized(this);
  19.                }
  20.            }
  21.            errorHandler.init(servletContext);
  22.        } catch (Exception ex) {
  23.            LOG.error("Dispatcher initialization failed", ex);
  24.            throw new StrutsException(ex);
  25.        }
  26.    }
上面是init方法,里面调用了诸多读取配置文件的方法,包含我们想要知道的读取struts.xml的方法:
  1.    private void init_TraditionalXmlConfigurations() {
  2.        String configPaths = initParams.get("config");
  3.        if (configPaths == ) {
  4.            configPaths = DEFAULT_CONFIGURATION_PATHS;
  5.        }
  6.        String[] files = configPaths.split("\\s*[,]\\s*");
  7.        for (String file : files) {
  8.            if (file.endsWith(".xml")) {
  9.                if ("xwork.xml".equals(file)) {
  10.                    configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
  11.                } else {
  12.                    configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
  13.                }
  14.            } else {
  15.                throw new IllegalArgumentException("Invalid configuration file name");
  16.            }
  17.        }
  18.    }
在上面的代码中,设定了读取struts.xml的默认路径,其内容如下:
private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";
至此,web应用配置struts2框架,以及struts2如何将Filter封装成struts2的,甚至如何读取struts.xml以及其他配置,并进行相关struts2参数初始化都有了大概了解,接下来说一下Action。


回到StrutsPrepareAndExecuteFilter类的doFilter中,


  1.    //每次发送一个Request,StrutsPrepareAndExecuteFilter都会调用doFilter方法  
  2.    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
  3.  
  4.        HttpServletRequest request = (HttpServletRequest) req;  
  5.        HttpServletResponse response = (HttpServletResponse) res;  
  6.  
  7.        try {  
  8.            //设置编码和国际化    
  9.            prepare.setEncodingAndLocale(request, response);  
  10.            //ActionContext创建  
  11.            prepare.createActionContext(request, response);  
  12.            prepare.assignDispatcherToThread();  
  13.            if ( excludedPatterns != && prepare.isUrlExcluded(request, excludedPatterns)) {  
  14.                chain.doFilter(request, response);  
  15.            } else {  
  16.                request = prepare.wrapRequest(request);  
  17.                ActionMapping mapping = prepare.findActionMapping(request, response, true);  
  18.                //如果找不到对应的action配置  
  19.                if (mapping == ) {  
  20.                    /*
  21.                     * 就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,
  22.                     * 当然.class文件除外。如果再没有则跳转到404
  23.                     */  
  24.                    boolean handled = execute.executeStaticResourceRequest(request, response);  
  25.                    if (!handled) {  
  26.                        chain.doFilter(request, response);  
  27.                    }  
  28.                } else {  
  29.                    /*
  30.                     * 找到对应action配置文件后,调用ExecuteOperations类中executeAction,
  31.                     * 开始谳用Action的方法。
  32.                     */  
  33.                    execute.executeAction(request, response, mapping);  
  34.                }  
  35.            }  
  36.        } finally {  
  37.            prepare.cleanupRequest(request);  
  38.        }  
  39.    }  
相关准备(prepare)方法,设置编码的方法不再叙述(setEncodingAndLocale),我们主要说一下ActionContext。

prepare.createActionContext(request, response);


ActionContext是一个struts2容器,主要存储request、session、application、parameters等相关信息。


ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问题


  1.    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
  2.        ActionContext ctx;
  3.        Integer counter = 1;
  4.        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
  5.        if (oldCounter != ) {
  6.            counter = oldCounter + 1;
  7.        }
  8.        
  9.        ActionContext oldContext = ActionContext.getContext();
  10.        if (oldContext != ) {
  11.            // detected existing context, so we are probably in a forward
  12.            ctx = new ActionContext(new HashMap<>(oldContext.getContextMap()));
  13.        } else {
  14.            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
  15.            stack.getContext().putAll(dispatcher.createContextMap(request, response, ));
  16.            ctx = new ActionContext(stack.getContext());
  17.        }
  18.        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
  19.        ActionContext.setContext(ctx);
  20.        return ctx;
  21.    }


我们再看一下ActionContext是什么,取部分代码,由此可知ActionContext是一个键值对集合,跟Spring中的bean类似:


  1. public class ActionContext implements Serializable {
  2.    static ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();
  3.    
  4.    private Map<String, Object> context;
  5.    public ActionContext(Map<String, Object> context) {
  6.        this.context = context;
  7.    }
有了创建Action的方法,肯定有使用Action的方法,我们现在去找使用的方法:
  1.    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
  2.            throws ServletException {
  3.        Map<String, Object> extraContext = createContextMap(request, response, mapping);
  4.        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
  5.        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
  6.        boolean nullStack = stack == ;
  7.        if (nullStack) {
  8.            ActionContext ctx = ActionContext.getContext();
  9.            if (ctx != ) {
  10.                stack = ctx.getValueStack();
  11.            }
  12.        }
  13.        if (stack != ) {
  14.            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
  15.        }
  16.        String timerKey = "Handling request from Dispatcher";
  17.        try {
  18.            UtilTimerStack.push(timerKey);
  19.            String namespace = mapping.getNamespace();
  20.            String name = mapping.getName();
  21.            String method = mapping.getMethod();
  22.            ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
  23.                    namespace, name, method, extraContext, true, false);
  24.            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
  25.            // if the ActionMapping says to go straight to a result, do it!
  26.            if (mapping.getResult() != ) {
  27.                Result result = mapping.getResult();
  28.                result.execute(proxy.getInvocation());
  29.            } else {
  30.                proxy.execute();
  31.            }
  32.            // If there was a previous value stack then set it back onto the request
  33.            if (!nullStack) {
  34.                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
  35.            }
  36.        } catch (ConfigurationException e) {
  37.            logConfigurationException(request, e);
  38.            sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
  39.        } catch (Exception e) {
  40.            if (handleException || devMode) {
  41.                sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
  42.            } else {
  43.                throw new ServletException(e);
  44.            }
  45.        } finally {
  46.            UtilTimerStack.pop(timerKey);
  47.        }
  48.    }

在上面的try语句里面我们可以得知,struts是怎么通过命名空间,读取action的配置,以及action的method方法,


尤其是如何从容器中获得ActionProxyFactory代理工厂 ,是怎么创建ActionProxy来执行一个特定的命名空间和动作的。


以及怎么通过我们在struts2中设定的跳转方法,跳转到指定页面的。


  1.            //执行execute方法,并转向结果  
  2.            if (mapping.getResult() != ) {  
  3.                Result result = mapping.getResult();  
  4.                result.execute(proxy.getInvocation());  
  5.            } else {  
  6.                proxy.execute();  
  7.            }

至此,对于Struts2是如何工作的,怎么读取xml的,如何搭建在web应用中基本叙述完了,更详细的实在说不完,各位大佬可以看下下面这位大神的博客,很给力,就是看起来太繁琐。


另外,本文部分来源于该大神,未通知,深表歉意。

http://blog.csdn.net/yuan_xw/article/details/7838123





           

               

    


 

   

       
       
   
    
 

 

       



   
       
           
       

       
            新年折上折!德国钻石无油烟不粘锅!2折只限今日!不抢就亏了!
            建颂企业 · 燨燚
       

   

   



       

    
    

        
        

            
                
            

        

        

            
            
            

                

                    
                

                
                
                
                
                  
                

                    还能输入1000个字符
                    
                

            

        

    

        


        
        

        

        

        
    


       

                    
    
    
        
            
    
    
        
            
    
   

   
       
           
       

       
            揭秘:头上长白发竟是身体缺了它?饭后吃点它,白发轻松变黑发!
            新至尊 · 燨燚
       

   

   




        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
                
    
   

   
       
           
       

       
            揭秘:头上长白发竟是身体缺了它?饭后吃点它,白发轻松变黑发!
            新至尊 · 燨燚
       

   

   




        
            
    
    
            

                        

                
文章热词

                

                                                    
                        
                        聚类算法概述                     

                                                                    
                        
                        可变长度数组概述                    

                                                                    
                        
                        em算法中的隐变量问题                    

                                                                    
                        
                        机器学习                    

                                                                    
                        
                        机器学习课程                    

                                                

            

                                

        
            
    
    
              

                

                    

                        

                            
LIUXUN1993728关注
LIUXUN1993728
 

345篇文章

排名:2000+

Jorocco关注
Jorocco
 

113篇文章

排名:千里之外

豆芽胡已关注取消
豆芽胡
 

60篇文章

排名:千里之外

Jack-Chan关注
Jack-Chan
 

468篇文章

排名:1000+


                        

                    

                

     

    
            
    
    
        
            
    
    
        
            
    
   

        
            
    
    
        
            
    
    
        
            
    
    
        
                
    
    
        
            
    
   

   
       
           
       

       
            一插上电,50平米内都暖和了!3天一度电,今日特惠!
            优诺 · 顶新
       

   

   




        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
   

   
       
           
       

       
            一插上电,50平米内都暖和了!3天一度电,今日特惠!
            优诺 · 顶新
       

   

   




        
                
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
   

   
       
           
       

       
            这家的片碱  价格为什么这么低 ,有什么秘密吗?
            泽琪化工 · 燨燚
       

   

   




        
            
    
    
        
                
    
    
        
            
    
    
        
            
    
    
        
            
    
   

   
       
           
       

       
            新年折上折!德国钻石无油烟不粘锅!2折只限今日!不抢就亏了!
            建颂企业 · 燨燚
       

   

   



        
                
    
    
        
                
    
    
        
            
    
    
        
            
    
    
        
                
    
   

        
            
    
    
        
            
    
    
        
                
    
    
        
            
    
    
        
                
    
   

        
                
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
   

   
       
           
       

       
            27岁冷水江妹子通过网络平台赚钱,爆赚成网红
            圣工石化 · 燨燚
       

   

   



        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
            
    
    
        
    

           


               
           

           

               

没有更多推荐了,返回首页


           

       

   

你可能感兴趣的:(JavaWeb中的Struts2概述)