前言
Spring 是 Java 后端程序员必须把握得一门框架技术,Spring 的横空出世,大大简化了企业级利用开发的复杂性。
Spring 框架中最外围的技术就是:
- IOC (管制反转):是面向对象编程中的一种设计准则,能够用来减低计算机代码之间的耦合度(百度百科)。艰深的说,转移对象创立的控制权,本来对象创立的控制权在开发者,当初通过 IOC 将控制权交给 Spring , 由 Spring 对立治理对象的创立、销毁等。
- AOP (切面编程):通过预编译形式和运行期间动静代理实现程序性能的对立保护的一种技术(百度百科)。艰深的说,将一些具备公共行为(如权限校验、日志记录等)封装到可重用的公共模块中,从何升高耦合度,进步程序的可重用性。
本文将次要介绍 Spring 中的 IOC 的依赖注入。
管制反转 IOC
IOC 次要由两种实现形式:
-
依赖查找(Dependency Lookup)
容器中的受控对象通过容器的 API 来查找本人所依赖的资源和合作对象。艰深的说,容器帮咱们创立好了对象,开发者须要什么就去容器中取。
-
依赖注入(Dependency Injection,简称 DI,IOC 最常见的形式)
是对依赖查找的优化,即无需开发者手动去容器中查找对象,只有通知容器须要什么对象,容器就会将创立好的对象进行注入。
依赖注入 DI
在 Spring 中依赖注入的模式次要有两种模式:
- 基于 xml 的模式
- 基于注解的模式
基于注解 DI 有三种表现形式:
- 基于属性注入
- 基于属性 setter 注入
- 基于结构器注入
三种惯例注入
基于属性注入
日常开发中最常应用的形式:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private PhoneService phoneService;
}
@Service
public class UserServiceImpl implements UserService {
@Resource
private PhoneService phoneService;
}
@Autowired
注解和 @Resource
注解的区别:
@Autowired | @Resource |
---|---|
Spring 的注解,它的包是 org.springframework.beans.factory.annotation.Autowired | 不是 Spring 的注解,它的包是 javax.annotation.Resource |
只依照 byType 注入 | 默认依照 byName 主动注入 |
无属性 | 有两个重要的属性:name 和 type |
@Resource
的拆卸程序:
- 如果同时指定了 name 和 type,则从 Spring 上下文中找到惟一匹配的 bean 进行拆卸,找不到则抛出异样。
- 如果指定了 name,则从上下文中 查找名称匹配 的 bean 进行拆卸,找不到则抛出异样。
- 如果指定了 type,则从上下文中 查找类型匹配的惟一 bean 进行拆卸,找不到或是找到多个,都会抛出异样。
- 如果既没有指定 name,又没有指定 type,则 主动依照 byName 形式 进行拆卸;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则主动拆卸。
基于 setter 办法注入
@Service
public class UserServiceImpl implements UserService {
private PhoneService phoneService;
@Autowired
public void setPhoneService(PhoneService phoneService) {this.phoneService = phoneService;}
}
基于结构器注入(Spring 官网举荐)
@Service
public class UserServiceImpl implements UserService {
private final PhoneService phoneService;
@Autowired
public UserServiceImpl(PhoneService phoneService) {this.phoneService = phoneService;}
}
为何 Spring 官网举荐应用结构器注入呢?
- 保障依赖不可变(final 关键字)
- 保障依赖不为空(省去了程序启动因注入对象为空而报异样)
- 防止循环依赖
- 晋升了代码的可复用性(非 IOC 环境下,可应用 new 实例化该类的对象)
接口注入
当咱们用下面三种形式注入接口时,接口有多个实现类时,程序启动就会报错,因为 Spring 不晓得要注入哪个实现类。
那么要如何解决接口多实现类的注入问题呢?
-
通过
@Autowired
注解联合@Qualifier
注解@Service public class UserServiceImpl implements UserService { @Autowired @Qualifier("applePhoneServieImpl") private PhoneService phoneService; }
@Qualifier("applePhoneServieImpl")
指定要引入的具体实现类。 -
通过
@Resource
注解动静获取@Service public class UserServiceImpl implements UserService {@Resource(name = "applePhoneServieImpl") private PhoneService phoneService; }
-
通过
@Primary
注解优先注入@Service @Primary public class ApplePhoneServieImpl implements PhoneService {}
@Primary
注解示意当有多个 bean 满足注入条件时,会优先注入该注解润饰的 bean。 -
通过
@ConditionalOnProperty
注解联合配置文件注入@Service @ConditionalOnProperty(prefix = "phone", name = "impl", havingValue = "vivo") public class VivoPhoneServieImpl implements PhoneService { }
@ConditionalOnProperty(prefix = "phone", name = "impl", havingValue = "vivo")
意指当配置文件中phone.impl=vivo
时,VivoPhoneServieImpl
才会注入到容器中。 -
通过其余 @Condition 条件注解
@ConditionalOnBean
:当存在某一个 Bean 时,初始化此类到容器。@ConditionalOnClass
:当存在某一个类时,初始化此类的容器。@ConditionalOnMissingBean
:当不存在某一个 Bean 时,初始化此类到容器。@ConditionalOnMissingClass
:当不存在某一个类时,初始化此类到容器。- …
-
通过汇合注入
@Service public class UserServiceImpl implements UserService { @Autowired private List<PhoneService> phoneServiceList; @Autowired private Map<String, PhoneService> phoneServiceMap; }
获取 Bean 的形式
在 Spring 我的项目中,有时候须要手动去获取 bean,手动获取的形式须要通过 applicationContext
,有以下几种模式:
-
通过
applicationContext
获取@Service public class UserServiceImpl implements UserService { @Autowired private ApplicationContext applicationContext; public Object getBean() {return applicationContext.getBean("appleService"); } }
-
实现
ApplicationContextAware
接口@Component public class SpringContextUtil implements ApplicationContextAware { public static ApplicationContext applicationContext; public void setApplicationContext(ApplicationContext applicationContext) {SpringContextUtil.applicationContext = applicationContext;} public static Object getBean(String name) {return applicationContext.getBean(name); } public static <T> T getBean(Class<T> clazz) {return applicationContext.getBean(clazz); } public static <T> T getBean(String name, Class<T> clazz) {return applicationContext.getBean(name, clazz); } public static Boolean containsBean(String name) {return applicationContext.containsBean(name); } public static Boolean isSingleton(String name) {return applicationContext.isSingleton(name); } public static Class<? extends Object> getType(String name) {return applicationContext.getType(name); } }
PhoneService phoneService = SpringContextUtil.getBean(PhoneService.class);
-
继承自抽象类
ApplicationObjectSupport
@Component public class SpringContextHelper extends ApplicationObjectSupport {public Object getBean(String name) {return getApplicationContext().getBean(name); } public <T> T getBean(Class<T> clazz) {return getApplicationContext().getBean(clazz); } public <T> T getBean(String name, Class<T> clazz) {return getApplicationContext().getBean(name, clazz); } public Boolean containsBean(String name) {return getApplicationContext().containsBean(name); } public Boolean isSingleton(String name) {return getApplicationContext().isSingleton(name); } public Class<? extends Object> getType(String name) {return getApplicationContext().getType(name); } }
-
继承自抽象类
WebApplicationObjectSupport
WebApplicationObjectSupport
继承了ApplicationObjectSupport
,所以应用办法和下面一样。
总结
本文次要记录了 Spring 中基于注解的 DI 几种形式,接口多个实现的注入形式,如何通过 applicationContext 手动获取 bean。