`
AngelAndAngel
  • 浏览: 230442 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

动手开发自己的mvc-2----完善控制层,提供自动注入和注解上传等功能

阅读更多
  当表单提交的内容过多 ,让懒惰的程序员一个个getParameter()是很让人抓狂的,所以自动注入表单域是mvc不可或缺的功能,另外,文件上传也是一个特殊的表单域,你想看到程序员发觉上传只需要注入就能完成功能时的那种欣喜吗  ? 我们一起做做看。
    我们依然从最简单的开始做,慢慢的润色。
    注入表单的思路比较简单:
    1,在form里面的name需要设置成诸如userinfo.username这类的,userinfo表示注入的目标对象,username表示userinfo对象的属性。这个对象必须是Action里面声明的
    2,MainServlet在接收表单时,从getParameterMap()得到所有表单域,拆分出目标对象和属性,通过反射执行set方法
  
注意:由于每个请求都会产生一个Action的新实例,所以在Action类的属性不会被多个请求共享,是线程安全的。

实现方式如下:
1,打开MainServlet,首先声明
Map<String,Object[]> paramMap = request.getParameterMap();

//此map对象用来缓存单页面的目标注入对象,比如此页面有多个Userinfo的属性需要注入,不可能每次注入都要生成Userinfo对象,肯定得在同一个对象中注入(小细节)
Map<String, Object> fieldMap = new HashMap<String, Object>();

得到请求信息后进行迭代
Set<Entry<String,Object[]>> paramSet = paramMap.entrySet();
for (Entry<String,Object[]> ent : paramSet) {
                   String paramName = (String) ent.getKey();
                   
                   Object[] paramValue = ent.getValue();
                   
                   handField(fieldMap,paramName,paramValue,action);
}


handField方法用来处理注入功能。
方法体和详细注释如下:
//.这个字符是不能直接用正则的,需要转义
          String[] paramVos = paramName.split("\\.");
          //这里只支持 对象.属性的表单注入,对于多级的大家可以自行实现,相信不是难事儿。
           if (paramVos. length == 2) {
              Class actionClass = action.getClass();
              Object fieldObj = fieldMap.get(paramVos[0]);
              //从你的action得到目标注入对象
              Field field  = actionClass.getDeclaredField(paramVos[0]);;
               if (fieldObj == null) {
                   //假如是第一次注入,为空,则实例化目标对象
                   Class fieldClass = field.getType();
                   fieldObj = fieldClass.newInstance();
                   //放入缓存,第二次直接从缓存取,保证同一个form注入的是同一个对象 
                   fieldMap.put(paramVos[0], fieldObj);
            }
              //构造目标属性的set方法 
              String setMethod = "set"
                        + paramVos[1].substring(0, 1).toUpperCase()
                        + paramVos[1].substring(1);
              Field fieldField = null;
              fieldField = fieldObj.getClass().getDeclaredField(
                        paramVos[1]);

              
               if(realValue!= null){
                   InvocakeHelp. invokeMethod(fieldObj, setMethod,
                              new Object[] { paramValue }); 
              }
              

          }


到此,基本的注入功能就有了,测试一下.
我们编写一个Userinfo对象,属性为username,email等,并在TestAction里声明Userinfo对象,名为user。
然后form表单里写好表单
<input type="text" name="user.username" />
<input type="text" name="user.email" />
提交表单,发觉已经被注入了,测试成功。
但是,这里只是测试的字符串类的表单域,假如Userinfo里面还有个age属性,Integer型的,该怎么办呢?Integer类型的接收String型的肯定是不行的。
有人说,那就在MainServlet直接判断呗,直接得到属性type,判断假如是Integer,就Integer.parseInt一下。
但是假如是double类型的呢?假如是更多类型的呢,假如是自定义的类型呢? 所以这里显然得做成可扩展的。
回想一下直接判断是个怎样的过程:
1,获取注入属性的类型(type)
2,根据不同类型,用不同方式转换值,比如Java.lang.Integer对应parseInt,Double对应parseDouble。
3,注入转换后的值
假如做成可扩展的,那么转换的类型是程序员自定义的,另外根据不同类型,配置不同的转换器,然后让MainServlet读取并自动转换。
那么我们的配置看起来应该是这样的:
 <converter type= "java.lang.Integer" handle= "org.love.converter.IntegerConverter" >
     </converter >


让IntegerConverter实现你设定好的接口,让MainServlet统一接口调用。
接口代码如下:
public interface TypeConverter {
     
     /**
      *
      * @param value 将要转换的值
      * @param field 将要转换的属性 元数据包含了更多的信息
      * @return 得到被转换后的对象
      */
     public Object convertValue(Object value, Field field);
}


IntegerConverter实现代码如下:
public class IntegerConverter implements TypeConverter {

     public Object convertValue(Object value,Field field) {
          
           if(value== null || value.equals( "")){
               return null;
          }
          //假如这里是数组,那么组装Integer数组并返回
           if(field.getType().isArray()){
              String[] intStr=(String[])value;
              Integer[] returnInt= new Integer[intStr.length];
               for( int i=0;i<intStr. length;i++){
                    if(intStr[i]!= null&&!(intStr[i].trim().equals( ""))){
                        returnInt[i]=Integer. parseInt(intStr[i]);  
                   }
                   
              }
               return returnInt;
          } else{
               return Integer. parseInt(value.toString());        
          }
          
          
     }

}


,然后打开ControlXML类,加上
private Map<String,TypeConverter> convertMap= new HashMap<String, TypeConverter>();

读取 converter元素并装配到convertMap.(详细代码就不贴了,可以对照源代码看)
现在可以在MainServlet里调用了,首先获得注入属性的类型
//得到被反射的属性的类别名称,假如是数组,也返回原始类型
String fieldTypeClassName = (fieldField.getType().isArray()?fieldField.getType().getComponentType().getName():fieldField.getType().getName());

这句代码比较长,我们没有简单的getType().getName(),原因是,假如注入属性的类别是Integer数组(或者其他类别数组),用getType()是得不到原始类别的(因为数组也是个特殊的类别),所以这里判断假如是数组就得到原始类别,方便后面做判断。
//接收原始值 
 Object realValue =paramValue;


/*
               * 假如传递过来的是字符串数组(比如多选)并且被注入的元素类别是非数组
               * 那么会被以逗号分割拼接,假如是数组就不用处理,直接用数组接收
              */
               if(paramValue instanceof String[]){                 
                  if(!fieldField.getType().isArray()){
                         realValue = Utils.join((String[])paramValue,","); 
                   }    
              }

//最后调用
if (realValue!=null&&convertMap.containsKey(fieldTypeClassName)) {
                   realValue = convertMap.get(fieldTypeClassName)
                             .convertValue(realValue, fieldField);
              }


realValue就是转换后的值,反射注入搞定!

接口的设计为程序的扩展提供无限可能,我们趁热打铁,假如注入属性是Date类型呢?很好办。
创建DateConverter类实现TypeConverter,并且在control.xml里配置
<converter type ="java.util.Date" handle= "org.love.converter.DateConverter">
</converter >

实现过程也比较简单,想必都会,我在这里写了个稍微功能强一点的Date转换器,代码如下,仅供参考:
public class DateConverter implements TypeConverter {

     private static final String[] FORMAT = {
           "HH",  // 2
           "yyyy", // 4
           "HH:mm", // 5
           "yyyy-MM", // 7
           "HH:mm:ss", // 8
           "yyyy-MM-dd", // 10
           "yyyy-MM-dd HH", // 13
           "yyyy-MM-dd HH:mm", // 16
           "yyyy-MM-dd HH:mm:ss" // 19
     }; 
     
     private static final DateFormat[] ACCEPT_DATE_FORMATS = new DateFormat[FORMAT.length]; //支持转换的日期格式  
     
     static {      
           for( int i=0; i< FORMAT. length; i++){
               ACCEPT_DATE_FORMATS[i] = new SimpleDateFormat(FORMAT[i]);
          }
     }
     
     public Object convertValue(Object value,Field field) {   
       
           if(value== null||value.equals( "")){
               return null;
          }
        //String[] params = (String[])value;   
        String dateString = (String)value; //获取日期的字符串
        int len = dateString != null ? dateString.length() : 0;
        int index   = -1;
       
        if (len > 0) {
               for ( int i = 0; i < FORMAT. length; i++) {
                    if (len == FORMAT[i].length()) {
                         index = i;
                   }
              }
          }
      
       
        if(index >= 0){
           try {
                    return ACCEPT_DATE_FORMATS[index].parse(dateString);
              } catch (ParseException e) {      
                    return null;
              }
        }
        return null;   
    }

}


也就是说支持数组中的各种时间格式。


设计程序时需要不断调整和重构,当我发现这两个转换器属于程序必备品,为什么还需要每次配置在control.xml里面呢?所以干掉这两个converter配置
,直接在ControlXML中加入代码:
convertMap .put("java.util.Date" , new DateConverter());
convertMap.put("java.lang.Integer" , new IntegerConverter());

到目前位置,C层的开发貌似已经有模有样了,可以放心大胆的测试了。

我们回到本章的开头,假设一个表单里面不仅有文本,还有文件上传,那么用这个框架肯定是搞不定的,因为你没法同时接收到文本数据和二进制流,而上传实在是一个烂大街的功能,所以我们必须搞定它。
用过struts2的都知道它是通过common-fileupload组件接收数据流,产生临时文件,然后绑定到注入的File属性,程序员只需要copy一下到自己的路径就行了,这里我不打算按照这个方式实现,
直接通过注解配置路径,自动上传。在这里依然要用到common-fileupload组件,它依赖common-io.jar。
当form上传文件时必须设置enctype="multipart/form-data",我们在MainServlet通过request的contentType来判断是否二进制请求流,假如为true,
则通过common-fileupload得到所有文件和文本对象,很自然而然的,代码就成了这样:
Map<String,Object[]> paramMap = new HashMap<String,Object[]>();
          String contentType=request.getContentType();
          
           //假如是带有上传,那么利用common fileupload封装表单
           if(contentType!= null && contentType.startsWith("multipart/form-data" )){
             //文件项工厂
              FileItemFactory factory = new DiskFileItemFactory();
              ServletFileUpload upload = new ServletFileUpload(factory);
              //得到所有表单项
              List<FileItem> items = upload.parseRequest(request);
              ...
           paramMap=fileParamMap.put(表单域名,FileItem[])
               
          }else{
           paramMap=request.getParameterMap(); 
        }


注意,这里的items不光是上传的文件数据,也包含文本数据。
现在paramMap里不仅装有文本数据,也有上传的文件流数据,下一步可以根据类别的不同做不同的注入了。但是这里有个问题,你仍然没有办法在自己编写的action中用
getParameter()或者getParameterMap()的方式得到提交的表单信息,所以这里需要改造一下。
     Java Web中提供一种装饰模式,让HttpServletRequest请求被处理之前改造自身。
    首先创建一个MulRequestWraper,继承HttpServletRequestWrapper,并且需要提供带HttpServletRequest参数的构造方法,然后重写一些重要的方法,代码如下:
public class MulRequestWraper extends HttpServletRequestWrapper {

     private Map<String, Object[]> paramMap = new HashMap<String, Object[]>();

     public MulRequestWraper(HttpServletRequest request) {
           super(request);

           try {
              FileItemFactory factory = new DiskFileItemFactory();
              ServletFileUpload upload = new ServletFileUpload(factory);
              List<FileItem> items = upload.parseRequest(request);
              Iterator<FileItem> iter = items.iterator();
            String  encoding=(request.getCharacterEncoding()==null ?"UTF-8" :request.getCharacterEncoding());
               while (iter.hasNext()) {
                   FileItem item = (FileItem) iter.next();

                   String fieldName = item.getFieldName();
                    if ( paramMap.containsKey(fieldName)) {
                        Object[] paramValue = paramMap.get(fieldName);

                         // 构造同类数组
                        Object[] paramValueTemp = (Object[]) Array.newInstance(
                                  paramValue[0].getClass(), paramValue. length + 1);
                         for ( int i = 0; i < paramValue.length; i++) {
                             paramValueTemp[i] = paramValue[i];
                        }

                         if (item.isFormField()) {
                             paramValueTemp[paramValueTemp. length - 1] = item
                                       .getString(encoding);
                        } else {
                              if (item.getSize() > 0) {
                                  paramValueTemp[paramValueTemp. length - 1] = item;
                             }
                        }

                         paramMap.put(fieldName, paramValueTemp);
                   } else {
                         if (item.isFormField()) {
                              paramMap.put(fieldName,
                                       new String[] { item.getString(encoding) });
                        } else {
                              if (item.getSize() > 0) {
                                   paramMap.put(fieldName, new FileItem[] { item });
                             }
                        }

                   }

              }
          } catch (Exception e) {
              e.printStackTrace();
          }

     }

     public String getParameter (String name) {
          Object[] values= paramMap.get(name);
           if(values. length>0){
               return (String)values[0];
          }
           return super.getParameter(name);
     }

     public String[] getParameterValues (String name) {
          Object[] values= paramMap.get(name);
           if(values!= null){
               return (String[])values;
          }
           return super.getParameterValues(name);
     }
     
     
     
     public Map getParameterMap () {
           paramMap.putAll( super.getParameterMap());
           return paramMap;
     }



因为HttpServletRequestWrapper类是实现HttpServletRequest接口的,所以可以在外面直接接收,外面的判断代码可以改造成如下:
//假如是带有上传,那么利用common fileupload封装表单
           if(contentType!= null && contentType.startsWith("multipart/form-data" )){
              request= new MulRequestWraper(request);
          }
          
          paramMap=request.getParameterMap();


逻辑更加清晰了,并且在后面的Action里都可以得到所有的表单信息(二进制的或者文本的)。

jdk5引入的注解不光可以简化各种配置信息,也逐渐成为了程序功能的一个部分
用注解实现上传配置的大致流程如下:
1,框架提供文件信息的Bean类:FilePo
2,自定义注解,(Field类型)
3,MainServlet通过转换FilePo,读取注解信息,实现上传
FilePo主要属性有:
      // 文件名 带后缀
     private String filename;

     // 相对于web的路径
     private String webpath;

     // 实际文件
     private File file;

     // 类型
     private String contentType;
     
     //大小 kb
     private double size;


,自定义注解的关键字是:@interface,代码如下
/**
 * 自动上传,这个注解用在FilePo对象上
 * @author 杜云飞
 *
 */
@Retention(RetentionPolicy.RUNTIME)
//表示本注解用在属性上
@Target(ElementType.FIELD )
public @interface UploadFile {
     
     /**
      * 上传的路径,默认上传到根目录
      */
   public String path() default "";
  
   /**
    * 文件名 为""表示用原文件名
    */
   public String name() default "";
}


暂时只用这几个属性,其实还可以加上传限制或者后缀等。
还记得之前我们做的TypeConverter么,现在咱们需要一个拦截FilePo的转换器,
新建FileConverter,继承TypeConverter,读取Field上的注解并且获取path和name信息,通过common-fileupload上传,就这么简单
具体实现如下:
public Object convertValue(Object value, Field field) {
           if(!(value instanceof FileItem) && !(value instanceof FileItem[])){
               return null;
          }
           UploadFile uf = field.getAnnotation(UploadFile.class );
          HttpServletRequest request = ActionContext.getRequest();
           try {

               if (uf != null) {
                   String path = uf.path();
                    logger.debug( "path: " + path);
                   //path =Utils.resolvePlaceHolder(path , ActionReplaceHolder.getInstance());
                   String name = uf.name();
                   String realpath = request.getRealPath(path);
                    logger.debug( "realpath: " + realpath);
                   File dsk = new File(realpath);
                    if (!dsk.exists()) {
                        dsk.mkdirs();
                   }
                   
                   FilePo[] fps= new FilePo[0];
                    if (field.getType().isArray()) {
                        
                         // 假如是数组,那么保存在同一个注释的文件夹里面,并且文件名为源文件的名字
                        FileItem[] fis = (FileItem[]) value;
                         for ( int i = 0; i < fis. length; i++) {
                             FileItem item = fis[i];
                              long filesize=item.getSize();
                             String filename = item.getName();
                              if(filename.indexOf(File. separator)>=0){
                                  filename=filename.substring(filename.lastIndexOf(File. separator)+1);
                             }
                              logger.debug( "filesize: "+filesize);
                             File file = new File(realpath + File.separator
                                      + filename);
                             
                             item.write(file);
                       
                             FilePo[] fps_temp=fps;
                             fps= new FilePo[fps.length+1];
                              for( int j=0;j<fps_temp.length;j++){
                                  fps[j]=fps_temp[j];
                             }
                             FilePo fp= new FilePo();
                             fp.setFile(file);
                             fp.setFilename(filename);
                             fp.setWebpath(path+filename);
                             fp.setContentType(item.getContentType());
                             fp.setSize(filesize/1024);
                             fps[fps. length-1]=fp;
                        }
                         if(fps!= null && fps. length>0){
                              return fps;
                        }
                         return null;
                  
                   } else {
                        FileItem[] items=(FileItem[])value;
                        FileItem item=items[0];
                         long filesize=item.getSize();
                         if(filesize==0){
                              return null;
                        }
                        String filename = item.getName();
                         if(!name.equals( "")){
                             String exts = filename.substring(filename
                                      .lastIndexOf( "."));
                             filename=name+exts ;
                        } else{
                             filename=filename.substring(filename.lastIndexOf(File. separator)+1);
                        }
                        File file = new File(realpath + File.separator
                                  + filename);
                        item.write(file);
                        FilePo fp= new FilePo();
                        fp.setFile(file);
                        fp.setFilename(filename);
                        fp.setWebpath(path+filename);
                        fp.setContentType(item.getContentType());
                        fp.setSize(filesize/1024);
                         return fp;
                   }
              } else {
                    // 没有注解的暂时不作任何处理
                    return null;
              }
              
          } catch (Exception ex) {
              ex.printStackTrace();
               logger.error( "上传模块出现错误:" +ex.getMessage());
          }

           return null;
     }


有时候我们希望path路径是从上下文资源里面取到的,而不是“死”的,比如从${requestScope.xxx},${sessionScope.xxx}或者${param.xxx}等地方获取需要的路径信息,注释的那段代码正是此功能的实现。
path=Utils.resolvePlaceHolder(path, ActionReplaceHolder.getInstance());(策略模式的运用)
ActionReplaceHolder是一个单例类,实现了ReplaceHolder接口,用于产生替换值,核心代码如下:
public String extract(String value) {
           if(value.indexOf( "requestScope.")!=-1){
              String prop=value.substring(value.indexOf("requestScope." )+"requestScope." .length());
               return (String)ActionContext.getRequest().getAttribute(prop);
          } else if(value.indexOf( "sessionScope.")!=-1){
              String prop=value.substring(value.indexOf("sessionScope." )+"sessionScope." .length());
               return (String)ActionContext.getRequest().getSession().getAttribute(prop);
          } else if(value.indexOf( "param.")!=-1){
              String prop=value.substring(value.indexOf("param." )+"param." .length());
               return ActionContext.getRequest().getParameter(prop);
          }
           return value;
     }


resolvePlaceHolder方法用于解析${}这类占位符,参考实现如下:
/**
      * 解析占位符具体操作
      * @param property
      * @return
      */
     public static String resolvePlaceHolder(String property,ReplaceHolder rh) {
           if ( property.indexOf( PLACEHOLDER_START ) < 0 ) {
               return property;
          }
          StringBuffer buff = new StringBuffer();
           char[] chars = property.toCharArray();
           for ( int pos = 0; pos < chars. length; pos++ ) {
               if ( chars[pos] == '$' ) {
                    if ( chars[pos+1] == '{' ) {
                        String propertyName = "";
                         int x = pos + 2;
                         for (  ; x < chars. length && chars[x] != '}'; x++ ) {
                             propertyName += chars[x];
                              if ( x == chars. length - 1 ) {
                                   throw new IllegalArgumentException( "unmatched placeholder start [" + property + "]" );
                             }
                        }
                        String systemProperty = rh.extract( propertyName );
                        buff.append( systemProperty == null ? "" : systemProperty );
                        pos = x + 1;
                         if ( pos >= chars. length ) {
                              break;
                        }
                   }
              }
              buff.append( chars[pos] );
          }
          String rtn = buff.toString();
           return isEmpty( rtn ) ? null : rtn;
     }


这也是hibernate中的标准解析方式。
详细可以参考本项目源码。
最后记得注册这个转换器,
convertMap .put( "org.love.po.FilePo", new FileConverter());

那么以后上传就可以直接注解FilePo属性就行了
 @UploadFile(path="uploadfiles/${param.folderPath}/" )
    private FilePo[] myimg;
   
    @UploadFile(path="uploadfiles/${sessionScope.folderPath}/" )
    private FilePo myimg0;
   
    @UploadFile(path="uploadfiles/xxx/")
    private FilePo myimg1;



当然,假如有人希望不用注解上传,也依然可以在Action里得到上传信息(因为之前request已经被包装过了,已经存有此类对象。)
比如你可以这样做:
FileItem[] fis = (FileItem[]) request.getParameterMap().get("myimg");
然后通过api自己实现上传。
是不是灰常方便啊。
到现在为止,我们控制层的大部分功能都已实现,但毕竟是一个演示项目,有很多代码需要优化,功能也有很多需要完善并且改进的地方,不过我觉得,只要思路清晰,整体框架没有偏离基准,添砖加瓦是很容易的事情。
下一章开始讲解业务逻辑容器。


By 阿飞哥 转载请说明
腾讯微博:http://t.qq.com/duyunfeiRoom
新浪微博:http://weibo.com/u/1766094735
原文地址:http://duyunfei.iteye.com/blog/1773715
分享到:
评论

相关推荐

    ASP.NET MVC 4高级编程 第4版PDF.rar

    ASP.NET MVC 是微软官方提出的一种Web开发框架,通过M是模型(model)-V视图(view)-C控制器(controller)l来设计创建Web应用程序。截至目前最新版本是MVC5,相对于之前的版本MVC5其可扩展性、易用性等方面都不很大的...

    springboot学习思维笔记.xmind

    @Controller在展现层(MVC→SpringMVC) 注入Bean的注解 @Autowired:Spring提供的注解 @Inject:JSR-330提供的注解 @Resource:JSR-250提供的注解 Java配置 @Configuration声明当前类是...

    Spring+MVC+Mybatis 书城项目

    Spring提供了很多功能,包括事务管理、依赖注入、AOP(面向切面编程)等。 Spring MVC: Spring MVC是Spring框架的一个模块,它实现了MVC(模型-视图-控制器)设计模式,用于构建Web应用。Spring MVC允许你将应用的...

    构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统

    1. ASP.NET MVC 基础 为什么选择MVC,假如您是个有梦想的人,那么你也应该喜欢极致的东西,而不顾一切付出 表现层的性能可以优化到极致 强迫学习前端语言html以及css、JavaScript 关注点分离 原生态url routing,...

    基于SSM框架个性化影片推荐系统.zip

    基于SSM框架的系统通常指的是利用Spring、Spring MVC和MyBatis这三个Java开发框架构建的应用程序。SSM是这三个框架的首字母缩写,它们合作提供了一个强大的技术栈,用于开发企业级的Web应用程序。以下是每个框架的...

    Spring.3.x企业应用开发实战(完整版).part2

    《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练使用...

    基于SSM的图书借阅系统

    首先,Spring框架为整个项目提供了一个强大的依赖注入(DI)和控制反转(IoC)基础。这使得开发人员能够更容易地管理项目的组件和配置。此外,Spring还提供了一套丰富的注解,用于简化常见的开发任务,如事务管理、数据...

    Spring 3 Reference中文

    1.1 依赖注入和控制反转.6 1.2 模块6 1.2.1 核心容器.7 1.2.2 数据访问/ 整合..7 1.2.3 Web ..8 1.2.4 AOP 和设备组件.8 1.2.5 测试.8 1.3 使用方案8 1.3.1 依赖管理和...

    weixin105文章管理系统+ssm(源码+部署说明+演示视频+源码介绍+lw).rar

    在weixin105文章管理系统中,Spring主要负责依赖注入和事务管理等功能。 SpringMVC:SpringMVC是Spring框架的一部分,它是一个用于构建Web应用程序的模型-视图-控制器(MVC)框架。在weixin105文章管理系统中,...

    weixin050高校体育场管理系统+ssm(源码+部署说明+演示视频+源码介绍+lw).rar

    使用Spring框架:Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,它提供了丰富的功能,如事务管理、安全性、依赖注入等,以支持企业级应用的开发。 使用SpringMVC框架:作为Spring的一部分,...

    Spring3.x企业应用开发实战(完整版) part1

    《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练使用...

    weixin133学生活动管理系统+ssm(源码+部署说明+演示视频+源码介绍+lw).rar

    Spring MVC:作为Web层的框架,它提供了一个模型-视图-控制器(MVC)设计模式的实现,有助于将业务逻辑、数据模型和用户界面分离,简化了Web层开发。 MyBatis:一个持久层框架,它简化了数据库操作,允许开发者通过...

    Spring API

    2. Spring 2.0和 2.5的新特性 2.1. 简介 2.2. 控制反转(IoC)容器 2.2.1. 新的bean作用域 2.2.2. 更简单的XML配置 2.2.3. 可扩展的XML编写 2.2.4. Annotation(注解)驱动配置 2.2.5. 在classpath中自动搜索组件...

    spring chm文档

    Spring Framework 开发参考手册 Rod Johnson Juergen Hoeller Alef Arendsen Colin Sampaleanu Rob Harrop Thomas Risberg Darren Davison Dmitriy Kopylenko Mark Pollack Thierry Templier Erwin ...

    Spring中文帮助文档

    11.2. 利用JDBC核心类控制JDBC的基本操作和错误处理 11.2.1. JdbcTemplate类 11.2.2. NamedParameterJdbcTemplate类 11.2.3. SimpleJdbcTemplate类 11.2.4. DataSource接口 11.2.5. SQLExceptionTranslator接口...

    Spring-Reference_zh_CN(Spring中文参考手册)

    标准MVC控制器代码 14.5.1.3. 把模型数据转化为XML 14.5.1.4. 定义视图属性 14.5.1.5. 文档转换 14.5.2. 小结 14.6. 文档视图(PDF/Excel) 14.6.1. 简介 14.6.2. 配置和安装 14.6.2.1. 文档视图定义 14.6.2.2. ...

    Spring 2.0 开发参考手册

    7.9. 使用“自动代理(autoproxy)”功能 7.9.1. 自动代理bean定义 7.9.2. 使用元数据驱动的自动代理 7.10. 使用TargetSources 7.10.1. 热交换目标源 7.10.2. 池化目标源 7.10.3. 原型目标源 7.10.4. ...

    AisMVC.zip

    我们团队开发web项目一般采用前后端分离,所以后端的的Controller层的功能仅仅只有提供ajax接口,页面集成后的跳转,过滤器和拦截器,所以我就想着自己写一款仿springmvc的mvc框架作为自己和团队以后的开发中小型项目的...

    weixin043培训机构客户管理系统的设计+ssm(源码+部署说明+演示视频+源码介绍+lw).rar

    "weixin043培训机构客户管理系统"是一个基于SSM(Spring, Spring MVC, MyBatis)框架开发的系统,用于帮助培训机构管理其客户信息和相关业务。以下是对该系统的技术介绍和功能概述: 技术介绍: Spring:作为企业...

    基于maven项目的SSM框架与layu前端框架的代码

    首先,需要说明的一点,AOP只是Spring的特性,它就像OOP一样是一种编程思想,并不是某一种技术,AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个...

Global site tag (gtag.js) - Google Analytics