1. 单例和多例(Bean的作用域):
Spring默认情况下是如何管理这个Bean的:
默认情况下Bean是单例的 (单例: singleton)
在Spring上下文初始化的时候实例化
每一调用getBean()方法的时候, 都返回那个单例的对象
// xml
// scope默认情况下就是singleton单例的
<bean id="dog" class="com.powernode.bean.Dog"></bean>
// @Test
public void test(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("set-DI.xml");
Dog dog1 = ctx.getBean("dog", Dog.class);
System.out.println(dog1);// com.powernode.bean.Dog@4e41089d
Dog dog2 = ctx.getBean("dog", Dog.class);
System.out.println(dog2);// com.powernode.bean.Dog@4e41089d
Dog dog3 = ctx.getBean("dog", Dog.class);
System.out.println(dog3);// com.powernode.bean.Dog@4e41089d
}
当将bean的scope属性设置为prototype
bean是多例的
spring上下文初始化的时候, 并不会初始化这些prototype的bean
每一次调用getBean()方法的时候, 实例化该bean对象
prototype翻译为: 原型
// xml
<bean id="dog" class="com.powernode.bean.Dog" scope="prototype"></bean>
// @Test
public void test(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("set-DI.xml");
Dog dog1 = ctx.getBean("dog", Dog.class);
// 构造方法执行!
// com.powernode.bean.Dog@4e41089d
System.out.println(dog1);
Dog dog2 = ctx.getBean("dog", Dog.class);
// 构造方法执行!
// com.powernode.bean.Dog@32a068d1
System.out.println(dog2);
Dog dog3 = ctx.getBean("dog", Dog.class);
// 构造方法执行!
// com.powernode.bean.Dog@33cb5951
System.out.println(dog3);
}
scope属性的值:
singleton: 单例
prototype: 原型/多例
引入springMVC框架之后:
request: 一次请求当中一个bean, 仅限于在WEB应用中使用
session: 一次会话中只有一个bean, 仅限于在WEB应用中使用
2. GoF之工厂模式:
设计模式: 一种可以被重复利用的解决方案
2.1 工厂模式的三种形态:
简单工厂模式: 不属于23种设计模式之一, 又叫做静态工厂方法模式, 简单工厂模式是工厂方法模式的一种特殊实现
工厂方法模式: 是23种设计模式之一
抽象工厂模式: 是23 种设计模式之一
2.2 简单工厂模式:
简单工厂模式是工厂方法模式的一种特殊实现, 又被称为: 静态工厂方法模式
简单工厂模式解决什么问题?
客户端程序不需要关心对象的创建细节, 需要哪个对象时, 只需要向工厂索要即可, 初步实现了责任的分离, 客户端只负责消费, 工厂负责生产, 生产和消费分离开来.
简单工厂模式中的角色:
抽象产品角色 Weapon
具体产品角色 Gun
工厂类角色 WeaponFactory
简单工厂模式的缺点:
缺点1: 工厂类集中了所有产品的创造逻辑, 形成了一个无所不知的全能类, 工厂类非常关键, 不能出问题, 一旦出了问题, 整个系统就会瘫痪
缺点2: 不符合OCP开闭原则, 在进行系统功能扩展的时候, 需要修改工厂类
Spring中的BeanFactory就使用了简单工厂模式
// 抽象产品
public abstract class Weapon {
// 所有的武器都可以攻击
public abstract void attack();
}
// 具体产品
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("砍他!!!");
}
}
public class Fighter extends Weapon{
@Override
public void attack() {
System.out.println("战斗机丢炸弹!!!");
}
}
public class Tank extends Weapon{
@Override
public void attack() {
System.out.println("坦克开炮!!!");
}
}
// 工厂类
public class WeaponFactory {
/**
* 静态方法: 要获取什么产品? 就看你传什么参数
* 简单工厂模式中有一个静态方法, 所以被称为: 静态工厂方法模式
*/
public static Weapon get(String weaponType){
if("TANK".equals(weaponType)){
return new Tank();
} else if("DAGGER".equals(weaponType)){
return new Dagger();
} else if("FIGHTER".equals(weaponType)){
return new Fighter();
} else {
throw new RuntimeException("不支持该武器的生产!");
}
}
}
// 客户端程序
public class Test {
public static void main(String[] args) {
// 需要坦克
// 对于我客户端来说, 坦克的生产细节, 我不需要关心, 我只需要向工厂索要就行
// 简单工厂模式达到了什么呢? 职责分离, 客户端不需要关心产品的生产细节
// 客户端只负责消费, 工厂类负责生产, 一个负责生产, 一个负责消费, 生产者和消费者分离, 者就是简单工厂的作用
Weapon tank = WeaponFactory.get("TANK");
tank.attack();
Weapon fighter = WeaponFactory.get("FIGHTER");
fighter.attack();
Weapon dagger = WeaponFactory.get("DAGGER");
dagger.attack();
Tank tank1 = new Tank();
tank1.attack();
}
}
2.3 工厂方法模式:
工厂方法模式既保留了简单工厂模式的优点, 同时又解决了简单工厂模式的缺点
工厂方法模式中的优点:
当你扩展一个产品的时候, 符合OCP原则, 因为只需要俩个类, 一个类是具体产品类, 一个类是具体工厂类, 都是添加类, 没有修改之前的代码, 所以符合OCP
一个调用者想创建一个对象, 只要知道其名称就可以了
屏蔽产品的具体实现, 调用者只关心产品的接口
工厂方法模式的缺点:
每次增加一个产品的话, 都需要增加一个具体类和对象实现工厂, 使得系统中类的个数成倍增加
在一定程度上增加了系统的复杂度, 同时也增加了系统具体类的依赖, 这并不是好事.
工厂方法模式的角色包括:
抽象产品角色 Weapon
具体产品角色 Dagger Gun
抽象工厂角色 WeaponFactory
具体工厂角色 DaggerFactory GunFactory
一个产品一个工厂
// 抽象产品角色
public abstract class Weapon {
// 所有的武器都可以攻击
public abstract void attack();
}
// 具体产品角色:
public class Tank extends Weapon {
@Override
public void attack() {
System.out.println("坦克开炮!!!");
}
}
public class Fighter extends Weapon {
@Override
public void attack() {
System.out.println("战斗机丢炸弹!!!");
}
}
// 具体工厂角色:
public class TankFactory extends WeaponFactory{
@Override
public Weapon get() {
return new Tank();
}
}
public class FighterFactory extends WeaponFactory{
@Override
public Weapon get() {
return new Fighter();
}
}
// 抽象工厂角色
abstract public class WeaponFactory {
// 这个方法不是静态的, 是实例方法
public abstract Weapon get();
}
// 这是客户端程序
public class Test {
public static void main(String[] args) {
WeaponFactory weaponFactory = new TankFactory();
Weapon tank = weaponFactory.get();
tank.attack();
WeaponFactory weaponFactory1 = new FighterFactory();
Weapon fighter = weaponFactory1.get();
fighter.attack();
}
}
3. Bean的获取方式(4种):
Spring为Bean提供了多种实例化方式, 通常包括4种方式, 也就是说在spring中为Bean对象的创建准备了多种方案, 目的是: 更加灵活
第一种方法: 通过构造方法实例化
// xml
// 直接在spring配置文件中配置类全路径
// Spring会自动的调用该类的无参数构造方法来实例化bean
<bean id="dog" class="com.powernode.bean.Dog"></bean>
// @Test
public void setEmpty(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("set-DI.xml");
Dog dog = ctx.getBean("dog", Dog.class);
System.out.println(dog);
}
第二种方式: 通过简单工厂模式实例化
通过简单工厂模式, 你需要在spring配置文件中告诉spring框架, 调用哪个类的哪个方法获取Bean
factory-method 属性指定的是工厂类当中的静态方法, 也就是告诉Spring框架, 调用这个方法可以获取Bean
// 类
public class Start {
public Start() {
System.out.println("大明星");
}
}
// 简单工厂模式中的工厂类角色
public class StartFactory {
// 工厂类中有一个静态方法
// 简单工厂模式, 又叫做: 静态工厂方法模式
public static Start get(){
return new Start();
}
}
// xml
// Spring提供的实例化方式第二种, 通过简单工厂模式, 你需要在spring配置文件中告诉spring框架, 调用哪个类的哪个方法获取Bean
// factory-method 属性指定的是工厂类当中的静态方法, 也就是告诉Spring框架, 调用这个方法可以获取Bean
<bean id="stars" class="com.powernode.xxxx.StartFactory" factory-method="get"/>
// @Test
public void conStars(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("set-DI.xml");
Start strs = ctx.getBean("stars", Start.class);
System.out.println(strs);
}
第三种方式: 通过factory-bean实例化
通过工厂方法模式, 通过factory-bean属性+factory-method属性共同完成, 告诉spring框架, 调用哪个对象的哪个方法来获取bean
factory-bean属性告诉spring调用哪个对象, factory-method告诉spring调用该对象的哪个方法
// 工厂方法模式中的具体产品角色
public class Gun {
public Gun() {
System.out.println("开枪!!");
}
}
// 工厂方法模式中的具体工厂角色
public class GunFactory {
// 工厂模式中的具体工厂角色中的方法是: 实例方法
public Gun get(){
return new Gun();
}
}
// xml
// Spring提供的实例化方式第三种, 通过工厂方法模式, 通过factory-bean属性+factory-method属性共同完成, 告诉spring框架, 调用哪个对象的哪个方法来获取bean
<bean id="gunFactory" class="com.powernode.xxxx.GunFactory"/>
// factory-bean属性告诉spring调用哪个对象, factory-method告诉spring调用该对象的哪个方法
<bean id="gun" factory-bean="gunFactory" factory-method="get"/>
// @Test
public void conGun(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("set-DI.xml");
Gun gun = ctx.getBean("gun", Gun.class);
System.out.println(gun);
}
第四种方式: 通过FactoryBean接口实例化
上述的第三种方式中, factory-bean是我们自己定义的, factory-method也是我们自己定义的
当你编写的类直接实现了FactoryBean接口之后, factory-bean不需要指定了, factory-method也不需要指定了
factory-bean会自动指向实现FactoryBean接口的类, factory-method会自动指向getObject()方法
// 普通的bean
public class Person {
public Person() {
System.out.println("蚂蚁");
}
}
// PersonFactory也是一个bean, 只不过这个bean有点特殊, 叫做工厂bean
// 通过工厂Bean这个特殊的Bean可以获取一个普通的Bean
public class PersonFactory implements FactoryBean {
@Override
public Object getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
// 这个方法在接口当中默认实现的
// 默认返回true, 表示单例的
// 如果想多例, 直接修改为return false即可
@Override
public boolean isSingleton() {
return true;
}
}
// xml
// 这种方式实际上就是第三种方式的简化, 由于你编写的类实现了BeanFactory接口, 所有不需要手动指定factory-bean factory-method
// 通过FactoryBean这个工厂Bean主要是想对普通Bean进行加工处理
<bean id="person" class="com.powernode.xxxx.PersonFactory"/>
// @Test
public void conPerson(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("set-DI.xml");
Person person = ctx.getBean("person", Person.class);
System.out.println(person);
}
4. BeanFactory和FactoryBean的区别:
BeanFactory
Spring IoC容器的顶级接口, BeanFactory翻译为"Bean工厂", 在Spring IoC容器中, Bean工厂负责Bean对象的创建
BeanFactory是工厂
FactoryBean
是一个工厂Bean, 是一个能够辅助Spring实例化其他Bean对象的一个Bean
在Spring中, Bean可以分为俩类:
普通Bean
工厂Bean (工厂Bean也是一种Bean, 只不过这种Bean比较特殊, 它可以辅助Spring实例化其他Bean对象)
5. ApplicationContext和BeanFactory的区别:
相同点:
都可以得到Spring上下文对象
都是来自Spring的顶级接口
不同点:
继承关系和功能: ApplicationContext属于BeanFactory的子类, BeanFactory只有简单的访问Bean的能力, 而ApplicationContext除了拥有BeanFactory功能之外, 还包含了更多的功能(国际化支持, 资源访问, 事件传播)
性能: ApplicationContext加载方式是将Bean对象一次性加载, 所以再后面访问Bean对象时会很快, BeanFactory需要某个Bean时, 采取加载Bean对象, 所以它在执行Bean获取时, 比较慢.