Spring(IOC与DI相关)的纯注解配置详解

导读:       

           我们在刚开始进行spring的学习时, 大部分都是在applicationContext.xm的配置文件中向IOC容器中注入bean,但是这种方式实在是有点麻烦,所以从spring的2.5版本开始就引入注解的方式来帮助我们简化bean的配置,而现在spring版本已经升级到5.0以上了,使用注解的方式来进行配置bean已经越来越普遍,在springboot中更是没有了applicationContext.xml的配置文件,都是采用注解的方式来进行IOC的注入,所以了解如何用注解来进行bean的配置变得很重要了。

IOC相关的注解

@Configuration注解

        作用:在applicationContext.xml配置文件中我们配置bean都是在<beans></beans>标签中进行的,而在纯注解的配置中我们不需要applicationContext.xml这个文件,所以在这里Configuration注解就相当于applicationContext.xml中的beans标签

        使用:  在一个类前加上这个注解即可,该类就相当于一个beans标签了

//该类就相当于一个空的beans标签了
@Configuration
public class UserDao {
}

启动容器

        与通过配置文件启动容器的方式相同,只不过是使用AnnotationConfigApplicationContext这个实现类传入字节码对象即可

public class Main {
    public static void main(String[] args) {
    //传入@Configuration注解所在类的Class对象
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserDao.class);
    }
}

@ComponentScan注解(包扫描注解)

        作用: 我们在使用配置注解(比如@Autowired,@Component等注解,下面会讲)时, 如果我们只是在一个方法或者类前写一个注解,那么spring怎么知道我们要将哪个类交给IOC容器管理呢,所以这个注解的作用就是告诉spring要扫描哪些包下的类,将这些配置了注解的类交给IOC容器管理

        使用: 在添加了@Configuration的注解类前加上此注解, 并且指定basePackages属性的值即可.

@Configuration
//basePackages为一个字符串数组,指定要进行扫描的包
@ComponentScan(basePackages = {包1, 包2, .....})
public class UserDao {
   
}

@Component注解

        作用:将一个类注入IOC容器中,相当于先前配置文件中的bean标签

        使用:  在一个类前加上此注解即可, 有一个value参数,作用是指定注入类的名称,相当于bean标签中的id属性.也可以省略不写,默认为类名首字母小写名称,例如Plant类的默认名称为plant

//将Animal类交给IOC容器管理
@Component(value = "animals")
public class Animal {
    public Animal(){
        System.out.println("Animal is creating....");
    }
}

@Controller,@Service,@Repository注解

        与上面的@Component注解作用与用法一样,只是帮助我们标识此类在项目中的层面罢了,

@Controller常用于控制层(其实在后面的web开发中,被它修饰的类中的方法默认都是一个Servlet,现在了解即可),@Service用于业务层,@Repository用于数据操作层。

@Scope注解

        作用:指定类的制造方式为单例还是单例

        使用:放置在类前面即可,指定为单例还是多例模式

@Component(value = "animals")
@Scope("singleton")//或者prototype
public class Animal {
    public Animal(){
        System.out.println("Animal is creating....");
    }
}

@PostConstruct,@PreDestroy注解

        作用:为一个类指定初始化(@PostConstruct)或者销毁(@PreDestroy)方法,相当于bean标签中的init-method和destroy-method方法

        使用:在一个想要充当初始化或者销毁方法的方法前添加此注解

@Component(value = "animals")
public class Animal {
    public Animal(){
        System.out.println("Animal is creating....");
    }
    
    //初始化方法
    @PostConstruct
    public void init(){
        System.out.println("Animal is initializing...");
    }

    //销毁方法
    @PreDestroy
    public void destroy(){
        System.out.println("Animal is destroying");
    }
}

测试:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserDao.class);
        Animal animal = applicationContext.getBean(Animal.class);
        //在退出虚拟机之前销毁IOC容器
        applicationContext.registerShutdownHook();
    }
}

结果:

Animal is creating....
Animal is initializing...
Animal is destroying

进程已结束,退出代码0

DI相关注解

 @Autowired,@Resource, @Qualifier(需配合Autowired注解使用)注解

        作用: 这些注解都是用来自动装配引用类型的注解,装配的bean必须在IOC容器中存在

        使用: 在一个setter或者字段名之前都可以添加使用.

//新建一个Person类,Person类有一个类型为Animal的字段animal
public class Person {
    
    //添加到字段之前也是可以的
//    @Autowired
    private Animal animal;

    public Person(){
        System.out.println("Person is creating....");
    }

    //setter方法前也可以,自动装配该字段
    @Autowired
    public void setAnimal(Animal animal) {
        this.animal = animal;
    }
}

        区别:

@Autowired:

@Autowired注解是默认首先按照类型装配的(byType),如果找不到对应类型的bean或者找到多个类型相同的bean,则再按照名称进行装配(byName)。如果想直接指定名称进行装配则需要配合@Qualifier注解进行使用:

首先我们创建两个类型一样的bean,一个为animal,一个为animal1

 然后我们在字段animals前添加此注解,由于@Autowired先根据类型进行装配,但是IOC中有两个类型相同的bean(animalanimal1),如前面所说,当找到多个类型的bean时,@Autowired注解会按照名称进行装配,所以这里就会按照bean的名称进行装配,但是这里变量的名称为animals,而两个bean的名称为animal和animal1(与变量名称不同,因为是按名称装配所以装配不成功),所以这里就会报错

@Component
public class Person {

    @Autowired
    private Animal animals;
    public Person(){
        System.out.println("Person is creating....");
    }

    public void printAnimal(){
        //打印自动装配的animals
        System.out.println("animal is" + animals);
    }
}

开启测试:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserDao.class);
        Person person = applicationContext.getBean(Person.class);
        person.printAnimal();
    }
}

结果:

警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person': Unsatisfied dependency expressed through field 'animal1s'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cn_java_factory.Animal' available: expected single matching bean but found 2: animal,animal1
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person': Unsatisfied dependency expressed through field 'animal1s'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cn_java_factory.Animal' available: expected single matching bean but found 2: animal,animal1

但是我们只要将字段名改为"animal"或者"animal1"就会运行成功

 @Autowired
//animal1或者animal都可以
 private Animal animal;

结果:

Person is creating....
animal iscn_java_factory.Animal@3b69e7d1

进程已结束,退出代码0

还可以直接为@Autowired注解指定以名称进行装配,这时就要使用@Qualifier注解,例如:

    @Autowired
    @Qualifier("animal")
    private Animal myAnimal;

我们这里使用@Qualifier注解指定@Autowired以指定名称进行装配(@Qualifier必须配合@Autowired使用),所以@Autowired会找到名称为"animal"的bean装配给myAnimal这个属性。

@Resource

        @Resource注解如果没有指定name和type属性则默认以名称进行装配,当注解写在字段上时,默认取字段名为指定名称,如果没有找到对应的bean,则再以类型进行自动装配,如果找不到或者找到多个同一类型的bean则报错;如果指定name属性则只会按照名称进行装配,没有找到指定的名称则报错,如果指定type属性则只会按照类型进行装配,如果找不到或者找到多个同一类型的bean则报错;

@Value注解

        作用:给简单类型字段进行赋值

        使用:在简单类型字段前添加即可

@Component
public class Person {

    //为String类型赋值
    @Value("jack")
    private String username;

    @Value("20")
    private int age;

    public Person(){
        System.out.println("Person is creating....");
    }

    public void print(){
        System.out.println("username is " + username + ", age " + age);
    }
}

测试:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserDao.class);
        Person person = applicationContext.getBean(Person.class);
        person.print();
    }
}

结果:

Person is creating....
username is jack, age 20

进程已结束,退出代码0

可以看出值已经注入进去了;

@Value还可以读取properties配置文件中的值:

1.通过@PropertySource注解将properties文件加载进来

@Configuration
@ComponentScan(basePackages = {"cn_java_factory"})
//info.properties为在resources文件夹下的配置文件,最好加上"classpath:"这个前缀
@PropertySource("classpath:info.properties")
public class UserDao {
}

2.通过${属性名}进行引用

@Component
public class Person {
    //hobby为info.properties中的属性名
    @Value("${hobby}")
    private String hobby;

    @Value("${weight}")
    private int weight;

    @Value("${height}")
    private double height;


    public Person(){
        System.out.println("Person is creating....");
    }

    public void print(){
        System.out.println("weight " + weight + " height" + height + " hobby " + hobby);
    }
}

@Bean注解

        作用: 将静态工厂方法或者实例工厂方法的返回对象注入到IOC容器中

        使用: 在工厂方法前加上此注解即可,可通过value指定bean的名称,如果不进行指定的话默认为方法名名称。

@Configuration
@ComponentScan(basePackages = {"cn_java_factory"})
@PropertySource("classpath:info.properties")
public class UserDao {
    //静态工厂
    @Bean("animals")
    public static Animal getAnimal(){
        return new Animal();
    }

    //实例工厂
    @Bean
    public Person getPerson(){
        return new Person();
    }
}

从下图可以看出@Bean如果没有指定value值则会使用方法名作为bean的名称,并且已经成功的将工厂方法产生的对象注入到IOC容器中了

 @Import注解

        当我们在被@Configuratin注解修饰的类中写多个被@Bean修饰的工厂方法时,可能会使修饰的类变得臃肿庞大,所以我们可以通过@Import注解将其它类中的被@Bean修饰的方法名引入进来来.

@Configuration
@ComponentScan(basePackages = {"cn_java_factory"})
@PropertySource("classpath:info.properties")
//引入其它类中的被@Bean修饰的工厂方法
@Import(PersonFactory.class)
public class UserDao {
}

PersonFactory类:

public class PersonFactory{
    //静态工厂
    @Bean("animals")
    public static Animal getAnimal(){
        return new Animal();
    }

    //实例工厂
    @Bean
    public Person getPerson(){
        return new Person();
    }
}

结果:

 可以看出工厂方法返回的对象也被注入到IOC中了

上面这些注解就是我们在spring配置中常用的注解了,感谢您的阅读!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值