Spring简介注解2

80 阅读9分钟

1. @Resource

默认按 byName自动注入,如果找不到再按byType找bean,如果还是找不到则抛异常,无论按byName还是byType如果找到多个,则抛异常。

可以手动指定bean,它有2个属性分别是name和type,使用name属性,则使用byName的自动注入,而使用type属性时则使用byType自动注入。

@Resource(name=”bean名字”)

@Resource(type=”bean的class”)

这个注解是属于J2EE的,减少了与spring的耦合。

2. @Async

java里使用线程用3种方法:

  1. 继承Thread,重写run方法

  2. 实现Runnable,重写run方法

  3. 使用Callable和Future接口创建线程,并能得到返回值。

前2种简单,第3种方式特别提示一下,例子如下:

class MyCallable implements Callable<Integer> {
    private int i = 0;
    // 与run()方法不同的是,call()方法具有返回值
    @Override
    public Integer call() {
        int sum = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            sum += i;
        }
        return sum;
    }
}

main方法:

public static void main(String[] args) {
        Callable<Integer> myCallable = new MyCallable();    // 创建MyCallable对象
        FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 30) {
                Thread thread = new Thread(ft);   //FutureTask对象作为Thread对象的target创建新的线程
                thread.start();                      //线程进入到就绪状态
            }
        }
        System.out.println("主线程for循环执行完毕..");
        try {
            int sum = ft.get();            //取得新创建的新线程中的call()方法返回的结果
            System.out.println("sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
}

而使用@Async可视为第4种方法。基于@Async标注的方法,称之为异步方法,这个注解用于标注某个方法或某个类里面的所有方法都是需要异步处理的。被注解的方法被调用的时候,会在新线程中执行,而调用它的方法会在原来的线程中执行。

application.xml形势的配置:

第一步配置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”/>

参数解读:

配置参数:

id:当配置多个executor时,被@Async("id")指定使用;也被作为线程名的前缀。

pool-size:

core size:最小的线程数,缺省:1

max size:最大的线程数,缺省:Integer.MAX_VALUE

queue-capacity:当最小的线程数已经被占用满后,新的任务会被放进queue里面,当这个queue的capacity也被占满之后,pool里面会创建新线程处理这个任务,直到总线程数达到了max size,这时系统会拒绝这个任务并抛出TaskRejectedException异常(缺省配置的情况下,可以通过rejection-policy来决定如何处理这种情况)。缺省值为:Integer.MAX_VALUE

keep-alive:超过core size的那些线程,任务完成后,再经过这个时长(秒)会被结束掉

rejection-policy:当pool已经达到max size的时候,如何处理新任务

ABORT(缺省):抛出TaskRejectedException异常,然后不执行DISCARD:不执行,也不抛出异常

DISCARD_OLDEST:丢弃queue中最旧的那个任务

CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行

第二步在类或方法上添加@Async,当调用该方法时,则该方法即是用异常执行的方法单独开个新线程执行。

@Async(“可以指定执行器id,也可以不指定”)
    public static void testAsyncVoid (){
        try {
            //让程序暂停100秒,相当于执行一个很耗时的任务
    System.out.println(“异常执行打印字符串”);
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

当在外部调用testAsync方法时即在新线程中执行,由上面执行器去维护线程。

总结:先用context:component-scan去扫描注解,让spring能识别到@Async注解,然后task:annotation-driven去驱动@Async注解,并可以指定默认的线程执行器executor。那么当用@Async注解的方法或类得到调用时,线程执行器会创建新的线程去执行。

上面方法是无返回值的情况,还有异常方法有返回值的例子。

@Async
public Future<String> testAsyncReturn () {  
    System.out.println("Execute method asynchronously - "  
      + Thread.currentThread().getName());  
    try {  
        Thread.sleep(5000);  
        return new AsyncResult<String>("hello world !!!!");  
    } catch (InterruptedException e) {  
        //  
    }  
    return null;  
}

返回的数据类型为Future类型,接口实现类是AsyncResult.

调用方法如下:

public void test(){
    Future<String> future = cc.testAsyncReturn();  
    while (true) {  ///这里使用了循环判断,等待获取结果信息  
        if (future.isDone()) {  //判断是否执行完毕  
            System.out.println("Result from asynchronous process - " + future.get());  
            break;  
        }  
        System.out.println("Continue doing something else. ");  
        Thread.sleep(1000);  
    }  
}

通过不停的检查Future的状态来获取当前的异步方法是否执行完毕

参考文章

编程的方式使用@Async:

@Configuration  
@EnableAsync  
public class SpringConfig {

    private int corePoolSize = 10;  
    private int maxPoolSize = 200; 
    private int queueCapacity = 10;  
    private String ThreadNamePrefix = "MyLogExecutor-";  
@Bean  
public Executor logExecutor() {  
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
    executor.setCorePoolSize(corePoolSize);  
    executor.setMaxPoolSize(maxPoolSize);  
    executor.setQueueCapacity(queueCapacity);  
    executor.setThreadNamePrefix(ThreadNamePrefix);  
    // rejection-policy:当pool已经达到max size的时候,如何处理新任务  
    // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行  
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
    executor.initialize();  
    return executor;  
    }
}

3.@Named

@Named和Spring的@Component功能相同。@Named可以有值,如果没有值生成的Bean名称默认和类名相同。比如

@Named

public class Person

@Named("cc")

public class Person

4. @Inject

使用@Inject需要引用javax.inject.jar,它与Spring没有关系,是jsr330规范。

与@Autowired有互换性。

5. @Singleton

只要在类上加上这个注解,就可以实现一个单例类,不需要自己手动编写单例实现类。

6.@Valid,@Valided

@Valid

网上一大片使用@Valid失效不能用的情况。为什么呢?

1.@Valid必需使用在以@RequestBody接收参数的情况下。

2.使用ajax以POST方式提示数据,禁止用Fiddler以及浏览器直接访问的方式测试接口

3.用添加注解驱动。

4.@Valid是应用在javabean上的校验。

<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>

这些jar包是需要的。@Valid是使用hibernate validation的时候使用,可参数下面介绍的@RequestBody

6.@Valid下后面紧跟BindingResult result,验证结果保存在result

例如:

@RequestMapping("/test")
    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校验机制使用

gitHub下载地址

@Validated与@RequestBody结合使用时,在接口方法里要增加@Valid。例如:

public Object edit(@Valid @RequestBody AddrRo addrRo) {.....}

7.@RequestBody

@RequestBody(required=true)

:有个默认属性required,默认是true,当body里没内容时抛异常。

application/x-www-form-urlencoded:窗体数据被编码为名称/值对。这是标准的编码格式。这是默认的方式 multipart/form-data:窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。二进制数据传输方式,主要用于上传文件

注意:必需使用POST方式提交参数,需要使用ajax方式请求,用Fiddler去模拟post请求不能。

引用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>

第二种: 就不用配置上面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";
    }

开发时,不是报415,就是400错误,头都大了。还是细节没做到位,注意下面几个要点:

Content-Type必需是application/json

需要jackson-databind.jar

要配置或直接配置bean

XXXController.jar在post方式接收数据

最最重要的,使用ajax以post方式请求。不能用Fiddler模拟,不然会出错。