常见设计模式以及它们在Spring中的应⽤
Spring中⽤到的设计模式
1 ⼯⼚⽅法模式:Spring使⽤⼯⼚模式通过BeanFactory ApplicationContext
简单⼯⼚模式:Bean() 根据id从IoC中获取Bean
2 代理模式:AOP中的动态代理
3 单例模式:Spring中的Bean默认作⽤范围是为单例
4 适配器模式:AOP的通知、 SpringMVC中前端控制器调⽤Controller⽤到了适配器模式
⼯⼚设计模式
⼯⼚⽅法对加简单⼯⼚的改进:
⽤⼯⼚⼦类替代switch-case判断,满⾜了开闭原则。
简单⼯⼚:在⼯⼚类⽤switch-case来根据条件获取指定的操作类,但是违背了开闭原则(只能添加类不能修改原有代码)。如果增加了操作类,就需要在⼯⼚类中修改switach-case。
⼯⼚⽅法:⽤指定的⼯⼚⼦类去获取对应的操作类。后期只需要添加⼯⼚⼦类即可
简单⼯⼚在Spring IoC中的应⽤:
在需要使⽤Bean时,使⽤getBean()来获取
⼯⼚设计模式在Spring IoC中的应⽤:
先通过反射到对应⼯⼚类,反射的信息配置在
然后由该⼯⼚类调⽤实际操作类来获取Bean,⼯⼚类和操作类的对应关系配置在
1 定义操作类,相当于上图中的加法类,属于操作类
public class CarInstanceFactory {
private Map<Integer, Car> map = new HashMap<Integer,Car>();
public void setMap(Map<Integer, Car> map) {
this.map = map;
}
public CarInstanceFactory(){
}
public Car getCar(int id){
(id);
}
}
2 在XML中定义⼀个⼯⼚类或者多个⼯⼚类,指定该⼯⼚类对应的操作类
然后定义每个Bean对应的⼯⼚类
carFactory就是⼀个⼯⼚类,相当于上⾯图中的⼀个加法⼯⼚
com.home.factoryMethod.CarInstanceFactory就是carFactory对应的操作类,相当于上图中的加法类
<bean id="carFactory" class="com.home.factoryMethod.CarInstanceFactory">
<property name="map">
<map>
<entry key="4">
<bean class="com.home.factoryMethod.Car">
<property name="id" value="4"></property>
<property name="name" value="Honda"></property>
<property name="price" value="300000"></property>
</bean>
</entry>
<entry key="6">
<bean class="com.home.factoryMethod.Car">
<property name="id" value="6"></property>
<property name="name" value="ford"></property>
<property name="price" value="500000"></property>
</bean>
</entry>
</map>
</property>
</bean>
<!-- 2.use Factory bean to get bean objectr
factory-bean : the bean define above
factory-method: method of get Bean Object
constructor-arg: parameters of factory-method
-->
<bean id="car4" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="4"></constructor-arg>
</bean>
/
/当系统需要取出car6时,会通过反射到该Bean对应的⼯⼚为carFactory
<bean id="car6" factory-bean="carFactory" factory-method="getCar">
<constructor-arg value="6"></constructor-arg>
</bean
单例模式
保证⼀个类只有⼀个实例
特点:单例类的构造函数都是私有的
懒汉模式:
特点:在第⼀次使⽤单例对象时,才去实例化单例对象
实现⽅式:
1 使⽤双重检查锁
使⽤volatile来禁⽌实例化对象时指令重排,防⽌实例化的对象为⼀空内存区域使⽤锁 + 双if,防⽌多线程下单例对象被实例化多次
public classs Singtonle{
private volatile static Singtone instance;
public static Singleton(){
if(instance == null){
synchornized(Singtone.class){
if (instance == null){
instance = new Singletone();
}
}
}
return instance;
}
}
springboot框架的作用2 使⽤私有静态内部类。
在内部类中,定义⼀个静态的单例类实例对象,并实例化它
在外部类中⽤静态⽅法返回刚才实例的对象
public class Singleton{
private static class SingletonHolder{
private static final Singleton instance = new Singletone();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
饿汉模式:
特点:在第⼀次使⽤前,单例对象就被实例完毕
实现⽅式: 将对象实例设置成静态成员变量,并直接初始化。在单例类被加载时就会实例化单例对象
缺点:⽆法在系统运⾏时给单例对象的创建添加额外的参数,只能在编译阶段读取配置⽂件中的参数
public class Singleton{
private static final Singletoninstance = new Singleton();
private Singleton(){)
public static Singleton getInstance(){
return instance;
}
}
单例模式在IoC容器中的使⽤:单例注册表
单例注册表:就是⼀个ConcurrentHashMap,key为Bean的id,value为Bean对象
使⽤ConcurrentHashMap可以避免多线程下的安全问题,如并发修改异常、数据不⼀致
jdk1.8中ConcurrentHashMap基于HashMap + ReentrantLock + CAS
使⽤单例注册表的原因:
IoC中的单例模式既不是饿汉模式,也不是懒汉模式, 因为这些单例模式都是从构造函数上着⼿的
因为IoC管理的是任意的对象,所以不能修改所有类的构造函数
所以IoC中⽐没有从构造函数上去控制单例,⽽是在后期的使⽤过程中,⽤注册表控制单例对象实例的个数
使⽤流程:
1 IoC容器初始化时,会读取XML或者扫描注解,将⼀些单例模式的Bean注⼊IoC容器,即插⼊ConcurrentHashMap中
2 当需要使⽤相应的对象时,就根据名称或类型去map中寻。
3 如果不到,就会创建⼀个
4 判断该Bean是不是单例模式的,如果是就它放⼊单例注册表中
public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{
/**
* 充当了Bean实例的缓存,实现⽅式和单例注册表相同
*/
private final Map singletonCache=new HashMap();
public Object getBean(String name)throws BeansException{
return getBean(name,null,null);
}
...
public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{ //对传⼊的Bean name稍做处理,防⽌传⼊的Bean name名有⾮法字符(或则做转码)
String beanName=transformedBeanName(name);
Object bean=null;
//⼿⼯检测单例注册表
Object sharedInstance=null;
//使⽤了代码锁定同步块,原理和同步⽅法相似,但是这种写法效率更⾼
synchronized(this.singletonCache){
sharedInstance=(beanName);
}
if(sharedInstance!=null){
...
//返回合适的缓存Bean实例
bean=getObjectForSharedInstance(name,sharedInstance);
}else{
...
//取得Bean的定义
RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false); ...
/
/根据Bean定义判断,此判断依据通常来⾃于组件配置⽂件的单例属性开关
//<bean id="date" class="java.util.Date" scope="singleton"/>
//如果是单例,做如下处理
if(mergedBeanDefinition.isSingleton()){
synchronized(this.singletonCache){
//再次检测单例注册表
sharedInstance=(beanName);
if(sharedInstance==null){
...
try {
//真正创建Bean实例
sharedInstance=createBean(beanName,mergedBeanDefinition,args);
//向单例注册表注册Bean实例
addSingleton(beanName,sharedInstance);
}catch (Exception ex) {
...
}finally{
...
}
}
}
bean=getObjectForSharedInstance(name,sharedInstance);
}
//如果是⾮单例,即prototpye,每次都要新创建⼀个Bean实例
//<bean id="date" class="java.util.Date" scope="prototype"/>
else{
bean=createBean(beanName,mergedBeanDefinition,args);
}
}
...
return bean;
}
}
代理模式
⽤代理对象去执⾏⽬标对象中的⽅法
代理模式在AOP中的使⽤:
1 通过运⾏时动态代理,来动态地⽣成⽬标对象的代理对象,从⽽可以增强⽬标对象中的⽅法
2 编译时的通过cglib来⽣成代理对象,基于字节码操作
⽐如需要⽤AOP⽣成给UserService的代理对象,添加⽇志管理
那么IUserService就是上图中的Subject类, UserService就是RrealSubject, ⽽代⽣成的代理对象就是Proxy类
使⽤动态代理来给常规的service添加事务
1 根据接⼝信息动态创建⼀个代理对象
2 将代理对象注⼊IoC容器
3 需要使⽤时就从IoC中取出这个代理对象
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论