`
AngelAndAngel
  • 浏览: 234303 次
  • 性别: 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
分享到:
评论

相关推荐

    spring-mvc注解详情

    注解在Spring MVC中扮演着核心角色,它们提供了声明式编程,使得开发者能够以更简洁的方式配置和控制应用程序的行为。 1. **@Controller**:这个注解标记一个类作为Spring MVC的控制器。控制器类处理来自客户端的...

    spring-webmvc-struts.jar aspectjweaver.jar

    标题中的"spring-webmvc-struts.jar"和"aspectjweaver.jar"是两个重要的Java库文件,它们在Java Web开发中扮演着核心角色,特别是对于基于Spring框架的应用程序。现在让我们详细了解一下这两个库以及它们在实际开发...

    一个改进版的spring-mvc-showcase

    Controller和Service通常通过@Autowired注解实现依赖注入,而DAO层则可能使用JDBC、MyBatis或Hibernate等技术进行数据操作。 此外,Spring MVC还支持Model-View-Controller模式中的视图解析,例如使用JSP、...

    spring-webmvc-portlet.rar

    在portlet环境中,Spring Web MVC可以作为portlet的后端处理逻辑,提供强大的控制层功能。整合步骤通常包括以下几步: 1. **配置portlet**:创建portlet的XML配置文件,定义portlet的类、显示模式和事件处理方法。 ...

    MVC注解Spring-Struts2Spring2hibernate3

    首先,Spring框架是Java企业级应用的核心框架,它提供了依赖注入(DI)和面向切面编程(AOP)等功能。在本项目中,Spring使用注解如@Service、@Repository和@Controller来标记服务层、数据访问层和控制层的类,简化...

    spring-mvc-mybatis

    在现代Java Web开发中,Spring MVC和MyBatis是两个非常重要的框架,它们分别负责控制层和数据访问层的处理。Spring MVC作为Spring框架的一部分,提供了强大的MVC(Model-View-Controller)架构支持,而MyBatis则是一...

    spirng-mvc-framework

    Spring MVC 框架是Java开发中的一个核心框架,它为构建基于模型-视图-控制器(MVC)模式的Web应用程序提供了强大的支持。Spring框架的整体设计基于IoC(Inversion of Control,控制反转)和AOP(Aspect Oriented ...

    spring-mvc-lib

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为构建模型-视图-控制器(MVC)架构的应用程序提供了强大的支持。在"spring-mvc-lib"这个压缩包中,我们很可能找到了Spring MVC框架运行所必需的一些库文件。虽然...

    the-mvc-spring-and-web-study.rar_Java spring mvc_The Web_mvc_spr

    Spring框架提供了强大的工具来管理对象的生命周期和相互之间的依赖关系,而AOP则允许开发者在不修改代码的情况下添加新的功能或行为,如日志记录、事务管理等。 在标签中,我们看到了"java_spring_mvc"、"the_web...

    Spring-MVC-model

    在Spring 3.2.6版本中,该框架已经相当成熟和完善,为开发者提供了丰富的功能和高度的灵活性。与Hibernate 4集成,可以方便地处理数据库操作,实现数据持久化。 1. **Spring MVC 框架核心组件** - **...

    mvc-ssm-crud.rar

    SSM框架是由Spring、Spring MVC和MyBatis三个开源组件组成的Java Web开发框架。这个名为“mvc-ssm-crud.rar”的压缩包文件显然包含了使用SSM搭建一个基础CRUD(Create, Read, Update, Delete)操作应用的教程或示例...

    smbms-mvc-master.zip

    4. **@Service/Repository**:服务层和数据访问层的注解,分别用于处理业务逻辑和数据库交互。 5. **Model**:模型对象,通常对应数据库中的实体,用于封装和传递数据。 6. **ViewResolver**:视图解析器,负责根据...

    spring mvc 自动注入+dwr

    **Spring MVC 自动注入** Spring MVC 是 Spring 框架的一部分,主要用于构建 Web 应用程序的控制器层。它提供了一种模型-视图-控制器(MVC)架构,使得开发者能够将业务逻辑、数据处理和用户界面清晰地分离。在 ...

    spring mvc-mybatis-lib

    Spring MVC还支持数据绑定、拦截器、异常处理和国际化等功能,极大地提高了开发效率。 MyBatis,是一个轻量级的持久层框架,它将SQL语句与Java代码解耦,使得开发者可以直接编写SQL来操作数据库,同时保持代码的...

    ssh注解开发案例

    SSH(Struts2 + Spring + Hibernate)是一个经典的Java Web开发框架,它整合了Struts2的MVC设计模式、Spring的依赖注入以及Hibernate的对象关系映射功能,为开发者提供了高效且灵活的开发环境。SSH注解开发是SSH框架...

    Maven-Spring-Spring-MVC-MyBatis-MySQL

    2. **Spring**:Spring是一个全面的Java企业级应用框架,它提供了依赖注入(DI)和面向切面编程(AOP)等功能。在SSM整合中,Spring作为核心容器,管理着其他组件,如Spring MVC和MyBatis的bean实例。 3. **Spring ...

    spring-mvc 注解方式xml配置

    在这里,`@RestController`注解表示这是一个RESTful风格的控制器,它结合了`@Controller`和`@ResponseBody`的功能,意味着该控制器方法的返回值会直接转换为HTTP响应体。`@RequestMapping("/model1")`注解定义了一个...

    spring mvc-spring boot-mybatis

    Spring MVC 通过DispatcherServlet处理请求,支持多种视图技术如JSP、Thymeleaf等,并提供了拦截器、数据绑定、格式化和验证等功能。 2. Spring Boot Spring Boot 是基于 Spring Framework 的一个快速开发工具,...

    基于注解Spring MVC环境搭建

    这篇博文(尽管描述为空,但提供了链接)很可能是关于创建一个基本的Spring MVC项目并使用注解来管理控制器、视图解析和其他关键组件的教程。 1. **Spring MVC简介** Spring MVC是Spring框架的一部分,它提供了...

    AngelRM_MVC-master.zip

    它简化了Web应用的开发,通过依赖注入(DI)和面向切面编程(AOP)提供了强大的功能。SpringMVC的核心组件包括DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver等。 **3. DispatcherServlet:** ...

Global site tag (gtag.js) - Google Analytics