Spring那些注解

如果不记得Spring有哪些注解,我们可以想办法将它们打印出来。

引入工具包:

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

通过这个反射工具包,我们可以以一行代码打印出所有Spring框架的注解:

import org.reflections.Reflections;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;

@Component
public class ScanAnnotationRunner implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        new Reflections("org.springframework")
                .getSubTypesOf(Annotation.class)
                .stream()
                .map(clazz -> clazz.getName())
                .sorted()
                .forEach(System.out::println);
    }
}

下面我们将来一一介绍那些重要的注解的含义及如何使用。

@Autowired、@Resource

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

@Autowired是Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired,只按照byType注入。

@Resource默认按照byName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。

@Resource装配顺序:

①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。

@Configurable

Spring只能对容器创建的对象进行依赖注入,而通过使用@Configurable,可以对new出来的对象进行依赖注入。

@Lookup

@Qualifier

@Value

@CacheConfig

@CacheEvict

@CachePut

@Cacheable

@Caching

@EnableCaching

@Bean

@Bean是一个方法级别上的注解,主要用在@Configuration注解的类里,也可以用在@Component注解的类里。添加的bean的id为方法名,也可以自定义名称。

@ComponentScan

@ComponentScan 的作用就是根据定义的扫描路径,把符合扫描规则的类装配到spring容器中。

(1) basePackages与value:  用于指定包的路径,进行扫描。

(2) basePackageClasses:用于指定某个类的包的路径进行扫描。

(3) nameGenerator: bean的名称的生成器。

(4) useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测, 默认是true。

(5) includeFilters:包含的过滤条件。

  • FilterType.ANNOTATION:按照注解过滤
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型
  •  FilterType.ASPECTJ:使用ASPECTJ表达式
  •  FilterType.REGEX:正则
  • FilterType.CUSTOM:自定义规则

(6) excludeFilters: 排除的过滤条件,用法和includeFilters一样   

示例:

@Configuration
@ComponentScan(value = "com.demo.spring",
        useDefaultFilters = false,
        includeFilters = {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {HelloController.class})
        }
)
public class MyConfig {

}

@ComponentScan.Filter

是@ComponentScan注解类中的子注解(内部注解),可以指定一些过滤规则:

  • FilterType.ANNOTATION:按照注解注入
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型注入
  • FilterType.ASPECTJ:使用ASPECTJ表达式
  • FilterType.REGEX:使用正则指定
  • FilterType.CUSTOM:使用自定义规则, 实现TypeFilter接口,重写match方法。

@ComponentScans

是@ComponentScan注解的集合,里面可以指定多个@ComponentScan注解,扫描多个包路径。

@Conditional

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

@Conditional可以标注在类和方法上。

如何定义:需要实现Condition接口,并重写方法来自定义match规则。返回true则注入bean,false则不注入。

@Configuration

从Spring3.0,@Configuration用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。

@Configuration注解的配置类有如下要求:

  • @Configuration不可以是final类型;
  • @Configuration不可以是匿名类;
  • 嵌套的configuration必须是静态类。

@DependsOn

spring容器载入bean顺序是不确定的,spring框架没有约定特定顺序逻辑规范。但spring保证如果A依赖B(如beanA中有@Autowired B的变量),那么B将先于A被加载。但如果beanA不直接依赖B,我们如何让B仍先加载呢?

此时就可以使用Spring @DependsOn控制bean加载顺序。

说明:Spirng容器自己会管理bean的生命周期和bean实例化的顺序,但是我们仍然可以根据我们自己的需求进行定制。我可以可以选择使用SmartLifeCycle接口,也可以用@DependsOn注解来管理初始化顺序。

@EnableAspectJAutoProxy

表示开启AOP代理自动配置,如果配@EnableAspectJAutoProxy表示使用cglib进行代理对象的生成;设置@EnableAspectJAutoProxy(exposeProxy=true)表示通过aop框架暴露该代理对象,aopContext能够访问.
从@EnableAspectJAutoProxy的定义可以看得出,它引入AspectJAutoProxyRegister.class对象,该对象是基于注解@EnableAspectJAutoProxy注册一个AnnotationAwareAspectJAutoProxyCreator,该对象通过调用AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);注册一个aop代理对象生成器。

@Import

@Import只能用在类上 ,@Import通过快速导入的方式实现把实例加入spring的IOC容器中。

@Import的三种用法主要包括:

第①种:直接填class数组方式

@Import({ 类名.class , 类名.class... })
public class TestDemo {

}

对应的import的bean都将加入到spring容器中,这些在容器中bean名称是该类的全类名 ,比如com.yc.类名。

第②种:ImportSelector方式【重点】

这种方式的前提就是一个类要实现ImportSelector接口,假如我要用这种方法,目标对象是Myclass这个类,分析具体如下:

创建Myclass类并实现ImportSelector接口:

public class Myclass implements ImportSelector {
//既然是接口肯定要实现这个接口的方法
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[0];
    }
}

分析实现接口的selectImports方法中的:

  • 返回值: 就是我们实际上要导入到容器中的组件全类名【重点 】
  • 参数: AnnotationMetadata表示当前被@Import注解给标注的所有注解信息【不是重点】

需要注意的是selectImports方法可以返回空数组但是不能返回null,否则会报空指针异常!

演示步骤:

① 创建Myclass类并实现ImportSelector接口,这里用于演示就添加一个全类名给其返回值

public class Myclass implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        return new String[]{"com.yc.Test.TestDemo3"};
    }
}

② 编写TestDemo 类,并标注上使用ImportSelector方式的Myclass类

@Import({Myclass.class})
public class TestDemo {
       
}

③ 编写打印容器中的组件测试类

/**
 * 打印容器中的组件测试
 */
public class AnnotationTestDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(TestDemo.class);  //这里的参数代表要做操作的类

        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : beanDefinitionNames){
            System.out.println(name);
        }

    }
}

第③种:ImportBeanDefinitionRegistrar方式

同样是一个接口,类似于第二种ImportSelector用法,相似度80%,只不过这种用法比较自定义化注册,具体如下:

第一步:创建Myclass2类并实现ImportBeanDefinitionRegistrar接口

public class Myclass2 implements ImportBeanDefinitionRegistrar {
//该实现方法默认为空
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
      
    }
}

参数分析:

  • 第一个参数:annotationMetadata 和之前的ImportSelector参数一样都是表示当前被@Import注解给标注的所有注解信息
  • 第二个参数表示用于注册定义一个bean

第二步:编写代码,自定义注册bean

public class Myclass2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //指定bean定义信息(包括bean的类型、作用域...)
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(TestDemo4.class);
        //注册一个bean指定bean名字(id)
        beanDefinitionRegistry.registerBeanDefinition("TestDemo4444",rootBeanDefinition);
    }
}

第三步:编写TestDemo 类,并标注上使用ImportBeanDefinitionRegistrar方式的Myclass2类

@Import({Myclass2.class})
public class TestDemo {

}

总结:

  • @Import({ 要导入的容器中的组件 } ):容器会自动注册这个组件,id默认是全类名
  • ImportSelector:返回需要导入的组件的全类名数组,springboot底层用的特别多【重点 】
  • ImportBeanDefinitionRegistrar:手动注册bean到容器

以上三种用法方式皆可混合在一个@Import中使用,特别注意第一种和第二种都是以全类名的方式注册,而第三中可自定义方式。
@Import注解本身在springboot中用的很多,特别是其中的第二种用法ImportSelector方式在springboot中使用的特别多,尤其要掌握!

@ImportResource

用于导入 Spring 的 xml 配置文件,让该配置文件中定义的 bean 对象加载到Spring容器中。

@Lazy

Spring IoC (ApplicationContext) 容器一般都会在启动的时候实例化所有单实例 bean 。如果我们想要 Spring 在启动的时候延迟加载 bean,即在调用某个 bean 的时候再去初始化,那么就可以使用 @Lazy 注解。

true 表示使用 延迟加载, false 表示不使用,false 纯属多余,如果不使用,不标注该注解就可以了。

@Primary

当一个接口有2个不同实现时,使用@Autowired注解时会报org.springframework.beans.factory.NoUniqueBeanDefinitionException异常信息。

@Primary可以理解为默认优先选择,不可以同时设置多个,内部实质是设置BeanDefinition的primary属性。

@Profile

指定组件在哪个环境(一般有dev/test/master, 名称自定义)的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件。

可以这样激活指定环境:

AnnotationConfigApplicationContext ctx=new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("upper");

@PropertySource

@PropertySouce是spring3.1开始引入的基于java config的注解。

通过@PropertySource注解将properties配置文件中的值存储到Spring的 Environment中,Environment接口提供方法去读取配置文件中的值,参数是properties文件中定义的key值。

用法①:@PropertySource和@Value

@Configuration
@PropertySource("classpath:jdbc.properties")
public class PropertiesWithJavaConfig {
   @Value(${jdbc.driver})
   private String driver;
   @Value(${jdbc.url})
   private String url;
   @Value(${jdbc.username})
   private String username;
   @Value(${jdbc.password})
   private String password;
   //要想使用@Value 用${}占位符注入属性,这个bean是必须的,这个就是占位bean,另一种方式是不用value直接用Envirment变量直接getProperty('key')  
   @Bean
   public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
      return new PropertySourcesPlaceholderConfigurer();
   }
}

用法②:通过Environment设置

@Configuration
@PropertySource("classpath:jdbc.properties")
public class PropertiesWithJavaConfig {

   @Autowired
    private Environment env;

}

@PropertySources

Spring 4中,Spring提供了一个新的注解——@PropertySources,从名字就可以猜测到它是为多配置文件而准备的。

@PropertySources({
@PropertySource("classpath:config.properties"),
@PropertySource("classpath:db.properties")
})
public class AppConfig {
    //something
}

@Scope

@Scope注解是springIoc容器中的一个作用域,在 Spring IoC 容器中具有以下几种作用域:

  • singleton单例模式 -- 全局有且仅有一个实例
  • prototype原型模式 -- 每次获取Bean的时候会有一个新的实例
  • request -- request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
  • session -- session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
  • globalsession -- global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义

几乎90%以上的业务使用singleton单实例就可以,所以spring默认的类型也是singleton,singleton虽然保证了全局是一个实例,对性能有所提高,但是如果实例中有非静态变量时,会导致线程安全问题,共享资源的竞争

当设置为prototype时:每次连接请求,都会生成一个bean实例,也会导致一个问题,当请求数越多,性能会降低,因为创建的实例,导致GC频繁,gc时长增加。

@EventListener

EventListener事件触发和监听器可以对代码解耦,在一些与业务无关的,通用的操作方法,我们可以把它设计成事件监听器,像通知,消息这些模块都可以这样设计。

@Order

注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响。

  • 注解可以作用在类(接口、枚举)、方法、字段声明(包括枚举常量);
  • 注解有一个int类型的参数,可以不传,默认是最低优先级;
  • 通过常量类的值我们可以推测参数值越小优先级越高;

@DateTimeFormat

在SpringMVC中Controller中方法参数为Date类型想要限定请求传入时间格式时,可以通过@DateTimeFormat来指定,但请求传入参数与指定格式不符时,会返回400错误。

如果在Bean属性中有Date类型字段,想再序列化转为指定格式时,也可用@DateTimeFormat来指定想要的格式。如下:

@NumberFormat

@NumberFormat是用来验证输入的数字格式。

@NonNull

@NonNull 在方法或构造函数的参数上使用,生成一个空值检查语句。用于指明所修饰的参数,字段或方法的值不可以为null。它是JSR 305(缺陷检查框架)的注解,是告诉编译器这个域不可能为空,当代码检查(静态检查)有空值时会给出一个风险警告。运行时不报任何警告,根据实际值得情况运行时可能出现空指针异常。 目前这个注解只有IDEA支持。

@EnableAsync

我们在使用多线程的时候,往往需要创建Thread类,或者实现Runnable接口,如果要使用到线程池,我们还需要来创建Executors,在使用spring中,已经给我们做了很好的支持。只要要@EnableAsync就可以使用多线程。使用@Async就可以定义一个线程任务。通过spring给我们提供的ThreadPoolTaskExecutor就可以使用线程池。

默认情况下,Spring将搜索相关的线程池定义:要么在上下文中搜索唯一的TaskExecutor bean,要么搜索名为“taskExecutor”的Executor bean。如果两者都无法解析,则将使用SimpleAsyncTaskExecutor来处理异步方法调用。

@EnableAsync开始对异步任务的支持,此注解和@Configuration一起使用。

@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
	
	private static final int corePoolSize = 10;       		// 核心线程数(默认线程数)
	private static final int maxPoolSize = 100;			    // 最大线程数
	private static final int keepAliveTime = 10;			// 允许线程空闲时间(单位:默认为秒)
	private static final int queueCapacity = 200;			// 缓冲队列数
	private static final String threadNamePrefix = "Async-Service-"; // 线程池名前缀
	
	@Bean("taskExecutor") // bean的名称,默认为首字母小写的方法名
	public ThreadPoolTaskExecutor getAsyncExecutor(){
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(corePoolSize);   
		executor.setMaxPoolSize(maxPoolSize);
		executor.setQueueCapacity(queueCapacity);
		executor.setKeepAliveSeconds(keepAliveTime);
		executor.setThreadNamePrefix(threadNamePrefix);
		
		// 线程池对拒绝任务的处理策略
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		// 初始化
		executor.initialize();
		return executor;
	}
}

@Async

在Spring中,基于@Async标注的方法,称之为异步方法;这些方法将在执行的时候,将会在独立的线程中被执行,调用者无需等待它的完成,即可继续其他的操作。

也可以在类上使用,在这种情况下,类中的所有方法都被认为是异步的。但是请注意,@Configuration配置类中声明的方法不支持@Async。
@Async在目标方法签名方面,支持任何参数类型。但是,返回值类型只能是void或Future。

@Service
public class testAsyncService {
	Logger log = LoggerFactory.getLogger(testAsyncService.class);
 
	// 发送提醒短信 1
	@Async("taskExecutor")
	public void service1() throws InterruptedException {
		log.info("--------start-service1------------");
		Thread.sleep(5000); // 模拟耗时
	    log.info("--------end-service1------------");
	}
	
	// 发送提醒短信 2
	@Async("taskExecutor")
	public void service2() throws InterruptedException {
		
		log.info("--------start-service2------------");
		Thread.sleep(2000); // 模拟耗时
	    log.info("--------end-service2------------");
 
	}
}

如下方式会使@Async失效:

  • 异步方法使用static修饰
  • 异步类没有使用@Component注解(或其他注解)导致spring无法扫描到异步类
  • 异步方法不能与异步方法在同一个类中
  • 类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象
  • 如果使用SpringBoot框架必须在启动类中增加@EnableAsync注解
  • 在@Async方法上标注@Transactional是没用的。 在@Async方法调用的方法上标注@Transactional 有效。
  • 调用被@Async标记的方法的调用者不能和被调用的方法在同一类中不然不会起作用。

下面关于线程池的配置还有一种方式,就是直接实现AsyncConfigurer接口,重写getAsyncExecutor方法即可,代码如下:

@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {

     @Override
     public Executor getAsyncExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         executor.setCorePoolSize(7);
         executor.setMaxPoolSize(42);
         executor.setQueueCapacity(11);
         executor.setThreadNamePrefix("MyExecutor-");
         executor.initialize();
         return executor;
     }

     @Override
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return new MyAsyncUncaughtExceptionHandler();
     }
}

@EnableScheduling

在配置类(@Configuration注解)上添加@EnableScheduling开启对定时任务的支持。

@Scheduled

@scheduled注解用来配置到方法上来完成对应的定时任务的配置,如执行时间,间隔时间,延迟时间等等。

参数说明:

① cron属性:这是一个时间表达式,可以通过简单的配置就能完成各种时间的配置,我们通过CRON表达式几乎可以完成任意的时间搭配,它包含了六或七个域:

  • Seconds : 可出现", - * /"四个字符,有效范围为0-59的整数
  • Minutes : 可出现", - * /"四个字符,有效范围为0-59的整数
  • Hours : 可出现", - * /"四个字符,有效范围为0-23的整数
  • DayofMonth : 可出现", - * / ? L W C"八个字符,有效范围为0-31的整数
  • Month : 可出现", - * /"四个字符,有效范围为1-12的整数或JAN-DEc
  • DayofWeek : 可出现", - * / ? L C #"四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推
  • Year : 可出现", - * /"四个字符,有效范围为1970-2099年
     

注:[年]不是必须的域,可以省略[年],则一共6个域。

常用通配符说明:

  • * 表示所有值。例如:在分的字段上设置 *,表示每一分钟都会触发。
  • ? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。
  • - 表示区间。例如 在小时上设置 “10-12”,表示 10,11,12点都会触发。
  • , 表示指定多个值,例如在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发。
  • / 用于递增触发。如在秒上面设置”5/15” 表示从5秒开始,每增15秒触发(5,20,35,50)。 在日字段上设置’1/3’所示每月1号开始,每隔三天触发一次。

下面简单举几个例子:

"0 0 12 * * ?"    每天中午十二点触发
"0 15 10 ? * *"    每天早上10:15触发
"0 15 10 * * ?"    每天早上10:15触发
"0 15 10 * * ? *"    每天早上10:15触发
"0 15 10 * * ? 2005"    2005年的每天早上10:15触发
"0 * 14 * * ?"    每天从下午2点开始到2点59分每分钟一次触发
"0 0/5 14 * * ?"    每天从下午2点开始到2:55分结束每5分钟一次触发
"0 0/5 14,18 * * ?"    每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发
"0 0-5 14 * * ?"    每天14:00至14:05每分钟一次触发
"0 10,44 14 ? 3 WED"    三月的每周三的14:10和14:44触发
"0 15 10 ? * MON-FRI"    每个周一、周二、周三、周四、周五的10:15触发

② fixedRate属性:该属性的含义是上一个调用开始后再次调用的延时(不用等待上一次调用完成),这样就会存在重复执行的问题,所以不是建议使用,但数据量如果不大时在配置的间隔时间内可以执行完也是可以使用的。

③ fixedDelay属性:该属性的功效与上面的fixedRate则是相反的,配置了该属性后会等到方法执行完成后延迟配置的时间再次执行该方法。

④ initialDelay属性:该属性跟上面的fixedDelay、fixedRate有着密切的关系,为什么这么说呢?该属性的作用是第一次执行延迟时间,只是做延迟的设定,并不会控制其他逻辑,所以要配合fixedDelay或者fixedRate来使用

⑤ fixedDelayString属性:与 fixedDelay 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。如:使用${time.fixedDelay}读取配置文件time.fixedDelay的值。

⑥ fixedRateString属性:与 fixedRate 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。

⑦ initialDelayString属性:与 initialDelay 意思相同,只是使用字符串的形式。唯一不同的是支持占位符。

@Schedules

在spring中还有另外的一个注解跟@Scheduled注解一样的作用,就是@Schedules注解,这个注解内部包含多个@Scheduled注解,可以表示一个方法可以存在多个调度设置

@Component

 标注Spring管理的Bean,使用@Component注解在一个类上,表示将此类标记为Spring容器中的一个Bean。

当我们的类不属于各种归类的时候(不属于@Controller、@Services等的时候),我们就可以使用@Component来标注这个类。

@Controller

用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller对象,分发处理器会扫描使用该注解的类的方法,并检测该方法是否使用了@RequestMapping注解。

@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是处理请求的处理器。 

@Indexed

@Repository

@Repository用于标注数据访问组件,即DAO组件。

@Service

@Service用于标注业务层组件。

@Repeat

指明该测试方法需被重复执行。注解指定该测试方法被重复的次数。重复的范围包括该测试方法自身也包括相应的初始化和销毁方法。

@Rollback

指明当测试方法执行完毕的时候是否对事务性方法中的事务进行回滚。如果为true,则进行回滚;否则,则提交。在Spring TestContext框架中,集成测试默认的Rollback语义为true,即使你不显示的指定它。

当被声明为方法级别的注解,则@Rollback为特定的方法指定回滚语义,并覆盖类级别的@Rollback和@Commit语义。

@Timed

表明被注解的测试方法必须在规定的时间区间内执行完成(以毫秒记)。如果测试执行时间超过了规定的时间区间,测试就失败了。

@ActiveProfiles

@ActiveProfiles是一个用于当集成测试加载ApplicationContext的时候声明哪一个bean definition profiles被激活的类级别的注解。

@ContextConfiguration
@ActiveProfiles("dev")
public class DeveloperTests {
// class body...
}
@ContextConfiguration
@ActiveProfiles({"dev", "integration"})
public class DeveloperIntegrationTests {
// class body...
}

@BootstrapWith

一个用于配置Spring TestContext框架如何引导的类级别的注解。具体地说,@BootstrapWith用于指定一个自定义的TestContextBootstrapper。

@ContextConfiguration

@ContextConfiguration定义了类级别的元数据来决定如何为集成测试来加载和配置应用程序上下文。具体地说,@ContextConfiguration声明了用于加载上下文的应用程序上下文资源路径和注解类。

资源路径通常是类路径中的XML配置文件或者Groovy脚本;而注解类通常是使用@Configuration注解的类。但是,资源路径也可以指向文件系统中的文件和脚本,解决类也可能是组件类等等。

@ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}
@ContextConfiguration(classes = TestConfig.class)
public class ConfigClassApplicationContextTests {
 // class body...
}

作为声明资源路径或注解类的替代方案或补充,@ContextConfiguration可以用于声明ApplicationContextInitializer类。

@ContextConfiguration(initializers = CustomContextIntializer.class)
public class ContextInitializerTests {
 // class body...
}

@ContextHierarchy

@ContextHierarchy是一个用于为集成测试定义。

ApplicationContext层次结构的类级别的注解。@ContextHierarchy应该声明一个或多个@ContextConfiguration实例列表,其中每一个定义上下文层次结构的一个层次。下面的例子展示了在同一个测试类中@ContextHierarchy的使用方法。但是,@ContextHierarchy一样可以用于测试类的层次结构中。

@ContextHierarchy({
 @ContextConfiguration("/parent-config.xml"),
 @ContextConfiguration("/child-config.xml")
})
public class ContextHierarchyTests {
 // class body...
}
@WebAppConfiguration
@ContextHierarchy({
 @ContextConfiguration(classes = AppConfig.class),
 @ContextConfiguration(classes = WebConfig.class)
})
public class WebIntegrationTests {
 // class body...
}

如果你想合并或者覆盖一个测试类的层次结构中的应用程序上下文中指定层次的配置,你就必须在类层次中的每一个相应的层次通过为@ContextConfiguration的name属性提供与该层次相同的值的方式来显示地指定这个层次。

@TestExecutionListeners

@TestPropertySource

@TestPropertySource是一个用于为集成测试加载ApplicationContext时配置属性文件的位置和增加到Environment中的PropertySources集中的内联属性的类级别的注解。

测试属性源比那些从系统环境或者Java系统属性以及通过@PropertySource或者编程方式声明方式增加的属性源具有更高的优先级。而且,内联属性比从资源路径加载的属性具有更高的优先级。

@ContextConfiguration
@TestPropertySource("/test.properties")
public class MyIntegrationTests {
// class body...
}
@ContextConfiguration
@TestPropertySource(properties = { "timezone = GMT", "port: 4242" })
public class MyIntegrationTests {
// class body…
}

@AfterTransaction

@AfterTransaction指明通过Spring的@Transactional注解配置为需要在事务中执行的测试方法在事务结束之后执行注解的void方法。从Spring框架4.3版本起,@AfterTransaction方法不再需要为public并可能被声明为基于Java8的接口的默认方法。

@BeforeTransaction

@BeforeTransaction指明通过Spring的@Transactional注解配置为需要在事务中执行的测试方法在事务开始之前先执行注解的void方法。从Spring框架4.3版本起,@BeforeTransaction方法不再需要为public并可能被声明为基于Java8的接口的默认方法。

@WebAppConfiguration

@WebAppConfiguration@WebAppConfiguration是一个用于声明集成测试所加载的ApplicationContext须是WebApplicationContext的类级别的注解。测试类的@WebAppConfiguration注解只是为了保证用于测试的WebApplicationContext会被加载,它使用”file:src/main/webapp”路径默认值作为web应用的根路径(即,资源基路径)。资源基路径用于幕后创建一个MockServletContext作为测试的WebApplicationContext的ServletContext。

@ContextConfiguration
@WebAppConfiguration("classpath:test-web-resources")
public class WebAppTests {
 // class body...
}

注意@WebAppConfiguration必须和@ContextConfiguration一起使用,或者在同一个测试类,或者在测试类层次结构中。

@Commit

指定事务性的测试方法在测试方法执行完成后对事务进行提交。@Commit可以用作@Rollback(false)的直接替代,以更好的传达代码的意图。和@Rollback一样,@Commit可以在类层次或者方法层级声明。

@Validated

用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。

@ControllerAdvice

在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。

@CookieValue

用来获取Cookie中的值。

  • value:参数名称
  • required:是否必须
  • defaultValue:默认值
/**
 * 了解:
 * 
 * @CookieValue: 映射一个 Cookie 值. 属性同 @RequestParam
 */
@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) {
	System.out.println("testCookieValue: sessionId: " + sessionId);
	return SUCCESS;
}

@CrossOrigin

出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源。例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有EVILL网站。来自EVILL的脚本不能够对你的银行API做出Ajax请求(从你的帐户中取出钱!)使用您的凭据。

跨源资源共享(CORS)是由大多数浏览器实现的W3C规范,允许您灵活地指定什么样的跨域请求被授权,而不是使用一些不太安全和不太强大的策略,如IFRAME或JSONP。

(1) controller配置CORS

① controller方法的CORS配置,您可以向@RequestMapping注解处理程序方法添加一个@CrossOrigin注解,以便启用CORS(默认情况下,@CrossOrigin允许在@RequestMapping注解中指定的所有源和HTTP方法):

@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

其中@CrossOrigin中的2个参数:

  • origins :允许可访问的域列表。
  • maxAge:准备响应前的缓存持续的最大时间(以秒为单位)。

② 为整个controller启用@CrossOrigin

@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

在这个例子中,对于retrieve()和remove()处理方法都启用了跨域支持,还可以看到如何使用@CrossOrigin属性定制CORS配置。

③ 同时使用controller和方法级别的CORS配置,Spring将合并两个注释属性以创建合并的CORS配置。

@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

    @CrossOrigin(origins = "http://domain2.com")
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }

    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}

④ 如果您正在使用Spring Security,请确保在Spring安全级别启用CORS,并允许它利用Spring MVC级别定义的配置。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }
}

(2) 全局CORS配置

除了细粒度、基于注释的配置之外,您还可能需要定义一些全局CORS配置。这类似于使用筛选器,但可以声明为Spring MVC并结合细粒度@CrossOrigin配置。默认情况下,所有origins and GET, HEAD and POST methods是允许的。

① Java Config

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer  {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
        .allowedOrigins("http://domain2.com")
        .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
        .exposedHeaders("header1", "header2")
        .allowCredentials(false).maxAge(3600);
    }
}

② XML配置

<mvc:cors>
    <mvc:mapping path="/**" />
</mvc:cors>
<mvc:cors>  
    <mvc:mapping path="/api/*"/>  
</mvc:cors>  

(3) 基于过滤器的CORS支持

作为上述其他方法的替代,Spring框架还提供了CorsFilter。在这种情况下,不用使用@CrossOrigin或WebMvcConfigurer#addCorsMappings(CorsRegistry),,例如,可以在Spring Boot应用程序中声明如下的过滤器:

@Configuration
public class MyConfiguration {

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("http://domain1.com");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

注:springMVC的版本要在4.2或以上版本才支持@CrossOrigin。

@DeleteMapping

@ExceptionHandler

代码中最常见的就是try-catch-finally,有时一个try,多个catch,覆盖了核心业务逻辑:

try{
    ..........
}catch(Exception1 e){
    ..........
}catch(Exception2 e){
    ...........
}catch(Exception3 e){
    ...........
}

Spring能够较好的处理这种问题,核心如下:

  • @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
  • @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开;其是对Controller层进行拦截

@ExceptionHandler注解作用对象为方法,并且在运行时有效,value()可以指定异常类。由该注解注释的方法可以具有灵活的输入参数。

① 基本使用

Spring的@ExceptionHandler可以用来统一处理方法抛出的异常,比如这样:

@ExceptionHandler()
public String handleExeption2(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:默认";
    return resultStr;
}

当我们使用这个@ExceptionHandler注解时,我们需要定义一个异常的处理方法,比如上面的handleExeption2()方法,给这个方法加上@ExceptionHandler注解,这个方法就会处理类中其他方法(被@RequestMapping注解)抛出的异常。

② 注解的参数

@ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常,比如这样:

@ExceptionHandler(NumberFormatException.class)
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}

此时注解的参数是NumberFormatException.class,表示只有方法抛出NumberFormatException时,才会调用该方法。

③ 就近原则

当异常发生时,Spring会选择最接近抛出异常的处理方法。
比如之前提到的NumberFormatException,这个异常有父类RuntimeException,RuntimeException还有父类Exception,如果我们分别定义异常处理方法,@ExceptionHandler分别使用这三个异常作为参数,比如这样:

@ExceptionHandler(NumberFormatException.class)
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}
 
@ExceptionHandler()
public String handleExeption2(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:默认";
    return resultStr;
}
 
@ExceptionHandler(RuntimeException.class)
public String handleExeption3(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:RuntimeException";
    return resultStr;
}

那么,当代码抛出NumberFormatException时,调用的方法将是注解参数NumberFormatException.class的方法,也就是handleExeption(),而当代码抛出IndexOutOfBoundsException时,调用的方法将是注解参数RuntimeException的方法,也就是handleExeption3()。

④ 注解方法的返回值

标识了@ExceptionHandler注解的方法,返回值类型和标识了@RequestMapping的方法是统一的。比如默认返回Spring的ModelAndView对象,也可以返回String,这时的String是ModelAndView的路径,而不是字符串本身。

有些情况下我们会给标识了@RequestMapping的方法添加@ResponseBody,比如使用Ajax的场景,直接返回字符串,异常处理类也可以如此操作,添加@ResponseBody注解后,可以直接返回字符串,比如这样:

@ExceptionHandler(NumberFormatException.class)
@ResponseBody
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}

⑤ 错误的操作

使用@ExceptionHandler时尽量不要使用相同的注解参数。

如果我们定义两个处理相同异常的处理方法:

@ExceptionHandler(NumberFormatException.class)
@ResponseBody
public String handleExeption(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:NumberFormatException";
    return resultStr;
}
 
@ExceptionHandler(NumberFormatException.class)
@ResponseBody
public String handleExeption2(Exception ex) {
    System.out.println("抛异常了:" + ex);
    ex.printStackTrace();
    String resultStr = "异常:默认";
    return resultStr;
}

两个方法都处理NumberFormatException,这种定义方式编译可以通过,而当NumberFormatException真正被抛出时,Spring会给我们报错:


java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class java.lang.NumberFormatException]: {public java.lang.String TestController.handleExeption(java.lang.Exception), public java.lang.String TestController.handleExeption2(java.lang.Exception)}

@GetMapping

用于处理请求方法的GET类型。

@InitBinder

从字面意思可以看出这个的作用是给Binder做初始化的,被此注解的方法可以对WebDataBinder初始化。webDataBinder是用于表单到方法的数据绑定的!

@InitBinder
public void initBinder(WebDataBinder webDataBinder){
	webDataBinder.setDisallowedFields("id");
}

 首先,该注解被解析的时机,是该匹配Controller的请求执行映射的方法之前; 同时 @InitBinder标注的方法执行是多次的,一次请求来就执行一次。

当某个Controller上的第一次请求由SpringMvc前端控制器匹配到该Controller之后,根据Controller的 class 类型 查找 所有方法上标注了@InitBinder的方法,并且存入RequestMappingHandlerAdapter的 initBinderCache,下次一请求执行对应业务方法之前时,可以走initBinderCache缓存,而不用再去解析@InitBinder; 所以 initBinder是controller级别的,一个controller实例中的所有@initBinder 只对该controller有效。

@InitBinder唯一的一个属性value,作用是限制对哪些 @RequestMapping 方法起作用,具体筛选条件就是通过@RequestMapping方法入参来筛选,默认不写就代表对所有@RequestMapping的方法起作用; @InitBinder标注的方法, 方法入参和 @RequestMapping方法入参可选范围一样(这里指的是比如HttpServletRequest、ModelMap这些), 通常一个入参 WebDataBinder 就够我们使用了; @InitBinder标注的方法返回值, 必须为null,这里我理解的是运行期的返回值;如果运行时返回值不为null,抛出异常 “@InitBinder methods should return void:”,编译时IDEA会提示@InitBinder应该返回null,但是不影响编译通过;

@MatrixVariable

 

@ModelAttribute

@ModelAttribute注解可被应用在 方法 或 方法参数 上。

(1) 对方法使用 @ModelAttribute 注解

注解在方法上的@ModelAttribute说明了方法的作用是用于添加一个或多个属性到model上。这样的方法能接受与@RequestMapping注解相同的参数类型,只不过不能直接被映射到具体的请求上。

在同一个控制器中,注解了@ModelAttribute的方法实际上会在@RequestMapping方法之前被调用。

① 无返回值的 @ModelAttribute 方法

一般被 @ModelAttribute 注解的无返回值控制器方法被用来向 Model 对象中设置参数。

可以使用 @ModelAttribute 标注的方法来设置其他 @ReqeustMapping 方法的公用参数,这样就不用每一个方法都设置共同的 Model 参数。

@Controller
public class ModelAttributeController {

	@ModelAttribute
	public void init(Model model) {
		model.addAttribute("arg", "model中设置的参数");
	}

	@RequestMapping("/model-attribute")
	public String get() {

		return "model-attribute";
	}

}

② 有返回值的 @ModelAttribute 方法

有返回值的 @ModelAttribute 方法和没有返回值的 @ModelAttribute 方法的区别就是,没有返回值的 @ModelAttribute 方法使用 model.addAttribute(String key, Object value); 来向 Model 中增加参数。而有返回值的 @ModelAttribute 方法则直接将需要增加的参数返回。

@Controller
public class ModelAttributeController {

	@ModelAttribute
	public User init(Model model) {
		User user = new User("小明", 18);
		return user;
	}

	@RequestMapping("/model-attribute")
	public String get() {

		return "model-attribute";
	}

}

③ 使用 @ModelAttribute("key") 来显示指定属性名

属性名没有被显式指定的时候又当如何呢?在这种情况下,框架将根据属性的类型给予一个默认名称。举个例子,若方法返回一个User类型的对象,则默认的属性名为"user"。你可以通过设置@ModelAttribute注解的值来改变默认值。当向Model中直接添加属性时,请使用合适的重载方法addAttribute(..)-即,带或不带属性名的方法。

④ @ModelAttribute 和 @RequestMapping 注解在同一个方法上

如果 @ModelAttribute 和 @RequestMapping 注解在同一个方法上,那么代表给这个请求单独设置 Model 参数。此时返回的值是 Model 的参数值,而不是跳转的地址。跳转的地址是根据请求的 url 自动转换而来的。比如下面的例子中跳转页面不是 HelloWorld.jsp 而是 model-attribute.jsp。并且参数只有在 model-attribute.jsp 中能够取得,而 demo.jsp 中不能取得。

@Controller
public class ModelAttributeController {

	@ModelAttribute("message")
	@RequestMapping("/model-attribute")
	public String init(Model model) {
		return "HelloWorld";
	}

	@RequestMapping("/demo")
	public String get() {

		return "demo";
	}

}

(2) 在方法参数上使用 @ModelAttribute 注解:

① 数据绑定

注解在方法参数上的@ModelAttribute说明了该方法参数的值将由model中取得。如果model中找不到,那么该参数会先被实例化,然后被添加到model中。在model中存在以后,请求中所有名称匹配的参数都会填充到该参数中。这在Spring MVC中被称为数据绑定,一个非常有用的特性,节约了你每次都需要手动从表格数据中转换这些字段数据的时间。

② 和 BindingResult 配合使用

使用 @ModelAttribute 进行数据绑定之后,可以使用 BindingResult 来返回数据验证结果。数据验证可以使用 hibernate validation 的 @Valid 标签或者 spring Validator 校验机制的 @Validated 配合 BindingResult 使用。 或者自定义校验器来返回 BindingResult 对象来进行校验。你可以通过Spring的 <errors> 表单标签来在同一个表单上显示错误信息。

@Valid 校验器:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {

    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}}

@Validated 校验器:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@Validated @ModelAttribute("pet") Pet pet, BindingResult result) {

    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}

自定义校验器:

@RequestMapping(path = "/owners/{ownerId}/pets/{petId}/edit", method = RequestMethod.POST)
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {

    // 自己编写一个校验方法来处理 result 对象
    new PetValidator().validate(pet, result);
    if (result.hasErrors()) {
        return "petForm";
    }

    // ...

}

@PathVariable

@PathVariable是spring3.0的一个新功能:接收请求路径中占位符的值。

@RequestMapping("/users/{username}")
@ResponseBody
public String userProfile(@PathVariable String username){
	return "user" + username; 
}

在默认情况下,@PathVariable注解的参数可以是一些基本的简单类型:int,long,Date,String等,Spring能根据URL变量的具体值以及函数参数的类型来进行转换。

我们还可以定义正则表达式进行更精确的控制,定义语法是{变量名:正则表达式}[a-zA-Z0-9_]+是一个正则表达式,表示只能包含小写字母,大写字母,数字,下划线。如此设置URL变量规则后,不合法的URL则不会被处理,直接由SpringMVC框架返回404Not Found。

@RequestMapping("/user/{username:[a-zA-Z0-9_]+}/blog/{blogId}")

@PostMapping

处理post请求,传统的RequestMapping来编写应该是@RequestMapping(value = “/get/{id}”,method = RequestMethod.POST)。

@PutMapping

和PostMapping作用等同,都是用来向服务器提交信息。如果是添加信息,倾向于用@PostMapping,如果是更新信息,倾向于用@PutMapping。两者差别不是很明显。

@RequestAttribute

@RequestAttribute用在方法入参上,作用:从request中取对应的值,至于request中是怎么存在该属性的,方式多种多样,拦截器中预存、ModelAttribute注解预存、请求转发带过来的。

@RequestAttribute属性required默认为true, request.getAttribute获取不到参数就会抛出异常 ServletRequestBindingException ;equired设置为false,即使没有从request中获取到就忽略跳过,赋值为null。

@RequestBody

@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。

@RequestHeader

@RequestHeader 是获取请求头中的数据,通过指定参数 value 的值来获取请求头中指定的参数值。

@RequestMapping

SpringMVC通过使用@RequestMapping注解,实现指定控制器可以处理哪些URL请求。

@RequestParam

将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)。

参数说明:

  • value:参数名
  • required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
  • defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值。

注:可以用Map类型的参数类型接收前端form表单以post方式直接submit的数据。

@RequestPart

@RequestPart这个注解用在multipart/form-data表单提交请求的方法上。 适用于复杂的请求域(像JSON,XML)。

@ResponseBody

将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
注意:在使用此注解之后不会再走视图处理器,而是直接将数据写入到输入流中,它的效果等同于通过response对象输出指定格式的数据。

@ResponseStatus

ResponseStatus是由SpringMVC提供的一个注解,可以标注在异常处理方法上,也可以标注在异常类上,用来指定在SpringMVC处理请求的过程中抛出了指定的异常时将响应给客户端的状态码和错误信息。比如我们希望在抛出了IllegalArgumentException时响应给客户端的状态码是502,错误信息是Hello Error,我们就可以像如下这样,在异常处理方法上加上@ResponseStatus注解,同时指定错误码和错误信息,此时的ResponseStatus注解将在ServletInvocableHandlerMethod的initResponseStatus方法中进行处理。

@ControllerAdvice(annotations=RestController.class)
public class MyExceptionHandler {
    
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(value=HttpStatus.BAD_GATEWAY, reason="Hello Error")
    public void handleIllegalArgumentException() {
        
    }
    
}

在没有使用异常处理器方法的情况下,如果异常类的源码是我们可以自己进行控制的,也可以在异常类上加上@ResponseStatus注解指定错误码和错误信息。此时的ResponseStatus注解将由ResponseStatusExceptionResolver进行处理。

@ResponseStatus(value=HttpStatus.BAD_GATEWAY, reason="Hello Error")
public class ResponseStatusException extends RuntimeException {

    /**
     * 
     */
    private static final long serialVersionUID = 6644585920167208469L;

}

@RestController

@RestController注解相当于@ResponseBody + @Controller合在一起的作用@RestController注解相当于@ResponseBody + @Controller合在一起的作用

注:如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是return 里的内容。

@RestControllerAdvice

@ControllerAdvice + @ResponseBody的组合效果一致。

@SessionAttribute

@ModelAttribute注解作用在方法上或者方法的参数上,表示将被注解的方法的返回值或者是被注解的参数作为Model的属性加入到Model中,然后Spring框架自会将这个Model传递给ViewResolver。Model的生命周期只有一个http请求的处理过程,请求处理完后,Model就销毁了。

如果想让参数在多个请求间共享,那么可以用到要说到的@SessionAttribute注解。

@SessionAttribute只能作用在参数上,用于获取已经存储的session数据。

@RequestMapping("/session")
public String session(@SessionAttribute("user") User user){
	// do something
	return "index";
}

@SessionAttributes

该注解使用在类注解上该注解使用在类注解上。

@SessionAttributes 除了可以通过属性名指定需要放到会 话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中 例如:

  • @SessionAttributes(types=User.class)会将model中所有类型为 User的属性添加到会话中。
  • @SessionAttributes(value={“user1”, “user2”}) 会将model中属性名为user1和user2的属性添加到会话中。
  • @SessionAttributes(types={User.class, Dept.class}) 会将model中所有类型为 User和Dept的属性添加到会话中。
  • @SessionAttributes(value={“user1”,“user2”},types={Dept.class})会将model中属性名为user1和user2以及类型为Dept的属性添加到会话中。
@SessionAttributes(value={"user"})
@Controller
public class UserController {

    @RequestMapping("/testSessionAttributes")
    public String testSessionAttributes(Model model){
        User user = new User("jack","123456");
        model.addAttribute("user", user);
        return "success";
    }
}

解释:处理方法testSessionAttributes在model中存放了属性名为user的数据, 处理结束后,model里的数据会被放入到request中,页面通过request域可以获取到。而这里使用了@SessionAttributes(value={“user”})将model中属性名为user的数据copy一份进了session域中。

@EnableWebMvc

@EnableWebMvc是使用Java 注解快捷配置Spring Webmvc的一个注解。

通过查看@EnableWebMvc的源码,可以发现该注解就是为了引入一个DelegatingWebMvcConfiguration Java 配置类。并翻看DelegatingWebMvcConfiguration的源码会发现该类似继承于WebMvcConfigurationSupport的类。

其实不使用@EnableWebMvc注解也是可以实现配置Webmvc,只需要将配置类继承于WebMvcConfigurationSupport类即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codedot

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值