Spring注解开发

@Configuration

  • 标注在类上,表明该类是一个配置类,相当于之前写的spring的xml文件,通常与@Bean结合使用
  • 该注解同时拥有了@Component的作用,将当前类注入ioc容器
    • 其中有一个value属性,指定注入ioc容器的名称,默认是类名首字母小写
    • 源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {}

@Bean

  • 在配置类中使用该注解
  • 标注在方法上,默认将方法的返回对象注入到ioc容器,名称默认使用的是方法名
/**
 * 配置类
 */
@Configuration(value = "MyConfiguration")
public class MyConfiguration {
    /**
     * 注入一个User对象,ioc容器中的name是user,类型是User类型
     */
    @Bean
    public User user(){
        return new User(1,"name");
    }
}

属性

  • name:指定注入ioc容器中的名称
  • value:同name
  • autowireCandidate:是否能够自动注入,默认是true,如果指定了属性为false,那么不能使用@Autowired或者@Resource自动注入
  • initMethod:指定初始化方法,在构造方法之后执行
  • destroyMethod:指定销毁方法,在容器关闭的时候执行

实例

/**
 * 配置类
 */
@Configuration(value = "MyConfiguration")
public class MyConfiguration {

    /**
     * 注入一个User对象,ioc容器中的name是user,类型是User类型
     * init是User类中的init方法,destory是User类中的destory方法
     */
    @Bean(initMethod = "init",destroyMethod = "destory")
    public User user1(){
        return new User(1,"name");
    }
}

/**
* User类
*/
public class User implements Serializable {
    private Integer id;
    private String name;

    public User(String name) {
        this.name = name;
    }

    public User(Integer id, String name) {
        System.out.println("执行构造方法");
        this.id = id;
        this.name = name;
    }

    public void init(){
        System.out.println("初始化方法");
    }

    public void destory(){
        System.out.println("销毁方法");
    }
}

@Scope

  • 指定对象的作用域名,标注在类上或者方法上,默认是单实例

四大作用域

  • singleton:单例,默认值,当容器启动的时候会创建对象放入ioc容器中,后续获取只是从容器中创建,并不会再次调用构造方法new出来
  • prototype:多实例,容器启动的时候并不会创建该对象,而是当需要用到的才调用构造方法new出来(不放入ioc容器,每次用到就new一个)
  • request:该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。
  • session:该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
 /**
     * 指定多实例,每次用到都会调用
     */
    @Bean(initMethod = "init",destroyMethod = "destory")
    @Scope(value = "prototype")
    public User user1(){
        return new User(1,"name");
    }

@Conditional

  • 标注在方法或者在类上,只有在满足其中的匹配条件才会将对象注入到ioc容器中。
  • 只有一个属性value,是一个Condition数组,要向实现相应的功能,可以自定义一个类,实现Condition这个接口即可。
  • 这个注解在SpringBoot中将会有很多的扩展,这里就不多说了。
/**
     * 指定多实例,每次用到都会调用
     * @Conditional 只有里面全部都匹配才会正常注入到容器中
     */
    @Bean(initMethod = "init",destroyMethod = "destory")
    @Conditional(value = {FirstCondition.class})
    public User user1(){
        return new User(1,"name");
    }
  • 下面看看FirstCondition这个类具体实现
    • 只需要实现接口中的matches方法即可,返回true表示符合条件,否则不满足条件,只有满足条件才会注入到ioc容器中
/**
 * 自定义的条件判断,实现Condition接口
 */
public class FirstCondition implements Condition {

    /**
     * 如果返回true表示符合条件,反之不符合条件
     * @param context ConditionContext对象,可以获取上下文的信息
     * @param metadata AnnotatedTypeMetadata对象,可以获取标注在该方法上面的注解信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取Environment,用来获取运行环境中的一些变量
        Environment environment = context.getEnvironment();
        //获取在properties文件中配置的参数,表示是否注入相关属性
        Boolean isAutowired = environment.<Boolean>getProperty("isAutowired", Boolean.class);
        return isAutowired;
    }
}

ConditionContext

  • 主要的功能是获取上下文的对象,比如BeanFactory
public interface ConditionContext {

    /**
    * 获取 BeanDefinitionRegistry,可以自己手动注册对象到ioc容器中
    */
    BeanDefinitionRegistry getRegistry();

    /**
     * 获取BeanFacotory,操作ioc容器,比如获取对应的Bean,判断ioc中是否已经注入   
     */
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();

    /**
     * 返回当前的运行环境,可以获取运行环境中的一下参数,或者一些配置文件中的数据
     */
    Environment getEnvironment();

    /**
     * 获取资源加载器
     */
    ResourceLoader getResourceLoader();

    /**
     * 获取类加载器 
     */
    @Nullable
    ClassLoader getClassLoader();
}

AnnotatedTypeMetadata

  • 主要的作用就是获取标注了@Conditional这个注解的方法上的注解和对应的参数等信息
public interface AnnotatedTypeMetadata {

    /**
    * 判断方法上是否有对应的注解 
    * @param annotationName 注解类的全类名,getName()
    */
    boolean isAnnotated(String annotationName);

    /**
    *  获取对应注解的全部属性的值,key是属性,value是属性的值
    * @param annotationName 注解类的全类名,getName()
    */
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName);

    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);

    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);

    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}

@PostConstruct

  • 标注在方法上,用来在类加载并且属性赋值之后调用,通常用来初始化,和@Bean中的init-menthd指定的作用相同
@PostConstruct
    public void init(){
        System.out.println("初始化方法");
    }

@PreDestroy

  • 标注在方法上,容器销毁执行,相当于@Bean中的destroy-method属性
@PreDestroy
    public void destory(){
        System.out.println("销毁方法");
    }

@Import

  • 用来形式化的注入,主要形式有:
    • 直接导入类
    • 导入配置类
    • 指定ImportSelector
    • 使用ImportBeanDefinitionRegistrar手动注册

直接导入类

  • 直接在 value属性中指定需要导入的类即可,如下:
@Configuration(value = "MyConfiguration")
@Import(value = {Person.class})
public class MyConfiguration {}

导入配置类

  • 新建一个配置类,但是不用@Configuration标注,使用@Import在另外一个配置类上引入即可
/**
 * 这是一个配置,但是并没有使用@Configuration这个注解,因此不会生效
 */
public class SecondConfiguration {
    @Bean
    public Person person(){
        return new Person();
    }
}
  • 在另外一个配置类使用@Import注解引入上面的配置类,如下:
@Configuration(value = "MyConfiguration")
@Import(value = {SecondConfiguration.class})
public class MyConfiguration {}

指定ImportSelector

  • 使用ImportSelector需要自定义一个实现类,如下:
/**
 * 自定义Selector,需要实现ImportSelector
 */
public class FirstSelector implements ImportSelector {
    /**
     * 筛选逻辑,返回的是String数组(需要注入到容器中的类的全类名)
     * @param importingClassMetadata AnnotationMetadata对象,对标注了@Import这个注解的类中的所有注解信息,比如获取标注指定注解的方法
     * @return 返回的是需要注入的字符串数组(类的全类名)
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //获取@Import标注的类中被@Bean标注的方法元数据
        Set<MethodMetadata> annotatedMethods = importingClassMetadata.getAnnotatedMethods(Bean.class.getName());
        annotatedMethods.forEach(o->{
            System.out.println(o.getMethodName());
        });
        //将Person类返回去,那么将会自动注入Person
        return new String[]{Person.class.getName()};
    }
}
  • 在配置类上使用@Import注解引入即可,如下:
@Configuration
@Import(value = {FirstSelector.class})
public class MyConfiguration {}

AnnotationMetadata

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {

    //拿到Class上标注的所有注解,依赖于Class#getAnnotations
    Set<String> getAnnotationTypes();

    // 拿到所有的元注解信息AnnotatedElementUtils#getMetaAnnotationTypes
    //annotationName:注解类型的全类名
    Set<String> getMetaAnnotationTypes(String annotationName);
    // 是否包含指定注解 (annotationName:全类名)
    boolean hasAnnotation(String annotationName);
    //这个厉害了,依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
    boolean hasMetaAnnotation(String metaAnnotationName);
    // 类里面只有有一个方法标注有指定注解,就返回true
    //getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
    boolean hasAnnotatedMethods(String annotationName);
    // 注意返回的是MethodMetadata 原理基本同上
    // .getDeclaredMethods和AnnotatedElementUtils.isAnnotated  最后吧Method转为MethodMetadata
    Set<MethodMetadata> getAnnotatedMethods(String annotationName);
}

使用ImportBeanDefinitionRegistrar手动注册

  • 需要自定义一个类实现ImportBeanDefinitionRegistrar,如下:
/**
 * 自定义的FirstBeanDefinitionRegistrar,需要实现ImportBeanDefinitionRegistrar
 */
public class FirstBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * 自己手动注册Bean到ioc容器中
     * @param importingClassMetadata 获取@Import标注的类上的注解信息,比如获取被指定注解标注的方法信息
     * @param registry  注册中心,可以获取指定bean的信息和手动注册bean
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //如果容器中没有Person这个Bean,就创建一个
        if (!registry.containsBeanDefinition(Person.class.getName())){
            GenericBeanDefinition beanDefinition=new GenericBeanDefinition();
            beanDefinition.setBeanClass(Person.class);
            //手动注册
            registry.registerBeanDefinition("person",beanDefinition);
        }
    }
}
  • 在配置类上使用@Import注解引入即可,如下:
@Configuration
@Import(value = {FirstBeanDefinitionRegistrar.class})
public class MyConfiguration {}

@Primary

  • 在spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种的情况下 @Primary 的作用就出来了。
  • 有两种使用方式,一种是结合@Bean,另外一种是结合@Service,@Component,@Controller.....
    @Bean
    @Primary
    public User user1(){
        return new User(1,"user1");
    }
    
    //第二种
    @Primary
    @Component
    public class Person {
        private String name;
        private Integer age;
    }

@Autowired

  • 标注在方法和属性上,用来自动为成员变量赋值
  • 默认先根据属性的类型从ioc容器中查找,如果ioc容器中存在多个类型相同的属性,将会根据变量名从ioc容器中查找。
@Controller
public class UserController {
    @Autowired
    private UserService userService;

属性

  • required:指定该属性是否是必须的,默认为true,表示一定要为属性赋值,如果ioc容器中没有对应的Bean,那个将会报错,如果为false,会先从ioc容器中查找对应的Bean,如果存在就进行赋值,不存在就不赋值,不会报错。

@Qualifier

  • @Autowired结合使用,用来从容器中注入指定名字的Bean
  • 使用场景:容器中存在多个类型相同的Bean,那么此时仅仅使用@Autowired就不太适用了,此时就要结合该注解,指定需要注入的name。(当然除了@Autowired还是可以根据成员变量的名称进行注入的)
@Controller
public class UserController {
    @Autowired
    @Qualifier(value = "userService")
    private UserService userService;

@PropertySource

  • 从配置文件中读取相关配置注入到指定的成员属性中
@Component
@PropertySource(value = {"classpath:user.properties"})
public class User implements Serializable {
    private String name;
    private Integer age;

    public User(){}

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

属性

  • value:指定资源文件的位置
  • ignoreResourceNotFound:是否忽略资源文件不存在,默认为false,表示如果资源文件不存在,那么将会抛出异常,如果为true,资源文件不存在的话,程序正常运行

@Value()

  • 有三个典型的使用场景:
    • 获取配置文件中对应的值,为指定属性赋值
    • 使用指定的值为属性赋值
    • 通过表达式计算得到的值为属性赋值

获取配置文件中的值为属性赋值

  • 使用${}的方式获取配置文件中设置的值
@Value("${name}")
private String name;

使用指定的值

  • 其中的value属性可以是自己随便指定的值,如下:
@Value("陈加兵")
private String name;    

表达式赋值

  • 表达式的计算需要使用#{}
 @Value("#{10+22}")
 private Integer age;

@Profile

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

推荐阅读更多精彩内容