一、使用 @Controller 定义一个 Controller 控制器
在SpringMVC 中,控制器Controller 处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中定义一个Controller,只需使用@Controller 注解标记一个类是Controller 。然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射,这样的Controller 就能被外界访问到。
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。但是@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。
对应表现层的Bean,使用@Controller注解标识UserController后,UserController类就是一个SpringMVC Controller 对象,就表示要把UserController交给Spring容器管理,在配置文件里配置< context:component-scan base-package = "com.app.controller" >,这样Spring就知道在哪可以找到标记为@Controller 的Controller 控制器了。在Spring容器中会存在一个名字为“userController”的SpringMVC Controller 对象,这个名字是根据UserController类名来取的,如果@Controller不指定其 value【@Controller】,则默认的bean名字为这个类的类名首字母小写,如果指定value【@Controller(value="UserController")】或者【@Controller("UserController")】,则使用value作为bean的名字。
二、使用 @RequestMapping 来映射 Request 请求与处理器
使用@RequestMapping 来映射URL 到控制器类,或者是到Controller 控制器的处理方法上。当@RequestMapping 标记在Controller 类上的时候,里面使用@RequestMapping 标记的方法的请求地址都是相对于类上的@RequestMapping 而言的;当Controller 类上没有标记@RequestMapping 注解时,方法上的@RequestMapping 都是绝对路径。
@PathVariable当使用@RequestMapping URI template 样式映射时,即 XXXUrl/{paramId}, 这时的paramId可通过 @Pathvariable注解把它传过来的值绑定到方法的参数上。
@RequestBody该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上 ,再把HttpMessageConverter返回的对象数据绑定到 controller中方法的参数上
@ResponseBody该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区
(一)使用 URI 模板
URI 模板就是在URI 中给定一个变量,然后在映射的时候动态的给该变量赋值。使用@PathVariable 来标记一个Controller 的处理方法参数,表示该参数的值将使用URI 模板中对应的变量的值来赋值。
(二)使用 @RequestParam 绑定 HttpServletRequest 请求参数到控制器方法参数
三、使用@Autowired自动装配
自动装配,Spring会自动将我们标记为@Autowired的元素装配好。
1)在某一时刻Spring调用了 Bean工厂 的 getBean(beanName) 方法。beanName可能是simpleController,或者simpleService,simpleDao,顺序没关系(因为后面会有依赖关系的处理)。我们假设simpleController吧。
2)getBean方法首先会调用Bean工厂中定义的getSingleton(beanName)方法,来判断是否存在该名字的bean单例,若果存在则返回,方法调用结束。
3)否则,Spring会检查是否存在父工厂,如果有则返回,方法调用结束。
4)否则,Spring 会检查该bean 定义(BeanDefinition实例,用来描述Bean结构,上篇文章讲到过,component-scan 扫描后,就是将beanDefinition实例放入Bean工厂,此时Bean还没有被实例化。)是否有依赖关系,如果有,执行1)步,获取依赖的bean实例。
5)否则,Spring会尝试创建这个bean实例,创建实例前,Spring会检查确定调用的构造器,并实例化该Bean。
6)实例化完成后,Spring会调用Bean工厂的populateBean方法来填充bean实例的属性,也就是我们前面提到的自动转配了。populateBean方法便是调用了BeanPostProcessor实例来完成属性元素的自动装配工作。
7)在元素装配过程中,Spring会检查被装配的属性是否存在自动装配的其他属性,然后递归调用getBean方法,直到所有@Autowired的元素都被装配完成。如在装配simpleController中的simpleService属性时,发现SimpleServiceImpl实例中存在@Autowired属性simpleDao,然后调用getBean(simpleDao)方法,同样会执行1)-7)整个过程。所以可以看成一个递归过程。
8)装配完成后,Bean工厂会将所有的bean实例都添加到工厂中来。
注:我们知道Spring MVC是多线程单实例的MVC框架,就是说,对于同一个Controller,只会生成一个实例来处理所有的请求,因此bean实例只会实例化一次,并被存放在工厂中,以供其他请求使用。
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
这个注解可以用到构造器,变量域,方法,注解类型上。文档上这样描述:将一个构造器,变量域,setter方法,config方法标记为被Spring DI 工具自动装配。换句话说,我们视图从bean 工厂中获取一个bean时,Spring会自动为我们装配该bean中标记为@Autowired的元素,而无需我们手动完成。
BeanPostProcessor
实际的注入装配动作是由BeanPostProcessor执行的,BeanPostProcessor来执行自动装配,并且默认情况下使用AutowiredAnnotationBeanPostProcessor实现类完成。
它的定义如下:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
BeanPostProcessor就是一个回调接口,定义了两个方法声明,一个是实例化前被调用,一个是实例化后被调用。它的实现类是AutowiredAnnotationBeanPostProcessor,用来自动装配注解的变量域,setter方法和任意的config方法。