做为一名java开发人员,咱们接触最先最多的框架确定就是spring了,一次次不停的使用spring框架提供的功能帮助咱们快速开发,然而其中的核心功能IOC(控制反转)和AOP能详细阐明的却很少,咱们知其然,殊不知其因此然,那么下面咱们就一块儿探索一下spring中的IOC吧java
例:在现实生活中个人头发长了,影响了个人帅气,那么我就须要理发,我不可能本身给本身理发,首先我没有理发的工具,其次我可能把本身的头发剪得像狗啃的同样,那么影响我帅气的外表,就没有妹子愿意跟我一块儿约会了,这确定是我不肯意发生的。其次,我也不是王思聪,我也不愿能拥有一名只为本身服务的人员,那么正确的作法就是去村口的理发店找Tony老师来给我打造帅气的发型。咱们来分解一下剪发的过程,我是需求提出者,头发变帅是要的结果,首先若是我本身为本身理发,不只可能达不到预期的效果,并且还费时费力,那么我就须要一名专业的理发师来为我服务,达到最终的目的,这个过程咱们可能称之为解耦合的过程。
在Java开发中,一个功能的实现每每是由多个类和方法来共同协做完成的,在没有IOC以前咱们建立一个类的依赖类的方式是new object(),控制权在本类手中,这些依赖关系将会使系统的复杂度提升,不利于维护和开发,这是咱们不肯意看到的,在有了IOC以后,一个类所须要的依赖类由IOC来管理,那么咱们只要关心自己类的功能和方法便可,将控制权交给了IOC容器,这就是我理解的控制反转。web
其中BeanFactory是IOC容器的基本实现,ApplicationContext是BeanFactory的子接口,提供更高级的特性。 而下面几个类则是具体的实现类,能够加载配置文件中定义的bean,管理全部加载的bean,有请求的时候分配bean。spring
建立应用对象之间的关联关系的传统方法(经过构造器或者查找)一般会致使结构复杂的代码,这些代码难以被复用也很难进行单元测试,若是状况不严重的话,这些对象所作的事情只是超出了应该作的范围,而最坏的状况是,这些对象的彼此之间高度耦合,难以复用和测试。
在spring中,对象无需本身查找或建立与其所关联的其余对象。相反,容器负责把须要互相协做的对象引用赋予各个对象。例如,一个订单管理组件须要信用卡认证组件,但它不须要本身建立信用卡认证组件。订单管理组件只须要代表本身两手空空,容器就会主动赋予它一个信用卡认证组件。建立应用对象之间协做关系的行为一般称为装配(wiring),这也是依赖注入(DI)的本质。(摘录spring实战第四版第二章)编程
1:在Java中进行显式配置 (我的喜欢)
咱们定义接口以及其实现类springboot
package com.lly.springtest1.ioc;
/**
* @Author lly
* @Description 水果接口类
* @Date 2019/1/29 10:23 AM
* @Param
* @return
**/
public interface IFruitService {
/**
* @Author lly
* @Description 显示水果信息
* @Date 2019/1/29 10:25 AM
* @Param []
* @return
**/
void showFruitInfo();
}
复制代码
package com.lly.springtest1.ioc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName OrangeServiceImpl
* @Description 水果实现类-橘子
* @Author lly
* @Date 2019/1/29 10:17 AM
* @Version 1.0
**/
@Component
@Data
@Slf4j
public class OrangeServiceImpl implements IFruitService {
@Override
public void showFruitInfo() {
log.info("橘子的重量是:{}kg",10);
}
}
复制代码
扫描组件配置类,@ComponentScan注解会默认扫描与其配置类相同的包以及这个包下面的全部的子包,查找带有@Component注解的类,固然咱们能够直接定@ComponentScan扫描的包bash
#单个包
@ComponentScan("包名")
@ComponentScan(basePackage="包名")
#多个包
@ComponentScan(basePackage={"包名","包名"})
复制代码
package com.lly.springtest1.ioc;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @ClassName TestIoc
* @Description 扫描组件配置类
* @Author lly
* @Date 2019/1/29 10:30 AM
* @Version 1.0
**/
@ComponentScan
@Configuration
public class TestIoc {
}
复制代码
单元测试session
package com.lly.springtest1.ioc;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestIoc.class)
public class TestIocTest {
@Autowired
private IFruitService iFruitService;
@Test
public void showInfo(){
iFruitService.showFruitInfo();
}
}
复制代码
结果框架
2:隐式的bean发现机制和自动装配 (我的喜欢)
其实这种方式咱们在 探索Spring系列(一)Spring容器和Bean的生命周期 这里章节已经见到过了,下面贴出核心代码ide
package com.lly.springtest1.entity;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanLifeCycle {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanLifeCycle.class);
context.close();
}
@Bean
public MyBeanPostProcessor getBean() {
return new MyBeanPostProcessor();
}
@Bean(initMethod = "myInit",destroyMethod = "myDestroy")
public GirlFriendEntity getGirl() {
GirlFriendEntity girl = new GirlFriendEntity();
girl.setName("颖宝");
return girl;
}
}
复制代码
这种配置的方式也能够将bean归入spring容器的管理中,下面咱们来测试一下工具
package com.lly.springtest1.entity;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = BeanLifeCycle.class)
public class BeanLifeCycleTest {
@Autowired
private MyBeanPostProcessor myBeanPostProcessor;
@Test
public void show(){
Assert.assertNotNull(myBeanPostProcessor);
}
}
复制代码
3:xml中显示配置(我的不喜欢这种方式,配置很繁琐,有兴趣的同窗自行了解学习)
4:总结,上述几种装配bean的方式,均可以实现一样的功能,也能够混合使用,使用哪一种方式彻底能够按照开发者我的的习惯和喜爱来决定,可是做者目前使用的都是前两种,消除配置式编程,更快乐,在目前比较流行的springboot开发中也是推荐前两种
1:消除歧义性 在上文中咱们定义个一个IFruitService,orangeEntity 这个类实现了这个接口,咱们在测试类中是直接注入的,那么我能够想一想一下,在实际的开发中,可能存在一个接口对应多个实现类的状况,spring在帮我自动注入的时候是怎么帮咱们选择的呢,下面咱们来实验一下。
咱们再建立一个水果的实现类 香蕉
package com.lly.springtest1.ioc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @ClassName BananaServiceImpl
* @Description 水果实现类-橘子
* @Author lly
* @Date 2019/1/29 10:17 AM
* @Version 1.0
**/
@Component
@Data
@Slf4j
public class BananaServiceImpl implements IFruitService {
@Override
public void showFruitInfo() {
log.info("香蕉的重量是:{}kg",100);
}
}
复制代码
而后咱们再次启动测试类发现
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lly.springtest1.ioc.IFruitService' available: expected single matching bean but found 2: bananaServiceImpl,orangeServiceImpl
复制代码
经过查看咱们发现IFruitService这个接口有2个实现类,咱们没有指定要使用哪一个,那么spring是不会知道咱们将要使用哪一个的。
解决方法:
spring提供2中方式来解决这个问题
第一种:使用@Qualifier注解来指定咱们要使用的具体实现类
咱们能够看到在咱们指定了具体实现类后测试用例顺利经过。
第二种:使用@Primary注解来指定哪一个实现类做为首选实现类,咱们在香蕉类上来加上这个注解
package com.lly.springtest1.ioc;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
/**
* @ClassName BananaServiceImpl
* @Description 水果实现类-橘子
* @Author lly
* @Date 2019/1/29 10:17 AM
* @Version 1.0
**/
@Component
@Data
@Slf4j
@Primary
public class BananaServiceImpl implements IFruitService {
@Override
public void showFruitInfo() {
log.info("香蕉的重量是:{}kg",100);
}
}
复制代码
测试类改造以下
package com.lly.springtest1.ioc;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestIoc.class)
public class TestIocTest {
@Autowired
private IFruitService iFruitService;
@Test
public void showInfo(){
iFruitService.showFruitInfo();
}
}
复制代码
启动查看结果
1)单例(singleton)在整个应用中,只建立一个实例;(默认状况下,spring应用上下文中全部的bean都是单例模式)。 2)原型(prototype)每次注入或者经过spring应该上下文获取的都是一个新的实例。 3)会话(session)在web应用中,为每一个会话建立一个bean实例。 4)请求(request)在web应用中。为每一个请求建立一个bean实例。
注解式指定bean做用域
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.INTERFACES)
@Scope(value = WebApplicationContext.SCOPE_REQUEST,proxyMode = ScopedProxyMode.INTERFACES)
复制代码
另外还可使用xml配置式,这里不作详解
工欲善其事必先利其器,spring做为咱们使用最频繁的框架,熟悉其主要功能和原理是颇有必有的,否则咱们一直摸着石头过河,下面一章咱们将要介绍spring另一个重要功能AOP(面向切面编程)