[spring注解]Spring相关注解(三)

前言

  接上文,本篇文章主要来学习下spring-beans,spring-core,spring-webmvc对应jar包中相关注解,其中spring-beans注解大概有:AutowiredQualifierRequiredLookupValue ;spring-core中注解大概有:OrderAliasFor ;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.

  1. 比如,当我们在一个bean中依赖其他的bean时,我们可以通过setter或者构造器方法注入其他依赖的bean,这样调用get方法的时候返回在bean中注入的实例。但是如果我们希望在每次调用get方法的时候都返回新的实例,(适用于单例bean依赖于原型bean的情况,因为单例bean只会实例化一次,而内部依赖的bean也只会被实例化一次),这时候就可以通过该注解来实现;
  2. 由于底层是通过cglib动态生成的,所以我们要注入的类不能是final修饰的;
  3. 该注解适用的就是单例bean依赖原型bean,而单例bean依赖单例bean,原型bean依赖原型就不会有这样的问题了。

具体使用可以参考StackOverFlow的一篇文章:# How to use spring @Lookup annotation?

4. Value 注解

  Value注解一般用于读取配置文件中的参数,一般情况下,为了方便和统一,我们通常将将配置信息存于properties文件中,然后在使用的时候通过@Value注解来解析获取。使用该注解需要先配置下PropertySourcesPlaceholderConfigurer,一般,目前主流的配置方式,XML配置:

<context:property-placeholder ignore-resource-not-found="true" ignore-unresolvable="true"
    location="classpath*:configure/*/*.properties" file-encoding="UTF-8"/>

注解配置:

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    return new PropertySourcesPlaceholderConfigurer();
}

然后使用@Value注解来获取数据:

@Value("${username}")
private String uname;

而@Value通常也有两种使用方式:

  • @Value("#{configProperties['key']}")
  • @Value("${key}")

@Value的参数类型是SpEL表达式,有兴趣的童鞋可以学习下,功能很强大的表达式语言,而有关@Value的用法可以参考我前文的注解<context:property-placeholder/>。

二、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添加了别名。该注解有三个参数:

  • attributevalue,别名名称,可以看到,这两个属性都是用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);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容