前言
接上文,本篇文章主要来学习下spring-beans,spring-core,spring-webmvc对应jar包中相关注解,其中spring-beans注解大概有:Autowired
,Qualifier
,Required
,Lookup
,Value
;spring-core中注解大概有:Order
,AliasFor
;spring-webmvc中注解有:EnableWebMvc
。
一、spring-beans相关注解
1. Autowired 和 Qualifier 注解
Autowired注解是Spring提供的用于注入bean使用的,默认情况下按照类型进行自动注入(byType),如果根据类型找不到或有多个类型的话,就会抛出异常。如果我们想让Spring 在找不到对应的bean的时候不抛出异常,可以使用该注解唯一的一个参数:required
,默认是true,我们可以配置为false,这样的话,如果找不到对应的bean,那么该属性就将被设置为null,而不会抛出异常。
该注解能用于的地方特别多,类上,方法上,属性上,构造方法上等,不过有一点可能需要我们简单注意下:
一般情况下,Autowired注解的required属性可以用于该注解所使用的任意地方。但是当用到构造器上的时候,只有一个构造器可以将required属性设置为true,其他构造器只能将该值设置为false。此外,当使用该注解标注多个构造器时,Spring会从所有满足注入条件的狗在其中选择入参最多的那个构造器。
我们可以来简单看下它的实现:
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
}
我们来简单看一个例子:
接口UserService:
public interface UserService {
User getUserById();
}
对应的实现:
@Service
public class UserServiceImpl implements UserService{
@Override
public User getUserById() {
User user = new User();
user.setId("123");
user.setName("zhangsan");
user.setaDouble(123.34D);
return user;
}
}
简单测试:
@RestController
public class HelloController {
@Autowired
private UserService userService;
@RequestMapping(value = "/test1.html", method = RequestMethod.GET)
public String test1(HttpServletRequest request) {
User user = userService.getUserById();
System.out.println(user.getId());
System.out.println(user.getaDouble());
System.out.println(user.getName());
return "test1";
}
简单测试下,没什么问题,该打印的都打印了。然后我们再给该接口添加一个实现:
@Service
public class UserServiceImpl2 implements UserService{
@Override
public User getUserById() {
User user = new User();
user.setId("456");
user.setName("zhang");
user.setaDouble(123.34D);
return user;
}
}
当有两个实现的时候,也就是通过类型可以查找到多个的时候,Spring区分不出来是哪个bean,将会直接抛出异常。而这个时候,我们可以配合使用Spring的@Qualifier
注解来解决这个问题。
@Autowired
@Qualifier("userService")
private UserService userService;
@Qualifier注解有一个参数:value
,该属性表示bean在Spring中对应的id。通过指定@Qualifier注解的value
属性,显式的将@Autowired的byType自动注入转换为byName的注入。
@Qualifier注解也可以用来标注我们的实现类,从而对bean的范围做一个限制。当然我们也可以自定义注入属性,这就用到@Qualifier的另一个功能了,也就是将Qualifier作为元注解,在此基础上自定义我们的注解。
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface UserAnnotation {
}
通过自定义的@UserAnnotation注解,用来标记我们对应的类:
@Service
@UserAnnotation
public class UserServiceImpl implements UserService{
}
这样,我们就可以在需要的时候使用该注解来进行注入:
@Autowired
@UserAnnotation
private UserService userService;
另,注意下:
As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean only defines one constructor to begin with. However, if several constructors are available, at least one must be annotated to teach the container which one to use.
有关@Autowired注解 对应官网文档:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-autowired-annotation
2. Required 注解
该注解用于Spring bean属性的setter方法,用来强制性设置该属性必须存在,如果该属性不存在,则会抛出异常,该注解通过RequiredAnnotationBeanPostProcessor
来实现。
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
通过@Required注解注释了之后,那么在进行注入的时候,该MovieFinder对象必须要有注入的值,否则就会直接报错;
3. Lookup 注解
和XML配置中bean的 lookup-method方法类似,该注解是Spring提供的用于动态获取新的实例的注解。我们来简单分析下:
It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean.
- 比如,当我们在一个bean中依赖其他的bean时,我们可以通过setter或者构造器方法注入其他依赖的bean,这样调用get方法的时候返回在bean中注入的实例。但是如果我们希望在每次调用get方法的时候都返回新的实例,(适用于单例bean依赖于原型bean的情况,因为单例bean只会实例化一次,而内部依赖的bean也只会被实例化一次),这时候就可以通过该注解来实现;
- 由于底层是通过cglib动态生成的,所以我们要注入的类不能是final修饰的;
- 该注解适用的就是单例bean依赖原型bean,而单例bean依赖单例bean,原型bean依赖原型就不会有这样的问题了。
具体使用可以参考StackOverFlow的一篇文章:# How to use spring @Lookup annotation?
4. Value 注解
Value注解一般用于读取配置文件中的参数,一般情况下,为了方便和统一,我们通常将将配置信息存于properties文件中,然后在使用的时候通过@Value注解来解析获取。使用该注解需要先配置下PropertySourcesPlaceholderConfigurer,一般,目前主流的配置方式,XML配置:
注解配置:
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
然后使用@Value注解来获取数据:
@Value("${username}")
private String uname;
而@Value通常也有两种使用方式:
- @Value("#{configProperties['key']}")
- @Value("${key}")
@Value的参数类型是SpEL表达式,有兴趣的童鞋可以学习下,功能很强大的表达式语言,而有关@Value的用法可以参考我前文的注解
二、Spring-core相关注解
1. Order注解
Order注解在Spring4.0之前都是用于控制AOP中AspectJ的切面的顺序,Spring4.0之后该注解做了增强,它开始支持配置类的加载排序,及对加载在list集合中的自动注入(auto-wired)组件的排序。
该注解只有一个value的参数,用于控制顺序,值越小优先级越高。
接下来看两个简单的例子,首先是配置类的加载顺序:
@Configuration
@Order(2)
public class BeanService1 {
@Bean
public User getUser() {
User user = new User();
user.setName("BeanService1");
return user;
}
}
@Configuration
@Order(13)
public class BeanService2 {
@Bean
public User getUser() {
User user = new User();
user.setName("BeanService2");
return user;
}
}
@Configuration
@Order(1)
public class BeanService3 {
@Bean
public User getUser() {
User user = new User();
user.setName("BeanService3");
return user;
}
}
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
User user = ac.getBean("getUser", User.class);
System.out.println(user.getName());
ac.close();
}
}
我们通过该例子来实现 通过名字来覆盖Bean配置,运行之后,可以看到,最终打印的是 BeanService2
,因为@Order值大的覆盖掉了前面的值,当然我们也可以配置不同的对象来查看下顺序,而不是覆盖。
可以再来看一个List的例子,地址:Spring @Order Annotation,这篇文章写的比较详细,大家可以直接看就可以了。
该注解的使用参考自:
Instantiate beans in order in Spring?
http://ifeve.com/spring4-2/
2. AliasFor 注解
Spring4.2之后引入的@AliasFor注解,用来给注解的属性起别名,让人使用注解时,更加的容易理解,Spring4.2中大量的注解都为value添加了别名。该注解有三个参数:
-
attribute
和value
,别名名称,可以看到,这两个属性都是用AliasFor注解来修饰的; -
annotation
,注解的类型,默认是Annotation.class
;
看一段代码实例:
@MainBean(beanName = "mainbean")
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
String[] beannames = context.getBeanNamesForType(Main.class);
//当加上@AliasFor时, 输出"mainbean"
//当去掉@AliasFor注解后, 输出"main"
System.out.println(beannames[0]);
context.close();
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@interface MainBean {
@AliasFor(annotation = Component.class, attribute = "value")
String beanName() default "";
}
代码转自:http://ifeve.com/spring4-2/
详细代码可以参考文档实现:https://docs.spring.io/spring/docs/current/javadoc-api/
三、Spring-webmvc 相关注解
1. EnableWebMvc 注解
这个注解很简单,就是开启Spring-MVC配置,添加再@Configuration 修饰的配置类上,不过只能由一个@Configuration配置类可以添加该注解,而在XML中对应的配置是mvc:annotation-driven
。然后可以继承WebMvcConfigurerAdapter来配置Web中一些设置:
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses = MyConfiguration.class)
public class MyConfiguration extends WebMvcConfigurerAdapter {
}
当然也可以直接选择实现WebMvcConfigurer
,不过需要实现所有的接口,而有些是我们不需要的,所以通常我们都是继承WebMvcConfigurerAdapter
,然后又选择的实现我们所需要的。比如:
@Configuration
@ComponentScans({@ComponentScan(value = {"controller", "global","controller2"}),
@ComponentScan(value = {"service"})})
@EnableWebMvc
@EnableScheduling
public class SpringConfig extends WebMvcConfigurerAdapter {
/**
* 指定视图解析器
*
* @return
*/
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
// 视图的路径
viewResolver.setPrefix("/WEB-INF/views/");
// 视图名称后缀
viewResolver.setSuffix(".jsp");
return viewResolver;
}
/**
* 配置默认servletHandler,用于静态资源访问
* @param configurer
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/**
* 配置视图管理器
* @param configurer
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false).favorParameter(true).ignoreAcceptHeader(false);
configurer.mediaType("json", MediaType.APPLICATION_JSON);
configurer.mediaType("xml", MediaType.APPLICATION_XML);
}
}