Spring(Bean详解)
GoF之工厂模式
GoF是指二十三种设计模式
GoF23种设计模式可分为三大类:
创建型(5个):解决对象创建问题。
单例模式
工厂方法模式
抽象工厂模式
建造者模式
原型模式
结构型(7个):一些类或对象组合在一起的经典结构。
代理模式
装饰模式
适配器模式
组合模式
享元模式
外观模式
桥接模式
行为型(11个):解决类或对象之间的交互问题。
策略模式
模板方法模式
责任链模式
观察者模式
迭代子模式
命令模式
备忘录模式
状态模式
访问者模式
中介者模式
解释器模式
工厂模式是解决对象创建问题的,所以工厂模式属于创建型设计模式。这里为什么学习工厂模式呢?这是因为Spring框架底层使用了大量的工厂模式。
1.1工厂模式的三种形态
工厂模式通常有三种形态:
第一种:简单工厂模式(Simple Factory):不属于23种设计模式之一。简单工厂模式又叫做静态工厂模式。简单工厂模式是工厂模式的一种特殊实现。
工厂方法模式(Factory Method):是二十三种设计模式之一。
抽象工厂模式(Abstract Factory):是二十三种设计模式之一。
1.2 简单工厂模式
简单工厂模式的角色包括三个:
抽象产品:角色
具体产品:角色
工厂类:角色
代码如下:
抽象产品角色:
package com.powernode.factory;
/**
* 武器(抽象产品角色)
* @author 耀耀
* @version 1.0
* @className Weapon
* @since 1.0
**/
public abstract class Weapon {
/**
* 所有的武器都有攻击行为
*/
public abstract void attack();
}
具体产品角色:
package com.powernode.factory;
/**
* 坦克(具体产品角色)
* @author 耀耀
* @version 1.0
* @className Tank
* @since 1.0
**/
public class Tank extends Weapon{
@Override
public void attack() {
System.out.println("坦克开炮!");
}
}
package com.powernode.factory;
/**
* 战斗机(具体产品角色)
* @author 耀耀
* @version 1.0
* @className Fighter
* @since 1.0
**/
public class Fighter extends Weapon{
@Override
public void attack() {
System.out.println("战斗机投下原子弹!");
}
}
package com.powernode.factory;
/**
* 匕首(具体产品角色)
* @author 耀耀
* @version 1.0
* @className Dagger
* @since 1.0
**/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("砍他丫的!");
}
}
工厂类角色:
package com.powernode.factory;
/**
* 工厂类角色
* @author 耀耀
* @version 1.0
* @className WeaponFactory
* @since 1.0
**/
public class WeaponFactory {
/**
* 根据不同的武器类型生产武器
* @param weaponType 武器类型
* @return 武器对象
*/
public static Weapon get(String weaponType){
if (weaponType == null || weaponType.trim().length() == 0) {
return null;
}
Weapon weapon = null;
if ("TANK".equals(weaponType)) {
weapon = new Tank();
} else if ("FIGHTER".equals(weaponType)) {
weapon = new Fighter();
} else if ("DAGGER".equals(weaponType)) {
weapon = new Dagger();
} else {
throw new RuntimeException("不支持该武器!");
}
return weapon;
}
}
测试程序(客户端程序):
package com.powernode.factory;
/**
* @author 耀耀1
* @version 1.0
* @className Client
* @since 1.0
**/
public class Client {
public static void main(String[] args) {
Weapon weapon1 = WeaponFactory.get("TANK");
weapon1.attack();
Weapon weapon2 = WeaponFactory.get("FIGHTER");
weapon2.attack();
Weapon weapon3 = WeaponFactory.get("DAGGER");
weapon3.attack();
}
}
简单工厂模式的优点:
客户端程序不需要关心对象的创建细节,需要哪个对象时,只需要向工厂索要即可,初步实现了责任的分离。客户端只负责“消费”,工厂负责“生产”。生产和消费分离。
简单工厂模式的缺点:
缺点1:工厂类集中了所有产品的创造逻辑,形成一个无所不知的全能类,有人把它叫做上帝类。显然工厂类非常关键,不能出问题,一旦出问题,整个系统瘫痪。
缺点2:不符合OCP开闭原则,在进行系统扩展时,需要修改工厂类。
Spring中的BeanFactory就使用了简单工厂模式。
1.3 工厂方法模式
工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点。
工厂模式的角色包括:
抽象工厂角色
具体工厂角色
抽象产品角色
具体产品角色
代码如下:
package com.powernode.factory;
/**
* 武器类(抽象产品角色)
* @author 耀耀
* @version 1.0
* @className Weapon
* @since 1.0
**/
public abstract class Weapon {
/**
* 所有武器都有攻击行为
*/
public abstract void attack();
}
具体产品角色:
package com.powernode.factory;
/**
* 具体产品角色
* @author 耀耀
* @version 1.0
* @className Gun
* @since 1.0
**/
public class Gun extends Weapon{
@Override
public void attack() {
System.out.println("开枪射击!");
}
}
抽象工厂角色:
package com.powernode.factory;
/**
* 武器工厂接口(抽象工厂角色)
* @author 耀耀
* @version 1.0
* @className WeaponFactory
* @since 1.0
**/
public interface WeaponFactory {
Weapon get();
}
具体工厂角色:
package com.powernode.factory;
/**
* 具体工厂角色
* @author 耀耀
* @version 1.0
* @className GunFactory
* @since 1.0
**/
public class GunFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Gun();
}
}
具体工厂角色:
package com.powernode.factory;
/**
* 具体工厂角色
* @author 耀耀
* @version 1.0
* @className FighterFactory
* @since 1.0
**/
public class FighterFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Fighter();
}
}
客户端程序:
package com.powernode.factory;
/**
* @author 耀耀
* @version 1.0
* @className Client
* @since 1.0
**/
public class Client {
public static void main(String[] args) {
WeaponFactory factory = new GunFactory();
Weapon weapon = factory.get();
weapon.attack();
WeaponFactory factory1 = new FighterFactory();
Weapon weapon1 = factory1.get();
weapon1.attack();
}
}
如果想扩展一个新的产品,只要新增一个产品类,再新增一个该产品对应的工厂即可,例如新增:匕首
增加:具体产品角色
package com.powernode.factory;
/**
* @author 耀耀
* @version 1.0
* @className Dagger
* @since 1.0
**/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("砍丫的!");
}
}
增加:具体工厂角色
package com.powernode.factory;
/**
* @author 耀耀
* @version 1.0
* @className DaggerFactory
* @since 1.0
**/
public class DaggerFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Dagger();
}
}
我们可以看到在进行功能扩展的时候,不需要修改之前的源代码,显然工厂方法模式符合OCP原则。
工厂方法模式的优点:
一个调用者想创建一个对象,只要知道其名称就可以了。
扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
屏蔽产品的具体实现,调用者只关心产品的接口。
工厂方法模式的缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
2,Bean的实例化方式
Spring为Bean提供了多种实例化方式,通常包括4种方式。(也就是说在Spring中为Bean对象的创建准备了多种方案,目的是:更加灵活)
第一种:通过构造方法实例化
默认情况下,会调用Bean的无参数构造方法
第二种:通过简单工厂模式实例化
第一步:定义一个Bean
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className Vip
* @since 1.0
**/
public class Vip {
}
第二步:编写简单工厂模式当中的工厂类
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className VipFactory
* @since 1.0
**/
public class VipFactory {
public static Vip get(){
return new Vip();
}
}
第三步:在Spring配置文件中指定创建该Bean方法(使用factory-method属性指定)
<bean id="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSimpleFactory(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Vip vip = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vip);
}
第三种:通过factory-bean实例化
这种方式本质上是:通过工厂方法模式经行实例化。
第一步:定义一个Bean
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className Order
* @since 1.0
**/
public class Order {
}
第二步:定义具体工厂类,工厂类中定义实力方法
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className OrderFactory
* @since 1.0
**/
public class OrderFactory {
public Order get(){
return new Order();
}
}
第三步:在Spring配置文件中指定factory-bean以及factory-method
<bean id="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/>
<bean id="orderBean" factory-bean="orderFactory" factory-method="get"/>
第四步:编写测试程序
@Test
public void testSelfFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Order orderBean = applicationContext.getBean("orderBean", Order.class);
System.out.println(orderBean);
}
第四种:通过FactoryBean接口实例化
以上的第三种方式中,factory-bean是我们自定义的,factory-method也是我们自己定义的。
在Spring中,当你编写的类直接实现FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。
factory-bean会自动指向实现FactoryBean接口的类,factory-method会自动指向getObject()方法。
第一步:定义一个Bean
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className Person
* @since 1.0
**/
public class Person {
}
第二步:编写一个类实现FactoryBean接口
package com.powernode.spring6.bean;
import org.springframework.beans.factory.FactoryBean;
/**
* @author 耀耀
* @version 1.0
* @className PersonFactoryBean
* @since 1.0
**/
public class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {
return new Person();
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
// true表示单例
// false表示原型
return true;
}
}
第三步:在Spring配置文件中配置FactoryBean
<bean id="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>
测试程序
@Test
public void testFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
Person personBean = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean);
Person personBean2 = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean2);
}
FactoryBean在Spring中是一个接口。被称为“工厂Bean”。“工厂Bean”是一种特殊的Bean。所有的“工厂Bean”都是用来协助Spring框架来创建其他Bean对象的
6.5 BeanFactory 和 FactoryBean的区别
BeanFactory
Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,"Bean工厂"负责创建Bean对象。
FactoryBean
FactoryBean:它是一个Bean,是一个能够辅助Spring实例化其它Bean对象的一个Bean。
在Spring中,Bean可以分为两类:
第一类:普通Bean
第二类:工厂Bean(记住:工厂Bean也是一种Bean,只不过这种Bean比较特殊,它可以辅助Spring实例化其它Bean对象。)
3,Bean的生命周期
3.1 什么是Bean的生命周期
Spring其实就是一个管理Bean对象的工厂。它负责对象的创建,对象的销毁等。
所谓的生命周期就是:对象从创建开始到最终销毁的整个过程。
什么时候创建Bean对象?
创建Bean对象的前后会调用什么方法?
Bean对象什么时候销毁?
Bean对象的销毁前后调用什么方法?
3.2 为什么要知道Bean的生命周期
其实生命周期的本质是:在哪个时间节点上调用了哪个类的哪个方法。
我们需要充分的了解在这个生命线上,都有哪些特殊的时间节点。
只有我们知道了特殊的时间节点都在哪,到时我们才可以确定代码写到哪。
我们可能需要在某个特殊的时间点上执行一段特定的代码,这段代码就可以放到这个节点上。当生命线走到这里的时候,自然会被调用。
3.3 Bean的生命周期之五步
Bean生命周期的管理,可以参考Spring的:AbstractAutowireCapableBeanFactory类的doCreateBean()方法。
Bean生命周期可以粗略的划分为五大步:
第一步:实例化Bean
第二步:Bean属性赋值
第三步:初始化Bean
第四步:使用Bean
第五步:销毁Bean
代码测试一下
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className User
* @since 1.0
**/
public class User {
private String name;
public User() {
System.out.println("1.实例化Bean");
}
public void setName(String name) {
this.name = name;
System.out.println("2.Bean属性赋值");
}
public void initBean(){
System.out.println("3.初始化Bean");
}
public void destroyBean(){
System.out.println("5.销毁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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
init-method属性指定初始化方法。
destroy-method属性指定销毁方法。
-->
<bean id="userBean" class="com.powernode.spring6.bean.User" init-method="initBean" destroy-method="destroyBean">
<property name="name" value="zhangsan"/>
</bean>
</beans>
测试程序
package com.powernode.spring6.test;
import com.powernode.spring6.bean.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author 耀耀
* @version 1.0
* @className BeanLifecycleTest
* @since 1.0
**/
public class BeanLifecycleTest {
@Test
public void testLifecycle(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User userBean = applicationContext.getBean("userBean", User.class);
System.out.println("4.使用Bean");
// 只有正常关闭spring容器才会执行销毁方法
ClassPathXmlApplicationContext context = (ClassPathXmlApplicationContext) applicationContext;
context.close();
}
}
注意事项:
第一:只有正常关闭的spring容器,bean的销毁才会被调用。
第二:ClassPathXmlApplicationContex类才有ckose()方法。
第三:配置文件中的int-method指定初始化方法。destroy-method指定销毁方法。
3.4 Bean生命周期之七步走
在以上的5步中,第3步是初始化Bean,如果你还想在初始化前和初始化后添加代码,可以加入“Bean后处理器”。
编写一个类实现BeanPostProcessor类,并且重写before和after方法:
package com.powernode.spring6.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author 耀耀
* @version 1.0
* @className LogBeanPostProcessor
* @since 1.0
**/
public class LogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean后处理器的before方法执行,即将开始初始化");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean后处理器的after方法执行,已完成初始化");
return bean;
}
}
在spring.xml文件中配置“Bean后处理器”:
<!--配置Bean后处理器。这个后处理器将作用于当前配置文件中所有的bean。-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
一定要注意:在spring.xml文件中配置的Bean后处理器将作用于当前配置文件中所有的Bean。
4,Bean的循环依赖问题
4.1什么是Bena的循环依赖
A对象中有B属性。B对象中有A属性。这就是循环依赖。我依赖你,你也依赖我。
比如:丈夫类Husband,妻子类Wife。Husband中有Wife的引用。Wife中有Husband的引用。
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className Husband
* @since 1.0
**/
public class Husband {
private String name;
private Wife wife;
}
package com.powernode.spring6.bean;
/**
* @author 耀耀
* @version 1.0
* @className Wife
* @since 1.0
**/
public class Wife {
private String name;
private Husband husband;
}
在singleton + set注入的情况下,循环依赖是没有问题的。Spring可以解决这个问题。
当循环依赖的所有Bean的scope="prototype"的时候,产生的循环依赖,Spring是无法解决的,会出现BeanCurrentlyInCreationException异常。
大家可以测试一下,以上两个Bean,如果其中一个是singleton,另一个是prototype,是没有问题的。
构造方法注入会导致**实例化对象的过程和对象属性赋值的过程没有分离开,必须在一起完成导致的。**
Spring为什么可以解决set + singleton模式下循环依赖?
根本的原因在于:这种方式可以做到将“实例化Bean”和“给Bean属性赋值”这两个动作分开去完成。
实例化Bean的时候:调用无参数构造方法来完成。此时可以先不给属性赋值,可以提前将该Bean对象“曝光”给外界。
给Bean属性赋值的时候:调用setter方法来完成。
两个步骤是完全可以分离开去完成的,并且这两步不要求在同一个时间点上完成。
也就是说,Bean都是单例的,我们可以先把所有的单例Bean实例化出来,放到一个集合当中(我们可以称之为缓存),所有的单例Bean全部实例化完成之后,以后我们再慢慢的调用setter方法给属性赋值。这样就解决了循环依赖的问题。
那么在Spring框架底层源码级别上是如何实现的呢?请看:
在以上类中包含三个重要的属性:
*Cache of singleton objects: bean name to bean instance.* 单例对象的缓存:key存储bean名称,value存储Bean对象【一级缓存】
*Cache of early singleton objects: bean name to bean instance.* 早期单例对象的缓存:key存储bean名称,value存储早期的Bean对象【二级缓存】
*Cache of singleton factories: bean name to ObjectFactory.* 单例工厂缓存:key存储bean名称,value存储该Bean对应的ObjectFactory对象【三级缓存】
这三个缓存其实本质上是三个Map集合。
我们再来看,在该类中有这样一个方法addSingletonFactory(),这个方法的作用是:将创建Bean对象的ObjectFactory对象提前曝光。
从源码中可以看到,spring会先从一级缓存中获取Bean,如果获取不到,则从二级缓存中获取Bean,如果二级缓存还是获取不到,则从三级缓存中获取之前曝光的ObjectFactory对象,通过ObjectFactory对象获取Bean实例,这样就解决了循环依赖的问题。
总结:
Spring只能解决setter方法注入的单例bean之间的循环依赖。ClassA依赖ClassB,ClassB又依赖ClassA,形成依赖闭环。Spring在创建ClassA对象后,不需要等给属性赋值,直接将其曝光到bean缓存当中。在解析ClassA的属性时,又发现依赖于ClassB,再次去获取ClassB,当解析ClassB的属性时,又发现需要ClassA的属性,但此时的ClassA已经被提前曝光加入了正在创建的bean的缓存中,则无需创建新的的ClassA的实例,直接从缓存中获取即可。从而解决循环依赖问题。
Spring(Bean详解)的更多相关文章
- Spring Bean 详解
Spring Bean 详解 Ioc实例化Bean的三种方式 1 创建Bean 1 使用无参构造函数 这也是我们常用的一种.在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象.如果类中没有⽆参构造函 ...
- Spring(3)——装配 Spring Bean 详解
装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 Bean 装配到 Spring IoC 容器中. 大部分场景下,我们都会使用 Appl ...
- Spring Bean详解
Spring Bean 在Spring的应用中,Spring IoC容器可以创建.装配和配置应用组件对象,这里的组件对象称为Bean. Bean的配置 Spring可以看作一个大型工厂,用于生产和管理 ...
- Spring二 Bean详解
Bean详解 Spring框架的本质其实是:通过XML配置来驱动Java代码,这样就可以把原本由java代码管理的耦合关系,提取到XML配置文件中管理.这样就实现了系统中各组件的解耦,有利于后期的升级 ...
- (转)java之Spring(IOC)注解装配Bean详解
java之Spring(IOC)注解装配Bean详解 在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看 ...
- spring在IoC容器中装配Bean详解
1.Spring配置概述 1.1.概述 Spring容器从xml配置.java注解.spring注解中读取bean配置信息,形成bean定义注册表: 根据bean定义注册表实例化bean: 将bean ...
- spring配置文件详解--真的蛮详细
spring配置文件详解--真的蛮详细 转自: http://book.51cto.com/art/201004/193743.htm 此处详细的为我们讲解了spring2.5的实现原理,感觉非常 ...
- 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
Spring AOP详解 . JDK动态代理.CGLib动态代理 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...
- J2EE进阶(四)Spring配置文件详解
J2EE进阶(四)Spring配置文件详解 前言 Spring配置文件是用于指导Spring工厂进行Bean生产.依赖关系注入(装配)及Bean实例分发的"图纸".Java EE程 ...
- spring事务详解(二)简单样例
系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 一.引子 ...
随机推荐
- 浙大版《C语言程序设计(第3版)》题目集 练习3-3 统计学生平均成绩与及格人数 (15 分)
练习3-3 统计学生平均成绩与及格人数 (15 分) 本题要求编写程序,计算学生们的平均成绩,并统计及格(成绩不低于60分)的人数.题目保证输入与输出均在整型范围内. 输入格式: 输入在第一行中给出非 ...
- 【Android异常】关于静态注册BroadcastReceiver接收不到自定义广播的问题
Android 8.0以上需要setComponent()来指定包名和类名,第1个参数是指接收广播类的包名,第2个参数是指接收广播类的完整类名.静态广播1.先使用Android Studio创建一个广 ...
- CSS设置边距
1.内边距 所有的 HTML 元素基本都是以矩形为基础. 每个 HTML 元素周围的矩形空间由三个重要的属性来控制: padding(内边距) margin(外边距) border(边框 ...
- python——pkl文件
pkl文件是python里面保存文件的一种格式,如果直接打开会显示一堆序列化的东西. cPickle在python3中更名为pickle 使用方式如下: import pickle as p shop ...
- ARM体系与架构【一】
由于笔试题(摩尔线程笔试题)也出现了相关的题目,所以也顺便为此做一点点小准备. 1.ARM用什么类型的指令集 ARM架构用的是RISC精简指令集. 2.RISV与RISC指令集有什么区别 3.ARM架 ...
- 基于C语言的小学四则运算出题器
一.实验目的: 1.帮助老师产出每周随机的300道含有两个运算符的四则运算,. 2.每次题目的产出均为随机,增强同学的四则运算能力. 二.实验环境: Visual C++ 三.实验内容: 1.实现随机 ...
- idea导入数据库
yml文件(在启动项文件(main)里,eg:springbook文件里面) sh-bean里org.example.sh.beans的Category类 CategoryDAO名字要和Categ ...
- Java基础学习——循环取最接近某个值的方法
if(diff<mindiff) mindiff=diff;//循环取最小值 float value = (float) fenzi/fenmu;//整数相除结果会自动转换为整数.即使强制转换为 ...
- 按正斜线输出M*N的矩阵
public static void outMatrix(int[][] array) { for(int row=0;row<array.length;row++) { int scolumn ...
- python去除前中后多处空格的方法
x=" asdf ada都 是 年 费 sdf sf " print("".join(x.split())) 测试全角半角空格都没有了