SpringMVC

whileinsist 2017-03-16

http://elf8848.iteye.com/blog/875830
概述:
1. Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。使用 Spring 可插入的 MVC 架构,可以选择是使用内置的 Spring Web 框架还是 Struts 这样的 Web 框架。通过策略接口,Spring 框架是高度可配置的,而且包含多种视图技术,例如 JavaServer Pages(JSP)技术、Velocity、Tiles、iText 和 POI。Spring MVC 框架并不知道使用的视图,所以不会强迫您只使用 JSP 技术。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色,这种分离让它们更容易进行定制。Spring3 MVC结构简单,应了那句话简单就是美,而且他强大不失灵活,性能也很优秀。

Struts2也是比较优秀的MVC构架,优点非常多比如良好的结构。但这里想说的是缺点,Struts2由于采用了值栈、OGNL表达式、struts2标签库等,会导致应用的性能下降。Struts2的多层拦截器、多实例action性能都很好。

优缺点

1、易于同其它View框架(Titles等)无缝集成,采用IOC便于测试。
2、它和tapestry一样是一个纯正的servlet系统,这也是它和tapestry相比 struts所没有的优势。而且框架本身有代码,而且看起来也不费劲比较简单可以理解。
3、Spring3 MVC是一个典型的教科书式的mvc构架,学习难度小于Struts2,Struts2用不上的多余功能太多。

4、Spring3 MVC很容易就可以写出性能优秀的程序,Struts2要处处小心才可以写出性能优秀的程序(指MVC部分)

5、Spring3 MVC的灵活是你无法想像的,Spring的扩展性有口皆碑,Spring3 MVC当然也不会落后,不会因使用了MVC框架而感到有任何的限制。

简析spring mvc 工作原理
(1 )启动服务器,根据web.xml 的配置加载前端控制器(也称总控制器) DispatcherServlet 。在加载
时、会完成一系列的初始化动作。
(2 )根据servlet 的映射请求(上面的helloWorld 实例中针对.do 请求),并参照“控制器配置文
件”(即spmvc-servlet.xml 这样的配置)文件,把具体的请求分发给特定的后端控制器进行处理(比
如上例会分发给HelloWorld 控制器进行处理)
(3 )后端控制器调用相应的逻辑层代码,完成处理并返回视图对象( ModelAndView )给前端处理器。
(4 )前端控制器根据后端控制器返回的ModelAndView 对象,并结合一些配置(后面有说明),返回一
个相应的页面给客户端。

这种Front Controller 模式常应用在主流的web 框架中,比如典型的struts1.x 框架.Front
Controller 模式:所有请求先交给一个前端处理器(总控处理器)处理,然后前端处理器会参照一些配
置文件再把具体的请求交给相应的后端处理器。后端处理器调用逻辑层代码,并根据逻辑返回相应的视
图对象给前端控制器。然后前端控制器再根据视图对象返回具体的页面给客户端(提示:和spring mvc
一样,在struts1.x 中前端控制器是Servlet, 而在struts2 中前端控制器是Filter )。

概述FrontController 模式:前端控制器预处理并分发请求给后端控制器,后端控制器进行真正的逻辑处理并返回
视图对象,前端控器器根据视图对象返回具体页面给客户端。


SSH
<action path="/GetPersonList" scope="request"   type="org.springframework.web.struts.DelegatingActionProxy">   </action>
<!-- 指定GetPersonList处理的代码,和注入实现业务的代码 --> 
<bean name="/GetPersonList" class="cn.base.GetPersonListAction">   <property name="getPersonList" ref=" getPersonListServices"></property>   </bean>

BeanNameUrlHandlerMapping 默认控制器

视图技术
(1)ModelAndView所表示的视图名很关键,视图解析链会依此名来选择一个正确的视图。
(2)不同的视图解析器解析视图规则不相同,但是他们实质都是实现了ViewResolver接口,并会依赖于配置View对象来处理请求的准备工作
(3)spring 内置了多种视图解析器AbstractCachingViewResolver、XmlViewResolver、ResourceBundleViewResolver、UrlBasedViewResolver、InternalResourceViewResolver
     VelocityViewResolver 、FreeMarkerViewResolver
(4)Spring支持多个视图解析器一起使用,即视图解析链。 视图解析链包含一系列视图解析器,更方便开发人员处理某些特殊请求,比如在特定情况下重新定义某些视图(为某个视图解析器使用order,可以改变此视图解析器在整个视图解析链中的解析顺序:order值越大,它在整个视图解析链中的顺序越靠前,即它越会被优先选作为视图解析器)。



• 注册注解处理器
• 方式一:bean
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>
• 方式二: 命名空间<context:annotation-config /> 将隐式地向 Spring 容器注册AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、PersistenceAnnotationBeanPostProcessor 以及equiredAnnotationBeanPostProcessor 这 4 个 BeanPostProcessor。
• 方式三: 命名空间<context:component-scan />如果要使注解工作,则必须配置component-scan ,实际上不需要再配置annotation-config。
base-package 属性指定了需要扫描的类包,类包及其递归子包中所有的类都会被处理。还允许定义过滤器将基包下的某些类纳入或排除。

•还允许定义过滤器将基包下的某些类纳入或排除 Spring 支持以下4 种类型的过滤方式:
• 注解 org.example.SomeAnnotation 将所有使用SomeAnnotation 注解的类过滤出来
• 类名指定 org.example.SomeClass 过滤指定的类
• 正则表达式 com.kedacom.spring.annotation.web..* 通过正则表达式过滤一些类
• AspectJ 表达式 org.example..*Service+ 通过AspectJ 表达式过滤一些类
以正则表达式为例,我列举一个应用实例:
<context:component-scan base-package="com.casheen.spring.annotation">  
    <context:exclude-filter type="regex" expression="com\.casheen\.spring\.annotation\.web\..*" /> 
</context:component-scan> 


@Component   泛指组件, 定义Bean 
@Controller 声明Action组件          添加了@Controller注解注解的类就可以担任控制器(Action)的职责
@Service    声明Service组件    @Service("myMovieLister")
@Repository 声明Dao组件
@RequestMapping("/person") –声明URL  请求映射     
@RequestParam      参数
@PathVariable 获取变量    
@Cacheable     声明一个方法的返回值应该被缓 存
@CacheFlush     声明一个方法是清空缓存的触发器
@PostConstruct      在方法上加上注解@PostConstruct ,这个方法就会在Bean 初始化之后被Spring 容器执行(注:Bean 初始化包括,实例化Bean ,并装配Bean 的属性(依赖注入))。
@PreDestroy     在方法上加上注解@PreDestroy ,这个方法就会在Bean 被销毁前被Spring 容器执行。
@SessionAttributes 
@ModelAttribute
@InitBinder
@Required 负责检查一个bean在初始化时其声明的 set方法是否被执行,
@Autowired 用于注入,(srping提供的) 默认按类型装配
@Transactional( rollbackFor={Exception.class}) 事务管理
@ResponseBody  spring的配置文件中要有这一行 <mvc:annotation-driven />,才能使用到spring内置支持的json转换
@Scope("prototype")   设定bean的作用域

JSR-250
@Resource  用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName")
@PostConstruct 和 @PreDestroy


@Component  注释定义 Bean  bean 的ID 默认为类名称开头字母小写
@Component("boss")
public class Boss {}
@Component 有一个可选的入参,用于指定 Bean 的名称,在 Boss 中,我们就将 Bean 名称定义为“boss”。一般情况下,Bean 都是 singleton 的,需要注入 Bean 的地方仅需要通过 byType 策略就可以自动注入了,所以大可不必指定 Bean 的名称。

在使用 @Component 注释后,Spring 容器必须启用类扫描机制以启用注释驱动 Bean 定义和注释驱动 Bean 自动注入的策略。Spring 2.5 对 context 命名空间进行了扩展,提供了这一功能,请看下面的配置:
<context:component-scan/> 扫描指定的包中的类上的注解  <context:component-scan base-package="com.baobaotao"/>
值得注意的是 <context:component-scan/> 配置项不但启用了对类包进行扫描以实施注释驱动 Bean 定义的功能,同时还启用了注释驱动自动注入的功能(即还隐式地在内部注册了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此当使用 <context:component-scan/> 后,就可以将 <context:annotation-config/> 移除了。
默认情况下通过 @Component 定义的 Bean 都是 singleton 的,如果需要使用其它作用范围的 Bean,可以通过 @Scope 注释来达到目标,如以下代码所示:
@Scope("prototype")
@Component("boss")
这样,当从 Spring 容器中获取 boss Bean 时,每次返回的都是新的实例了。

@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。

要使用spring mvc中的@Controller注解,就必须要配置<mvc:annotation-driven />,否则org.springframework.web.servlet.DispatcherServlet无法找到控制器并把请求分发到控制器。

@RequestMapping(params = "method=geList") --即处理/person.do? method=geList方法
value     :@RequestMapping(value = "/anno.do")  =@RequestMapping("/anno.do")==@RequestMapping("/anno")
method     :@RequestMapping(value = "/anno.do", method = RequestMethod.GET) 此配置说明只有GET请求才能访问anno.do
headers     :@RequestMapping(value = "/pets", method = RequestMethod.POST, headers="content-type=text/*")   符合头信息的时候才调用 
params。:@RequestMapping(value = "/anno.do",params = "method=geList")   此配置说明只有附带正确的请求参数才能访问


@Controller
@RequestMapping("/bbtForum") :类级别的基路径请求
public class BbtForumController {
@RequestMapping(params = "method=listBoardTopic")
     public String listBoardTopic(int topicId,User user) {}
}
如果我们使用以下的 URL 请求:
http://localhost/bbtForum.do?method=listBoardTopic&topicId=1&userId=10&userName=tom
topicId URL 参数将绑定到 topicId 入参上,而 userId 和 userName URL 参数将绑定到 user 对象的 userId 和 userName 属性中。和 URL 请求中不允许没有 topicId 参数不同,虽然 User 的 userId 属性的类型是基本数据类型,但如果 URL 中不存在 userId 参数,Spring 也不会报错,此时 user.userId 值为 0 。如果 User 对象拥有一个 dept.deptId 的级联属性,那么它将和 dept.deptId URL 参数绑定。


@RequestParam
• 参数绑定说明 :注解改变默认的绑定规则
@RequestParam("id")
http://localhost/bbtForum.do?method=listBoardTopic&id=1&userId=10&userName=tom
listBoardTopic(@RequestParam("id")int topicId,User user) 中的 topicId 绑定到 id 这个 URL 参数, 那么可以通过对入参使用 @RequestParam注解来达到目的

@PathVariable
@PathVariable是用来对指定请求的URL路径里面的变量ID  @RequestMapping(value = "/{id}") {id}在这个请求的URL里就是个变量,可以使用@PathVariable来获取
@PathVariable和@RequestParam的区别就在于:@RequestParam用来获得静态的URL请求入参


@Cacheable 和@CacheFlush
              • @Cacheable :声明一个方法的返回值应该被缓 存
              例如:@Cacheable(modelId = "testCaching")
              • @CacheFlush :声明一个方法是清空缓存的触发器
                 例如:@CacheFlush(modelId = "testCaching")
              • 说明
               要配合缓存处理器使用,参考: http://hanqunfeng.javaeye.com/blog/603719


@Autowired 与 @Resource的区别
1、 @Autowired与@Resource都可以用来装配bean. 都可以写在字段上,或写在setter方法上。

2、 @Autowired默认按类型装配(这个注解是属业spring的),默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:
@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用,如下:
Java代码
字段上:
@Autowired() @Qualifier("baseDao")   
private BaseDao baseDao;  
set方法上
@Autowired
public void setBaseDao(BaseDao baseDao) { this.baseDao = baseDao;}
构造器上:
@Autowired
public void Class(BaseDao baseDao) {        this.baseDao = baseDao;    }


3、@Resource(这个注解属于J2EE(JSR-250)的),默认安装名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,默认取字段名进行安装名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
Java代码
@Resource(name="baseDao")   
private BaseDao baseDao;  
@Resource(name="baseDao") private BaseDao baseDao;
注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时,@Resourve注解会回退到按类型装配。但一旦指定了name属性,就只能按照名称装配了
推荐使用:@Resource注解在字段上,这样就不用写setter方法了,并且这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。

@Autowired 根据bean 类型从spring 上线文中进行查找,注册类型必须唯一,否则报异常。与@Resource 的区别在于,
@Resource 允许通过bean 名称或bean 类型两种方式进行查找
@Autowired(required=false) 表示,如果spring 上下文中没有找到该类型的bean 时, 才会使用new SoftPMServiceImpl();


@PostConstruct 和 @PreDestroy
Spring 容器中的 Bean 是有生命周期的,Spring 允许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操作,您既可以通过实现 InitializingBean/DisposableBean 接口来定制初始化之后 / 销毁之前的操作方法,也可以通过 <bean> 元素的 init-method/destroy-method 属性指定初始化之后 / 销毁之前调用的操作方法。
JSR-250 为初始化之后/销毁之前方法的指定定义了两个注释类,分别是 @PostConstruct 和 @PreDestroy,这两个注释只能应用于方法上。标注了 @PostConstruct 注释的方法将在类实例化后调用,而标注了 @PreDestroy 的方法将在类销毁之前调用。
我们知道,不管是通过实现 InitializingBean/DisposableBean 接口,还是通过 <bean> 元素的 init-method/destroy-method 属性进行配置,都只能为 Bean 指定一个初始化 / 销毁的方法。但是使用 @PostConstruct 和 @PreDestroy 注释却可以指定多个初始化 / 销毁方法,那些被标注 @PostConstruct 或@PreDestroy 注释的方法都会在初始化 / 销毁时被执行。



@SessionAttributes
• 说明
顾名思义SessionAttributes就是保存session的,SessionAttributes使用方法很简单,@SessionAttributes 允许指定多个属性。你可以通过字符串数组的方式指定多个属性,如 @SessionAttributes({“attr1”,”attr2”})。此外,@SessionAttributes 还可以通过属性类型指定要 session 化的 ModelMap 属性,如 @SessionAttributes(types = User.class),当然也可以指定多个类,如 @SessionAttributes(types = {User.class,Dept.class}),还可以联合使用属性名和属性类型指定:@SessionAttributes(types = {User.class,Dept.class},value={“attr1”,”attr2”})。
@SessionAttributes 只能声明在类上,而不能声明在方法上。
• 例如
@SessionAttributes("currUser") // 将ModelMap 中属性名为currUser 的属性
@SessionAttributes({"attr1","attr2"})
@SessionAttributes(types = User.class)
@SessionAttributes(types = {User.class,Dept.class})
@SessionAttributes(types = {User.class,Dept.class},value={"attr1","attr2"})

@ModelAttribute
通过注解@ModelAttribute可以获得之前保存的值。
http://www.cnblogs.com/edwardlauxh/archive/2011/03/11/1981639.html


@InitBinder
• 说明
如果希望某个属性编辑器仅作用于特定的 Controller ,
可以在 Controller 中定义一个标注 @InitBinder 注解的方法,
可以在该方法中向 Controller 了注册若干个属性编辑器

• 例如
@InitBinder
public void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}

@Scope("prototype")
1.singleton:singleton类型的bean定义,在一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例
2.prototype:scope为prototype的bean,容器在接受到该类型的对象的请求的时候,会每次都重新生成一个新的对象给请求方,虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不在拥有当前对象的引用,请求方需要自己负责当前对象后继生命周期的管理工作,包括该对象的销毁。也就是说,容器每次返回请求方该对象的一个新的实例之后,就由这个对象“自生自灭”了。
3.request ,session和global session
http://hi.baidu.com/edgar108/item/7b02b308a158ad35a2332a02

         



注释配置和 XML 配置的适用场合
是否有了这些 IOC 注释,我们就可以完全摒除原来 XML 配置的方式呢?答案是否定的。有以下几点原因:
• 注释配置不一定在先天上优于 XML 配置。如果 Bean 的依赖关系是固定的,(如 Service 使用了哪几个 DAO 类),这种配置信息不会在部署时发生调整,那么注释配置优于 XML 配置;反之如果这种依赖关系会在部署时发生调整,XML 配置显然又优于注释配置,因为注释是对 Java 源代码的调整,您需要重新改写源代码并重新编译才可以实施调整。
• 如果 Bean 不是自己编写的类(如 JdbcTemplate、SessionFactoryBean 等),注释配置将无法实施,此时 XML 配置是唯一可用的方式。
• 注释配置往往是类级别的,而 XML 配置则可以表现得更加灵活。比如相比于 @Transaction 事务注释,使用 aop/tx 命名空间的事务配置更加灵活和简单。
所以在实现应用中,我们往往需要同时使用注释配置和 XML 配置,对于类级别且不会发生变动的配置可以优先考虑注释配置;而对于那些第三方类以及容易发生调整的配置则应优先考虑使用 XML 配置。Spring 会在具体实施 Bean 创建和 Bean 注入之前将这两种配置方式的元信息融合在一起。

Servlet拦截匹配规则可以自已定义,拦截哪种URL合适?
当映射为@RequestMapping("/user/add")时,为例:
1、拦截*.do、*.htm, 例如:/user/add.do
这是最传统的方式,最简单也最实用。不会导致静态文件(jpg,js,css)被拦截。
2、拦截/,例如:/user/add
可以实现现在很流行的REST风格。很多互联网类型的应用很喜欢这种风格的URL。
弊端:会导致静态文件(jpg,js,css)被拦截后不能正常显示。想实现REST风格,事情就是麻烦一些。后面有解决办法还算简单。
3、拦截/*,这是一个错误的方式,请求可以走到Action中,但转到jsp时再次被拦截,不能访问到jsp。



AOP:
切面编程,就是在你项目原有的功能基础上,通过AOP去添加新的功能,这些功能是建立在原有功能的基础上的,而且原有的功能并不知道你已经添加了新的功能。比如说,你去ATM取钱,取钱是一个功能,取完钱后向你的手机发送一条取钱信息,这就是新加的功能。
AOP就是在某一个类或方法执行前后打个标记,声明在执行到这里之前要先执行什么,执行完这里之后要接着执行什么。插入了新的执行方法。

1.切面(aspect):要实现的交叉功能,是系统模块化的一个切面或领域。如日志记录。 
2.连接点:应用程序执行过程中插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段。 
3.通知:切面的实际实现,他通知系统新的行为。如在日志通知包含了实现日志功能的代码,如向日志文件写日志。通知在连接点插入到应用系统中。
4.切入点:定义了通知应该应用在哪些连接点,通知可以应用到AOP框架支持的任何连接点。
5.引入:为类添加新方法和属性。 
6.目标对象:被通知的对象。既可以是你编写的类也可以是第三方类。 
7.代理:将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变。 
8.织入:将切面应用到目标对象从而创建一个新代理对象的过程。织入发生在目标对象生命周期的多个点上:编译期:切面在目标对象编译时织入.这需要一个特殊的编译器.
        类装载期:切面在目标对象被载入JVM时织入.这需要一个特殊的类载入器.运行期:切面在应用系统运行时织入.

相关推荐