该类等价 与XML中配置beans,至关于Ioc容器,它的某个方法头上若是注册了@Bean,就会做为这个Spring容器中的Bean,与xml中配置的bean意思同样。javascript
@Configuration注解的类必需使用<context:component-scanbase-package="XXX"/>
扫描.以下:html
@Configuration public class TestConfiguration { //在properties文件里配置 @Value("${wx_appid}") public String appid; @Bean public WxMpService wxMpService() { WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } }
测试:前端
package com.test.spring.support.configuration; public class TestMain { public static void main(String[] args) { //@Configuration注解的spring容器加载方式,用AnnotationConfigApplicationContext替换ClassPathXmlApplicationContext ApplicationContext context = new AnnotationConfigApplicationContext(TestConfiguration.class); //若是加载spring-context.xml文件: //ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml"); } }
定义一个MainConfig,用@Configuration注解,那MainConfig至关于xml里的beans,里面用@Bean注解的和xml里定义的bean等价,用<context:component-scanbase-package=”XXX”/>
扫描该类,最终咱们能够在程序里用@AutoWired或@Resource注解取得用@Bean注解的bean,和用xml先配置bean而后在程序里自动注入同样。目的是减小xml里配置。java
注: (1)、@Bean注解在返回实例的方法上,若是未经过@Bean指定bean的名称,则默认与标注的方法名相同;git
(2)、@Bean注解默认做用域为单例singleton做用域,可经过@Scope(“prototype”)设置为原型做用域;程序员
(3)、既然@Bean的做用是注册bean对象,那么彻底可使用@Component、@Controller、@Service、@Ripository等注解注册bean,固然须要配置@ComponentScan注解进行自动扫描。github
为了简化从properties里取配置,可使用@Value, 能够从properties文件中的配置值。web
在dispatcher-servlet.xml里引入properties文件。ajax
<context:property-placeholder location="classpath:test.properties" />
在程序里使用@Value:正则表达式
@Value("${wx_appid}") publicString appid;
即便给变量赋了初值也会以配置文件的值为准。
目前4种注解意思是同样,并无什么区别,区别只是名字不一样。使用方法:
<context:component-scanbase-package="XXX"/>
扫描被注解的类@Controller public class TestController { }
实现初始化和销毁bean以前进行的操做,只能有一个方法能够用此注释进行注释,方法不能有参数,返回值必需是void,方法须要是非静态的。
例如:
public class TestService { @PostConstruct public void init(){ System.out.println("初始化"); } @PreDestroy public void dostory(){ System.out.println("销毁"); } }
@PostConstruct:在构造方法和init方法(若是有的话)之间获得调用,且只会执行一次。 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,而且只会被服务器调用一次,相似于Serclet的inti()方法。被@PostConstruct修饰的方法会在构造函数以后,init()方法以前运行。
@PreDestory:注解的方法在destory()方法调用后获得执行。被@PreDestory修饰的方法会在服务器卸载Servlet的时候运行,而且只会被服务器调用一次,相似于Servlet的destroy()方法。被@PreConstruct修饰的方法会在destroy()方法以后运行,在Servlet被完全卸载以前。
网上拷贝的流程图:
引深一点,Spring 容器中的 Bean 是有生命周期的,Spring 容许在 Bean 在初始化完成后以及 Bean 销毁前执行特定的操做,经常使用的设定方式有如下三种:
1.经过实现InitializingBean/DisposableBean
接口来定制初始化以后/销毁以前的操做方法;
2.经过 <bean>
元素的init-method/destroy-method
属性指定初始化以后 /销毁以前调用的操做方法;
3.在指定方法上加上@PostConstruct
或@PreDestroy
注解来制定该方法是在初始化以后仍是销毁以前调用
但他们以前并不等价。即便3个方法都用上了,也有前后顺序.
Constructor > @PostConstruct >InitializingBean > init-method
自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将做为首选者,不然将抛出异常。
例如:
@Component public class Apple implements Fruit{ @Override public String hello() { return "我是苹果"; } } @Component @Primary public class Pear implements Fruit{ @Override public String hello(String lyrics) { return "梨子"; } } public class FruitService { //Fruit有2个实例子类,由于梨子用@Primary,那么会使用Pear注入 @Autowired private Fruit fruit; public String hello(){ return fruit.hello(); } }
用于指定该Bean是否取消预初始化,用于注解类,延迟初始化。
Autowired默认先按byType,若是发现找到多个bean,则,又按照byName方式比对,若是还有多个,则报出异常。
1.能够手动指定按byName方式注入,使用@Qualifier。
//经过此注解完成从spring配置文件中 查找知足Fruit的bean,而后按//@Qualifier指定pean @Autowired @Qualifier("pean") public Fruit fruit;
2.若是要容许null 值,能够设置它的required属性为false,如:
@Autowired(required=false) public Fruit fruit;
默认按 byName自动注入,若是找不到再按byType找bean,若是仍是找不到则抛异常,不管按byName仍是byType若是找到多个,则抛异常。
能够手动指定bean,它有2个属性分别是name和type,使用name属性,则使用byName的自动注入,而使用type属性时则使用byType自动注入。
@Resource(name=”bean名字”)
或
@Resource(type=”bean的class”)
这个注解是属于J2EE的,减小了与spring的耦合。
基于@Async标注的方法,称之为异步方法,这个注解用于标注某个方法或某个类里面的全部方法都是须要异步处理的。被注解的方法被调用的时候,会在新线程中执行,而调用它的方法会在原来的线程中执行。
application.xml形势的配置:
<!--扫描注解,其中包括@Async --> <context:component-scan base-package="com.test"/> <!-- 支持异步方法执行, 指定一个缺省的executor给@Async使用--> <task:annotation-driven executor="defaultAsyncExecutor" /> <!—配置一个线程执行器--> <task:executor id=" defaultAsyncExecutor "pool-size="100-10000" queue-capacity="10" keep-alive =”5”/>
@Async(“能够指定执行器id,也能够不指定”) public static void testAsyncVoid (){ try { //让程序暂停100秒,至关于执行一个很耗时的任务 System.out.println(“异常执行打印字符串”); Thread.sleep(100000); } catch (InterruptedException e) { e.printStackTrace(); } }
当在外部调用testAsync方法时即在新线程中执行,由上面<task: annotation-driven/>执行器去维护线程。
总结:先用context:component-scan去扫描注解,让spring能识别到@Async注解,而后task:annotation-driven去驱动@Async注解,并能够指定默认的线程执行器executor。那么当用@Async注解的方法或类获得调用时,线程执行器会建立新的线程去执行。
@Valid
网上一大片使用@Valid失效不能用的状况。为何呢?
@Valid必需使用在以@RequestBody接收参数的状况下。
使用ajax以POST方式提交数据,禁止用Fiddler以及浏览器直接访问的方式测试接口
用<mvc:annotation-driven />添加注解驱动。
@Valid是应用在javabean上的校验。
<!--引入jar包--> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.2.0.Final</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-jaxb-annotations</artifactId> <version>2.5.3</version> </dependency>
这些jar包是须要的。@Valid是使用hibernate validation,可参考下面介绍的@RequestBody
@Valid下后面紧跟BindingResult result,验证结果保存在result
例如:
@RequestMapping("/test") @RequestBody public String testValid(@Valid User user, BindingResult result){ if (result.hasErrors()){ List<ObjectError> errorList = result.getAllErrors(); for(ObjectError error : errorList){ System.out.println(error.getDefaultMessage()); } } return "test"; }
在入参User上添加了@Valid作校验,在User类里属性上实行实际的特定校验。
例如在User的name属性上加
@NotBlank private String name;
所有参数校验以下:
空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 没法查检长度为0的字符串 @NotBlank 检查约束字符串是否是Null还有被Trim的长度是否大于0,只对字符串,且会去掉先后空格. @NotEmpty 检查约束元素是否为NULL或者是EMPTY. Booelan检查 @AssertTrue 验证 Boolean 对象是否为 true @AssertFalse 验证 Boolean 对象是否为 false 长度检查 @Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围以内 @Length(min=, max=)验证注解的元素值长度在min和max区间内 日期检查 @Past 验证 Date 和 Calendar 对象是否在当前时间以前 @Future 验证 Date 和 Calendar 对象是否在当前时间以后 @Pattern 验证 String 对象是否符合正则表达式的规则 数值检查,建议使用在Stirng,Integer类型,不建议使用在int类型上,由于表单值为“”时没法转换为int,但能够转换为Stirng为"",Integer为null @Min(value=””) 验证 Number 和 String 对象是否大等于指定的值 @Max(value=””) 验证 Number 和 String 对象是否小等于指定的值 @DecimalMax(value=值) 被标注的值必须不大于约束中指定的最大值. 这个约束的参数是一个经过BigDecimal定义的最大值的字符串表示.小数存在精度 @DecimalMin(value=值) 被标注的值必须不小于约束中指定的最小值. 这个约束的参数是一个经过BigDecimal定义的最小值的字符串表示.小数存在精度 @Digits 验证 Number 和 String 的构成是否合法 @Digits(integer=,fraction=)验证字符串是不是符合指定格式的数字,interger指定整数精度,fraction指定小数精度。 @Range(min=, max=) 检查数字是否介于min和max之间. @Range(min=10000,max=50000,message="range.bean.wage") private BigDecimal wage; @Valid 递归的对关联对象进行校验, 若是关联对象是个集合或者数组,那么对其中的元素进行递归校验,若是是一个map,则对其中的值部分进行校验.(是否进行递归验证) @CreditCardNumber信用卡验证 @Email 验证是不是邮件地址,若是为null,不进行验证,算经过验证。 @ScriptAssert(lang=,script=, alias=) @URL(protocol=,host=,port=,regexp=, flags=)
@Validated
@Valid是对javabean的校验,若是想对使用@RequestParam方式接收参数方式校验使用@Validated
**使用@Validated的步骤:**
第一步:定义全局异常,让该全局异常处理器能处理全部验证失败的状况,并返回给前台失败提示数据。以下,该类不用在任何xml里配置。
import javax.validation.ValidationException; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @ControllerAdvice @Component public class GlobalExceptionHandler { @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { return new MethodValidationPostProcessor(); } @ExceptionHandler @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) public String handle(ValidationException exception) { System.out.println("bad request, " + exception.getMessage()); return "bad request, " + exception.getMessage(); } }
第二步。在XXController.java头上添加@Validated,而后在@RequestParam后台使用上面介绍的验证注解,好比@NotBlank,@Rank.
以下:
@Controller @RequestMapping("/test") @Validated public class TestController extends BaseController { @RequestMapping(value = "testValidated", method = RequestMethod.GET) @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) public Object testValidated(@RequestParam(value = "pk", required = true) @Size(min = 1, max = 3) String pk, @RequestParam(value = "age", required = false) @Range(min = 1, max = 3) String age) { try { return "pk:" + pk + ",age=" + age; } catch (Throwable t) { return buildFailure("消息列表查询失败"); } }
}
当传入非法参数时,会被全局处理器拦截到,(Spring切面编程方式),若是参数非法即刻给前台返回错误数据。
测试:http://127.0.0.1:8080/TestValidate/test/testValidated?pk=2&age=12
返回:
注意
@Valid是使用hibernateValidation.jar作校验
@Validated是只用springValidator校验机制使用
@Validated与@RequestBody结合使用时,在接口方法里要增长@Valid。例如:
public Object edit(@Valid @RequestBody AddrRo addrRo) {.....}
处理映射请求的注解。用于类上,表示类中的全部响应请求的方法都是以该地址做为父路径。有6个属性。
一、 value, method: value:指定请求的实际地址,指定的地址能够是URI Template 模式; method:指定请求的method类型, GET、POST、PUT、DELETE等; 好比:
@RequestMapping(value = "/testValid", method = RequestMethod.POST) @ResponseBody public Object testValid(@RequestBody @Valid Test test,BindingResult result, HttpServletRequest request, HttpServletResponse response) { XXX }
value的uri值为如下三类:
A) 能够指定为普通的具体值;如@RequestMapping(value ="/testValid")
B) 能够指定为含有某变量的一类值;如@RequestMapping(value="/{day}")
,配合@PathVariable使用
C) 能够指定为含正则表达式的一类值;如@RequestMapping(value="/{textualPart:[a-z-]+}.{numericPart:[\\d]+}")
能够匹配../chenyuan122912
请求。配合@PathVariable使用
二、 consumes,produces: consumes: 指定处理请求的提交内容类型(Content-Type),例如@RequestMapping(value = "/test", consumes="application/json")
处理application/json
内容类型
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
3 params、headers: params: 指定request中必须包含某些参数值是,才让该方法处理。
例如:
@RequestMapping(value = "/test", method = RequestMethod.GET, params="name=chenyuan") public void findOrd(String name) { // implementation omitted }
仅处理请求中包含了名为“name”,值为“chenyuan”的请求.
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
@RequestMapping(value = "/test", method = RequestMethod.GET, headers="Referer=www.baidu.com") public void findOrd(String name) { // implementation omitted }
仅处理request的header中包含了指定“Refer”请求头和对应值为“www.baidu.com”的请求
@GetMapping(value = "page")等价于@RequestMapping(value = "page", method = RequestMethod.GET)
@PostMapping(value = "page")等价于@RequestMapping(value = "page", method = RequestMethod.POST)
@RequestBody(required=true)
:有个默认属性required,默认是true,当body里没内容时抛异常。
引用jar包:
Spring相关jar包。
以及
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.5.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.3</version> </dependency>
dispatchServlet-mvc.xml配置 第一种,直接配置MappingJackson2HttpMessageCoverter:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"> </bean> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean> </property> </bean>
第二种:配置注解驱动<mvc:annotation-driven/>
就不用配置上面bean,默认会配好。
Ajax请求:
function testRequestBody() { var o = {"status":9}; jQuery.ajax({ type: "POST", url: "http://127.0.0.1:8080/TestValidate/test/testValid", xhrFields:{ withCredentials:true }, data: JSON.stringify(o), contentType: "application/json", dataType: "json", async: false, success:function (data) { console.log(data); }, error: function(res) { console.log(res); } }); }
后台XXXcontroller.java:
@RequestMapping(value="/ testValid ",method=RequestMethod.POST) @ResponseBody public Object setOrderInfo(@RequestBody InfoVO infoVO,HttpServletRequest request, HttpServletResponse response){ InfoVO cVo = getInfoVo(infoVO); return "success"; }
也能够接收复杂参数:
@RequestMapping(value="/ testValid ",method=RequestMethod.POST) @ResponseBody public Object setOrderInfo(@RequestBody List<Map<String,Object>> param,HttpServletRequest request, HttpServletResponse response){ return "success"; }
注意下面几个要点:
必需使用POST方式提交参数,须要使用ajax方式请求,用Fiddler去模拟post请求不能实现。
Content-Type必需是application/json
是Cross-Origin ResourceSharing(跨域资源共享)的简写
做用是解决跨域访问的问题,在Spring4.2以上的版本可直接使用。在类上或方法上添加该注解
controller设置请求地址:
import java.util.HashMap; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** * */ @RestController @RequestMapping(value = "/api", method = RequestMethod.POST) public class ApiController { @CrossOrigin(origins = "http://172.16.71.27:8080") @RequestMapping(value = "/get") public HashMap<String, Object> get(@RequestParam String name) { HashMap<String, Object> map = new HashMap<String, Object>(); map.put("title", "hello world"); map.put("name", name); return map; } }
ajax访问:
<script> $(function() { $('#title').click(function() { // alert('点击了'); $.ajax({ url : "http://localhost:8081/api/get", type : "POST", data : { name : "测试" }, success : function(data, status, xhr) { console.log(data); alert(data.name); } }); }); }) </script>
特别注意:
1.必定要在某类 或者某方法上 添加相似 method = RequestMethod.POST 的属性
eg: @RequestMapping(value = "/api", method = RequestMethod.POST)
2:在某个方法上添加@CrossOrigin 注解时 origins 属性必定要写ip号 若是输入localhost有时会出现403错误
eg:@CrossOrigin(origins = "http://172.16.71.27:8080")
做用是提取和解析请求中的参数。@RequestParam支持类型转换,类型转换目前支持全部的基本Java类型
@RequestParam(value="number", required=false,defaultValue =0) String number
将请求中参数为number映射到方法的number上。required=false表示该参数不是必需的,请求上可带可不带。
@PathVariable:处理request uri部分,当使用@RequestMapping URI template 样式映射时, 即someUrl/{paramId}
, 这时的paramId可经过 @Pathvariable注解绑定它传过来的值到方法的参数上
例如:
@Controller @RequestMapping("/owners/{a}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{b}") public void findPet(@PathVariable("a") String a,@PathVariable String b, Model model) { // implementation omitted } }
匹配正则表达式
不少时候,须要对URL变量进行更加精确的定义,例如-用户名只可能包含小写字母,数字,下划线,咱们但愿:
除了简单地定义{username}变量,还能够定义正则表达式进行更精确的控制,定义语法是{变量名:正则表达式}[a-zA-Z0-9_]+
一个正则表达式,表示只能包含小写字母,大写字母,数字,下划线。如此设置URL变量规则后,不合法的URL则不会被处理,直接由SpringMVC框架返回404Not Found。
@RequestMapping("/user/{username:[a-zA-Z0-9_]+}/blog/{blogId}")
总结
处理request header部分的注解
//将头部信息绑定到方法参数上: @RequestMapping("/test") public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive")long keepAlive) { //... } //将cookie里JSESSIONID绑定到方法参数上 @RequestMapping("/test") public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie){ //... }
@ModelAttribute
在Spring mvc中,注解@ModelAttribute是一个很是经常使用的注解,其功能主要在两方面:
通常开发中,第一种用法居多
@SessionAttributes
@sessionattributes注解应用到Controller上面,能够将Model中的属性同步到session当中。
@Controller @RequestMapping("/Demo.do") @SessionAttributes(value={"attr1","attr2"}) public class Demo { @RequestMapping(params="method=index") public ModelAndView index1() { ModelAndView mav = new ModelAndView("index.jsp"); mav.addObject("attr1", "attr1Value"); mav.addObject("attr2", "attr2Value"); return mav; } @RequestMapping(params="method=index2") public ModelAndView index2(@ModelAttribute("attr1")String attr1, @ModelAttribute("attr2")String attr2) { ModelAndView mav = new ModelAndView("success.jsp"); return mav; } }
index1方法返回一个ModelAndView 其中包括视图index.jsp 和 两个键值放入model当中,在没有加入@sessionattributes注解的时候,放入model当中的键值是request级别的。
如今由于在Controller上面标记了@SessionAttributes(value={"attr1","attr2"}) 那么model中的attr1,attr2会同步到session中,这样当你访问index 而后在去访问index2的时候也会获取这俩个属性的值。
当须要清除session当中的值得时候,咱们只须要在controller的方法中传入一个SessionStatus的类型对象 经过调用setComplete方法就能够清除了。
@RequestMapping(params="method=index3") public ModelAndView index4(SessionStatus status) { ModelAndView mav = new ModelAndView("success.jsp"); status.setComplete(); return mav; }
在controller中获取sessionAttributes的只有两种方式。
1、public ModelAndView index2(@ModelAttribute("attr1")String attr1, @ModelAttribute("attr2")String attr2)
2、public ModelAndView index2(ModelMap map)
这俩种方式的区别是,若是使用@ModelAttribute属性获取值,而且@SessionAttributes注解当中还设置了该属性,当属性为null时会跑出异常,由于这几行代码。
配置bean的做用域。
@Controller @RequestMapping("/test") @Scope("prototype") public class TestController { }
@Scope 注解 目前用到的大多就是 单列 singleton 和多例 prototype 。默认是单例模式,即@Scope("singleton"),
一般使用状况: @Service 直接默认就行,因此不用加@Scope注解 顺便提醒一下,既然是单例。。。属性的值但是下一次访问还在哦 ,@Controller 控制层用多例 @Scope("prototype") 毕竟咱们不想两个用户的访问结果互相串。若是有其它具体需求再具体分析
singleton:单例,即容器里只有一个实例对象。
prototype:多对象,每一次请求都会产生一个新的bean实例,Spring不没法对一个prototype bean的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,由程序员负责销毁该对象,无论何种做用域,容器都会调用全部对象的初始化生命周期回调方法,而对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用
这里牵扯到单利模式的线程安全的设计。
咱们知道sping ioc注入的bean;通常都是无状态的【dao,service等,这种不会牵涉到值或者状态改变的状况】,也就是在多线程下能够基本保证线程安全的;可是,有些状况下多是有状态的;有状态的bean ,是要注意线程安全的;spring是有考虑到这方面的需求的;prototype 原型类型,应运而生;
1.1 xml配置:
<bean id="virtualProductService" class="com.san.mpa.service.VirtualProductService" scope="prototype">
1.2 注解配置:
@Scope("prototype") public class CustInfoList extends HttpServlet {}
2.1有状态会话bean (多对象) :
每一个用户有本身特有的一个实例,在用户的生存期内,bean保持了用户的信息,即“有状态”;一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每一个用户最初都会获得一个初始的bean。
2.2无状态会话bean (单例) :
bean一旦实例化就被加进会话池中,各个用户均可以共用。即便用户已经消亡,bean 的生命期也不必定结束,它可能依然存在于会话池中,供其余用户调用。因为没有特定的用户,那么也就不能保持某一用户的状态,因此叫无状态bean。但无状态会话bean 并不是没有状态,若是它有本身的属性(变量),那么这些变量就会受到全部调用它的用户的影响,这是在实际应用中必须注意的。
使用prototype后可使并发调用在不一样的实例中完成,不会产生线程安全的问题。
request:对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
web.xml增长以下配置:
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> session:该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。也要在web.xml配置以下代码: <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
global session:做用不大,可不用管他。
测试:
@Controller @RequestMapping("/demo/lsh/ch5") @Scope("prototype") public class MultViewController { private static int st = 0; //静态的 private int index = 0; //非静态 @RequestMapping("/test") public String test() { System.out.println(st++ + " | " + index++); return "/lsh/ch5/test"; } } 屡次请求:/test 后运行结果: 单例的@Scope("singleton"): 0 | 0 1 | 1 2 | 2 3 | 3 4 | 4 改成多例的@Scope("prototype"): 0 | 0 1 | 0 2 | 0 3 | 0 4 | 0 最佳实践:定义一个非静态成员变量时候,则经过注解@Scope("prototype"),将其设置为多例模式。
@ResponseStatus用于修饰一个类或者一个方法。
例如:
首先定义一个异常类
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="出现了错误") public class UserException extends RuntimeException{ XXXXX }
当访问此接口,某处抛出UserException时,则会把value和reason返回给前端。
@RequestMapping("/testResponseStatus") public String testResponseStatus(int i){ if(i==0) throw new UserNotMatchException(); return "hello"; }
前端显示异常信息:
修饰方法:
若是@ResponseStatus做用于方法上,不管方法执行成功与否都会返回到自定义的异常页面。
@ControllerAdvice @Component public class GlobalExceptionHandler { @Bean public MethodValidationPostProcessor methodValidationPostProcessor() { return new MethodValidationPostProcessor(); } @ExceptionHandler @ResponseBody @ResponseStatus(value=HttpStatus.BAD_REQUEST,reason="哈哈") public String handle(ValidationException exception) { System.out.println("bad request, " + exception.getMessage()); return "bad request, " + exception.getMessage(); } }
结果以下:
正如上面所说,该方法获得调用,不管是否抛异常,都会把value和reason添加到response里。
总结:@ResponseStatus是为了在方法或类获得调用时将指定的code和reason添加到response里返前端,就像服务器常给咱们报的404错误同样,咱们能够本身指定高逼格错误提示。
官方解释是:It is typically used todefine@ExceptionHandler,@InitBinder, and@ModelAttribute methods that apply to all@RequestMapping methods
意思是:即在@ControllerAdvice注解的类中。把使用了@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到全部(全局)的使用了 @RequestMapping注解的方法。很是简单,不过只有当使用@ExceptionHandler最有用,另外两个用处不大。
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(SQLException.class) @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR,reason=”sql查询错误”) @ResponseBody public ExceptionResponse handleSQLException(HttpServletRequest request, Exception ex) { String message = ex.getMessage(); return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), message); } }
即表示让Spring捕获到全部被@RequestMapping注解的方法所抛出的SQLException异常,并交由这个被注解的handleSQLException方法处理,同时使用@ResponseStatus指定了code和reason写到response上,返回给前端。
注意:ControllerAdvice只能捕获到全局Controller范围内的,以外的异常就没法捕获了,如filter中抛出异常的话,ControllerAdvice是没法捕获的。
元注解是指注解的注解,好比咱们看到的ControllerAdvice注解定义以下。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { XXX }
@Retention: 定义注解的保留策略:
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时没法得到,
@Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,在运行时能够经过反射获取到
@Target:定义注解的做用目标:
@Target(ElementType.TYPE) //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR) //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包
由以上的源码能够知道,他的elementType 能够有多个,一个注解能够为类的,方法的,字段的等等
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类能够继承父类中的该注解
好比自定义注解是:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface DBTable { public String name() default ""; }
表示该注解只能用在接口、类、枚举、注解上。该注解会被编译到class里可经过反射获得。
@Cacheable能够标记在一个方法上,也能够标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类全部的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用一样的参数来执行该方法时能够直接从缓存中获取结果,而不须要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。须要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable能够指定三个属性,value、key和condition。
1.value属性指定Cache名称
value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪一个Cache上的,对应Cache的名称。其能够是一个Cache也能够是多个Cache,当须要指定多个Cache时其是一个数组。
@Cacheable("cache1")//Cache是缓存在cache1上的 public User find(Integer id) { return null; } @Cacheable({"cache1", "cache2"})//Cache是缓存在cache1和cache2上的 public User find(Integer id) { return null; }
2. 使用key属性自定义key
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当咱们没有指定该属性时,Spring将使用默认策略生成key。咱们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
自定义策略是指咱们能够经过Spring的EL表达式来指定咱们的key。这里的EL表达式可使用方法参数及它们对应的属性。使用方法参数时咱们能够直接使用“#参数名”或者“#p参数index”。下面是几个使用参数做为key的示例。
@Cacheable(value="users", key="#id") public User find(Integer id) { return null; } @Cacheable(value="users", key="#p0") public User find(Integer id) { returnnull; } @Cacheable(value="users", key="#user.id") public User find(User user) { returnnull; } @Cacheable(value="users", key="#p0.id") public User find(User user) { returnnull; }
除了上述使用方法参数做为key以外,Spring还为咱们提供了一个root对象能够用来生成key。经过该root对象咱们能够获取到如下信息。
属性名称 | 描述 | 示例 |
---|---|---|
methodName | 当前方法名 | #root.methodName |
method | 当前方法 | #root.method.name |
target | 当前被调用的对象 | #root.target |
targetClass | 当前被调用的对象的class | #root.targetClass |
args | 当前方法参数组成的数组 | #root.args[0] |
caches | 当前被调用的方法使用的Cache | #root.caches[0].name |
当咱们要使用root对象的属性做为key时咱们也能够将“#root”省略,由于Spring默认使用的就是root对象的属性。如:
@Cacheable(value={"users", "xxx"}, key="caches[1].name") public User find(User user) { returnnull; }
3.condition属性指定发生的条件
有的时候咱们可能并不但愿缓存一个方法全部的返回结果。经过condition属性能够实现这一功能。condition属性默认为空,表示将缓存全部的调用情形。其值是经过SpringEL表达式来指定的,当为true时表示进行缓存处理;当为false时表示不进行缓存处理,即每次调用该方法时该方法都会执行一次。以下示例表示只有当user的id为偶数时才会进行缓存。
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0") public User find(User user) { System.*out*.println("find user by user " + user); return user; }
@CacheEvict是用来标注在须要清除缓存元素的方法或类上的。当标记在一个类上时表示其中全部的方法的执行都会触发缓存的清除操做。@CacheEvict能够指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性相似。即value表示清除操做是发生在哪些Cache上的(对应Cache的名称);key表示须要清除的是哪一个key,如未指定则会使用默认策略生成的key;condition表示清除操做发生的条件。下面咱们来介绍一下新出现的两个属性allEntries和beforeInvocation。
1. allEntries属性
allEntries是boolean类型,表示是否须要清除缓存中的全部元素。默认为false,表示不须要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候咱们须要Cache一下清除全部的元素,这比一个一个清除元素更有效率。
@CacheEvict(value="users", allEntries=true) public void delete(Integer id) { System.*out*.println("delete user by id: " + id); }
2. beforeInvocation属性
清除操做默认是在对应方法成功执行以后触发的,即方法若是由于抛出异常而未能成功返回时也不会触发清除操做。使用beforeInvocation能够改变触发清除操做的时间,当咱们指定该属性值为true时,Spring会在调用该方法以前清除缓存中的指定元素。
@CacheEvict(value="users", beforeInvocation=true) public void delete(Integer id) { System.*out*.println("delete user by id: " + id); }
其实除了使用@CacheEvict清除缓存元素外,当咱们使用Ehcache做为实现时,咱们也能够配置Ehcache自身的驱除策略,其是经过Ehcache的配置文件来指定的。