一、Spring 框架两大核心机制——IoC
- 设计思想:IoC(控制反转)
- 手段:DI(依赖注入)
二、Spring-Bean依赖注入
1、设计原则:依赖倒置原则
1、高层模块不应该依赖底层模块,二者都应该依赖抽象。
2、抽象不应该依赖细节,细节应该依赖抽象。
3、依赖倒置的中心思想是面向接口编程。
4、依赖倒置原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定的多。
5、使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类来完成。
经典案例:高层模块依赖于底层实现模块
/*高层模块*/
public class Driver {
//司机的主要职责就是驾驶汽车
public void drive(Benz benz){
benz.run();
}
}
/*底层实现模块*/
public class Benz {
//汽车肯定会跑
public void run(){
System.out.println("奔驰汽车开始运行...");
}
}
驾驶人依赖于开奔驰,而不会开宝马
/*底层实现模块*/
public class BMW {
//宝马车当然也可以开动了
public void run(){
System.out.println("宝马汽车开始运行...");
}
}
- 接口传递
- 构造方法传递
- setter方法传递
-
1、接口传递对象
高层模块依赖于对象实现的接口,而对象通过实现接口方法,不与高层模块建立依赖关系。
//将汽车模块抽象为一个接口:可以是奔驰汽车,也可以是宝马汽车 public interface ICar { //是汽车就应该能跑 public void run(); } public class Benz implements ICar{ //汽车肯定会跑 public void run(){ System.out.println("奔驰汽车开始运行..."); } } public class BMW implements ICar{ //宝马车当然也可以开动了 public void run(){ System.out.println("宝马汽车开始运行..."); } }
/*高层模块依赖对象接口*/ public class Driver implements IDriver{ //司机的主要职责就是驾驶汽车 public void drive(ICar car){ car.run(); } }
-
2、构造方法传递对象
//将司机模块抽象为一个接口 public interface IDriver { public void drive(); } public class Driver implements IDriver{ //有参构造器注入依赖 private ICar car; public Driver(ICar car){ this.car = car; } public void drive(){ this.car.run(); } }
-
3、setter方法传递对象
public class Driver implements IDriver{ private ICar car; /*setter方法注入依赖*/ public void setCar(ICar car){ this.car = car; } public void drive(){ this.car.run(); } }
2、Bean的作用
- 什么是Bean?
Spring Bean是被实例的,组装的被Spring 容器管理的Java对象。
Spring 容器会自动完成@Bean对象的实例化。
创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。
Bean是把需要被调用实例化的对象由Spring容器管理,相当于定义一个组件,这个组件是用于具体实现某个功能的。
- 传统的实例化对象:
//new 一个person对象
Person person = new Person();
//实例化person类的行为说话
Say say = new Say();
person.setName("小明");
person.behavier(say);
//new一个service
UserSevice userservice = new UserService();
//调用servie方法
userService.login();
在传统的程序开发中,需要调用对象时,通常由调用者来创建被调用者的实例,即对象是由调用者主动new出来的。
通过Bean的依赖注入,让Spring控制对象的实例化,开发者即可直接使用Bean的属性或者方法,省去了new对象的注入,降低代码间的依赖关系,使对象开箱即用。
-
通过 IoC 容器创建对象,把Bean添加到Spring组件中
Spring三种装配bean的方式
1.在XML中进行显示配置
2.使用Java代码进行显示配置
3.隐式的bean发现机制和自动装配
推荐方式: 3>2>1
3、基于XML装配bean的三种实例化方式
实例化方式
首先我们应该知道Spring中基于XML装配bean时有三种实例化方式,分别是
- 默认构造
- 静态工厂
- 实例工厂
1)默认构造
接口加入组件,成为Bean对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
">
<bean name=“userService” class="com.shanmu.UseServiceImpl"></bean>
</beans>
UseServiceImpl实现UserService接口
public class UseServiceImpl implements UserService{
@Override
public void saveUser(){
System.out.print("i am a user");
}
}
调用实现类的方法saveUser()
public static void main(String[] args){
//不采用spring
UserService userService =new UseServiceImpl();
userService.saveUser();
//采用spring Bean默认构造方法
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService =(UserService)context.getBean("userService");
userService.saveUser();
}
2)静态工厂
静态工厂是用于生成实例对象的,工厂类中的所有方法必须是静态的
首先我们需要一个工厂类,这个工厂类中有一个静态方法帮我们得到了UseServiceImpl实例,所以我们后面只需要直接调用静态方法即可
下面是工厂类,通过这个类我们就可以得到UserService
public class MyBeanFactory(){
public static UserService createService(){
return new UserServiceImpl();
}
}
xml中注册Bean组件
<bean name=“userService” class="com.*.MyBeanFactory" factory-method="CreateService">
</bean>
调用静态工厂类方法
public static void main(String[] args){
//不采用spring
UserService userService=MyBeanFactory.createService();
userService.saveUser();
//采用spring Bean默认构造方法
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService =(UserService)context.getBean("userService");
userService.saveUser();
}
3)实例工厂
实例工厂一样是需要有一个工厂类,但是与静态工厂不一样的地方在于里面的方法都是“非静态的”,所以我们需要得到一个工厂类的实例才能得到一个UseServiceImpl实例
public class MyBeanFactory(){
public UserService createService(){
return new UserServiceImpl();
}
}
需要在spring.xml中这样配置
<!--先对工厂类实例化注入到Bean中-->
<bean name=“myBeanFactory” class="com.*.MyBeanFactory">
</bean>
<!-- 引用工厂类的Bean方法-->
<bean name=“userService” factory-bean="myBeanFactory" factory-method="createService">
</bean>
调用工厂方法需先实例化工厂对象,在进行方法调用
public static void main(String[] args){
//不采用spring
MyBeanFactory myBeanFactory=new MyBeanFactory();//实例化工厂
UserService userService=myBeanFactory.createService();//调用实例化工厂返回实现类
userService.saveUser();
//采用spring默认构造方法
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService =(UserService)context.getBean("userService");
userService.saveUser();
}
4、基于注解@Component
自动装配优势:
- 便利,自动化装配,隐式配置代码量少。
自动装配限制:
- 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
- 装配依赖中若是出现匹配到多个bean(出现歧义性),装配将会失败。
4.1 Spring实现自动装配两个步骤:
- 组件扫描(component scanning):Spring会扫描@Component注解的类,并会在应用上下文中为这个类创建一个bean。
- 自动装配(autowiring):Spring自动满足bean之间的依赖。
使用到的注解:
-
@Component
:表明这个类作为组件类,并告知Spring要为这个类创建bean。默认bean的id为第一个字母为小写类名,可以用value指定bean的id。 -
@Configuration
:代表这个类是配置类。 -
@ComponentScan
:启动组件扫描,默认会扫描所在包以及包下所有子包中带有@Component注解的类,并会在Spring容器中为其创建一个bean。可以用basePackages属性指定包。 -
@RunWith
(SpringJUnit4ClassRunner.class):以便在测试开始时,自动创建Spring应用上下文。 -
@ContextConfiguration
:指定类加载配置。相当于new一个**AnnotationConfigApplicationContext(config.class)**配置类加载Spring的应用上下文
-
@Autowired
:将一个类的依赖bean装配进来。@Autowired CDPlayer cdPlayer;
public class MediaPlayer implements CDPlayer { private final CDDisk cdDisk; @Autowired public MediaPlayer(CDDisk cdDisk) { this.cdDisk = cdDisk; } }
定义功能类组件
@Component/*声明为Bean对象,相当于注册到Spring容器中的组件*/
public class HuaHua implements CDDisk {
@Override
public void sing() {
String title = "烟火里的尘埃";
String singer = "华晨宇";
System.out.println(title + "_" + singer);
}
}
@Component/*声明为Bean对象,相当于注册到Spring容器中的组件*/
public class MediaPlayer implements CDPlayer {
/*Spring Ioc容器自动注入Bean依赖
--这里使用构造方法依赖注入*/
private final CDDisk cdDisk;
@Autowired
public MediaPlayer(CDDisk cdDisk) {
this.cdDisk = cdDisk;
}
@Override
public void play() {
cdDisk.sing();
}
}
配置类
Spring会扫描@Component注解的类,并会在应用上下文中为这个类创建一个bean。
@Configuration//声明这是一个配置类
/*启动组件扫描,默认会扫描所在包以及包下所有子包中带有@Component注解的类,并会在Spring容器中为其创建一个bean。*/
@ComponentScan("注册Bean对象的包路径")
public class CDPlayerConfig {
}
引用Bean对象
-
使用AnnotationConfigApplicationContext可以实现基于Java的配置类加载Spring的应用上下文
AnnotationConfigApplicationContext
作为Spring容器,接受输入一个配置类作为参数
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);//创建应用上下文指定配置类
CDPlayer cdPlayer = context.getBean(CDPlayer.class);//获取Ioc容器中的Bean对象
cdPlayer.play();
}
- 自动装配**@Autowired**、注解声明指定类加载配置@ContextConfiguration
@ContextConfiguration(classes = CDPlayerConfig.class)//指定类加载配置
public class test2 {
@Autowired
CDPlayer cdPlayer;
public void test() {
cdPlayer.play();
}
}
5、Java配置注册Bean
Java配置是通过@configuration和@Bean来实现的
@Configuration可理解为用spring的时候xml里面的标签
@Bean可理解为用spring的时候xml里面的标签
场景:
尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化更为推荐,但是有时候行不通。比如引用第三方组件,没办法在它的类上添加@Component及@Autowired。所以就需要JavaConfig或者XML配置
JavaConfig是配置相关代码,不含任何逻辑代码。通常会将JavaConfig放到单独的包中。
优点:
- 可以实现基本数据类型的值、字符串字面量、类字面量等注入。
使用到的注解:
- @Bean:注解在方法上,声明当前方法的返回值为一个Bean。默认情况下配置后bean的id和注解的方法名一样,可以通过name属性自定义id。
- @ImportResourse:将指定的XML配置加载进来
- @Import:将指定的Java配置加载进来。
5.1 创建JavaConfig类
@Configuration
public class CDPlayerConfig {
}
使用@Configuration表明CDPlayerConfig是一个配置类
5.2 声明简单的bean
@Bean
public IMediaPlayer cdplayer(JayCompactDisc dis) {
return new VCDPlayer(dis);
}
@Bean注解会告诉Spring将返回一个的对象注册为Bean
Spring上加@Bean注解的都是默认单例模式
默认情况下,@Bean的Id与带有@Bean的方法名一样。当然也可以通过@Bean的name属性指定额外的方法名。
5.3 Java配置和注解混合配置
- 全局配置使用Java配置(如数据库配置、MVC相关配置)
:将指定的Java配置加载进来。
5.1 创建JavaConfig类
@Configuration
public class CDPlayerConfig {
}
使用@Configuration表明CDPlayerConfig是一个配置类
5.2 声明简单的bean
@Bean
public IMediaPlayer cdplayer(JayCompactDisc dis) {
return new VCDPlayer(dis);
}
@Bean注解会告诉Spring将返回一个的对象注册为Bean
Spring上加@Bean注解的都是默认单例模式
默认情况下,@Bean的Id与带有@Bean的方法名一样。当然也可以通过@Bean的name属性指定额外的方法名。
5.3 Java配置和注解混合配置
- 全局配置使用Java配置(如数据库配置、MVC相关配置)
- 业务Bean的配置使用注解配置(@Service、@Component、@Repository、@Cotroller)