自动装配

### 1. 自动装配:Autowire

自动装配是一种自动为Bean的属性注入值的做法!

假设存在`Student`类:

public class Student {
    
        private City from;
    
        public City getFrom() {
            return from;
        }
    
        public void setFrom(City from) {
            this.from = from;
        }
    
    }

类的属性希望被自动的注入值,则应该配置它的`autowire`属性:

<bean id="student"
        class="cn.tedu.spring.Student"
        autowire="default">
    </bean>

关于`autowire`属性,取值可以是`byName`,表示**根据名称实现自动装配**,即:要求类中的属性名与被装配进来的Bean的id保持一致(其实本质上还是通过SET方法注入的,要求的是SET方法的名称与Bean的id保持一致),所以:

    <bean id="from"
        class="cn.tedu.spring.City" />
    
    <bean id="student"
        class="cn.tedu.spring.Student"
        autowire="byName">
    </bean>

自动装配其实是**尝试自动装配**,即:能够装配就装配,不能装配也不会报错!

配置时,`autowire`属性的取值还可以是`byType`,即**根据类型实现自动装配**,Spring容器在尝试自动装配时,会在容器中(Spring管理的范围内)查找类型匹配的对象,如果没有,则不装配,如果有1个,则可以成功的自动装配,如果匹配类型的有2个或更多,则抛出异常`UnsatisfiedDependencyException`!

除此以外,该属性还可以有其它取值,但是,通常,并不会在开发中使用该属性,因为它存在是否装配不明确的问题!而且,在实际开发中,其实自定义的Bean根本就不会在XML文件中配置,所以更加无处使用该属性!

关于自动装配,重点理解`byName`和`byType`的特点。

### 2. 注解

#### 2.1. 为什么要使用注解

使用XML应用Spring时,需要使用大量的配置,导致代码的可读性较差(在阅读代码时,经常需要结合多个Java文件和XML配置一起查阅),并且,在XML配置中,如果出现拼写错误也不会实时提示错误信息!

在实际使用时,会大量的使用注解,来取代XML中的配置!

#### 2.2. 基本注解

如果希望某个类被Spring管理,可以在类上添加`@Component`注解,并且,在Spring的配置文件中添加组件扫描的配置:

<!-- 组件扫描 -->
    <!-- base-package:根包 -->
    <context:component-scan 
        base-package="cn.tedu.spring" />

以上配置中的`base-package`用于指定组件所在的包,当加载该配置文件时,Spring框架会扫描指定的包,如果类之前添加了注解,则会被Spring管理。

扫描的包之所以是`base-package`,表示各个类在其各层级的子包下,都会被扫描到,例如配置为`cn.tedu.spring`时,如果类是`cn.tedu.spring.dao.UserDao`,也是在扫描范围之内的!

默认情况下,会使用类名且首字母改为小写作为Bean的id,所以,如果被Spring管理的是`Student`类,则默认的id就是`student`,当然,如果觉得默认的id命名不满足当前的应用需求,可以配置注解属性:

@Component("stu")
    public class Student {
    }

与`@Component`相同的注解还有:

- `@Controller`:如果某个类在应用中起到的作用是控制器,则应该使用该注解
- `@Service`:如果某个类在应用中起到的作用是业务逻辑类,则应该使用该注解
- `@Repository`:如果某个类在应用中起到的作用是持久层处理(增删改查),则应该使用该注解

其实,以上4个的作用、语法是完全相同的,只是语义不同,建议区分使用。

#### 2.3. 【不常用】 作用域与生命周期的注解

使用`@Scope`注解可以配置Bean的作用域,例如希望实现非单例效果时:

    @Component("stu")
    @Scope("prototype")
    public class Student extends Object {
    
    }

在单例的情况下,如果需要单例模式是懒汉式的,则可以使用`@Lazy`注解:

@Component("stu")
    @Lazy
    public class Student extends Object {
    
        public Student() {
            System.out.println("Student()");
        }
        
    }

其实,`@Lazy`注解也可以配置属性值为`true`或`false`,但是,没有添加该注解时就是饿汉式的,添加该注解表示懒汉式且值为`true`,所以,不必配置属性值!

关于生命周期方法:

    @PostConstruct
    public void init() {
        System.out.println("Student.init()");
    }
    
    @PreDestroy
    public void destroy() {
        System.out.println("Student.destroy()");
    }


注意:以上2个注解是`javax`包中的注解,应该添加Tomcat或其它服务端环境。

### 2.4. 自动装配的注解

假设存在`Student`类中的`public City from;`属性需要被装配值:

    @Component
    public class Student {
        public City from;
    }

首先,`City`应该在Spring管理范围之内,即:需要在组件扫描范围之内,且添加了注解:

    package cn.tedu.spring;

    @Component
    public class City {
    }

然后,在`Student`类的`from`属性之前,添加`@Autowired`注解即可:

@Autowired
    public City from;


与使用XML配置自动装配不同,注解方式的自动装配并不要求属性有SET方法,对属性的访问权限也没有要求,即使属性是私有的,也不影响装配过程!

`@Autowired`注解是优先`byType`也可以byName!所以,要求匹配类型的Bean对象有且仅有1个(0个则无法装配,属性值为null,多个则直接抛出异常)!如果项目中确实有多个匹配类型的类,则只保留1个被Spring管理,其它的不由Spring管理即可,例如去掉类之前的`@Component`或相同作用的注解,或把这些类转移到组件扫描的包以外去。

使用`@Resource`注解也可以实现自动装配,它是优先`byName`,如果失败,则会尝试`byType`。并且,使用`@Resource`时,如果一定要根据名称来装配,却存在名称不匹配的问题,可以在注解中配置名称:

@Resource(name="city")
    public City from;

以上`@Autowired`和`@Resource`都可以实现自动装配,在实际应用中,使用任何一个都可以!因为`@Resource`功能更强,所以很多人喜欢使用它,但是,并不是每个Java项目都是服务端项目,某些项目中可能并没有`javax`包中的内容,也就根本没有`@Resource`注解,就只能使用`@Autowired`,当然,这样的项目毕竟是少数,所以,在使用Java开发的Web应用程序中,使用这2种注解中的任何一个都是正确的做法!

### 2.5. 小结

常用注解:

- `@Controller`
- `@Service`
- `@Component`
- `@Autowired` / `@Resource`

非常用注解:

- `@Repository`
- `@Scope`
- `@Lazy`
- `@PostConstruct`
- `@PreDetroy`

### 【附】 关于单例模式

    public class King {
        private static King king;

        private King() {
        }

        public static King getInstance() {
            synchronized("lock") {
                if (king == null) {
                    king = new King();
                }
            }
            return king;
        }

    }
原文地址:https://www.cnblogs.com/topzhao/p/10290738.html