Spring中用到的设计模式
1 工厂方法模式:Spring使用工厂模式通过BeanFactory ApplicationContext
简单工厂模式:BeanFactory.getBean() 根据id从IoC中获取Bean
2 代理模式:AOP中的动态代理
3 单例模式:Spring中的Bean默认作用范围是为单例
4 适配器模式:AOP的通知、 SpringMVC中前端控制器调用Controller用到了适配器模式
工厂设计模式
工厂方法对加简单工厂的改进:
用工厂子类替代switch-case判断,满足了开闭原则。
简单工厂:在工厂类用switch-case来根据条件获取指定的操作类,但是违背了开闭原则(只能添加类不能修改原有代码)。
如果增加了操作类,就需要在工厂类中修改switach-case。
工厂方法:用指定的工厂子类去获取对应的操作类。后期只需要添加工厂子类即可
简单工厂在Spring IoC中的应用:
在需要使用Bean时,使用getBean()来获取
BeanFactory.getBean(),即通过id从IoC容器中获取Bean时,就是通过工厂类来从操作类中获取一个Bean
工厂设计模式在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){
return map.get(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;
}
}
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=this.singletonCache.get(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=this.singletonCache.get(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中取出这个代理对象
@Configuration
public class BeanFactory {
private IAccountService accountService;
private TransactionManager txManager;
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
public final void setAccountService(IAccountService accountService) {
this.accountService = accountService;
}
/**
* 获取Service代理对象
* @return
*/
@Bean
public IAccountService proxyAccountService() {
return (IAccountService)Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
accountService.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 添加事务的支持
*
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("test".equals(method.getName())){
return method.invoke(accountService,args);
}
Object rtValue = null;
try {
//1.开启事务
txManager.beginTransaction();
//2.执行操作
rtValue = method.invoke(accountService, args);
//3.提交事务
txManager.commit();
//4.返回结果
return rtValue;
} catch (Exception e) {
//5.回滚操作
txManager.rollback();
throw new RuntimeException(e);
} finally {
//6.释放连接
txManager.release();
}
}
});
}
}
在需要使用这个service时,根据id来注入
@Resource("proxyAccountService")
IAccountService proxyAccountService;
适配器模式
作用:将一个接口转换为用户希望的另一个接口。从而使原本因为接口不兼容而不能一起工作的那些类可以一起工作
使用方式:在两个不兼容的类之间,加一个适配器类
使用场景:想使用一个已经存在的类,但是该类中方法的名称与你预期的不相同。
即想复用已有的类,但是接口与复用环境要求不一致
那就新创建一个类,类中的方法名是你所希望的,然后用这个类的方法去调用原有类中对应的方法
优点:提高了系统的复用性和扩展性。可以模糊具体的实现,对外提供统一的访问接口
比如SpringBoot的日志系统中,就是使用门面框架去调日志体实现框架。即用sl4j去调用Logback或者Log4j
sl4j中的API是固定的,但是Logback和Log4j中操作日志的API是不同的,为了实现无缝切换日志实现框架,
就需要在门面框架和实现框架中间,加一个适配器类。每个日志实现框架有自己对应的适配器类
这些适配器类中的方法的名称是相同的, 但是不同适配器中方法的具体实现是针对特定的日志实现框架
具体调用过程就是:门面框架调用适配器类,适配器类调用它对应的日志实现框架
例如target类是:
class Target{
public virtual void Request(){
Console.WriteLine("普通请求");
}
}
Adatee(需要被适配的类)为:
class Adaptee{
public void SpecificRequest(){
COnsole.WriteLine("特殊请求");
}
}
此时我们需要调用一个类中的Request(),但是这个类中对应方法的名称是SpecificRequest()兼容
我们就需要在中间加一个适配器类,适配器类中的方法名是我们所期望的
然后在适配器类中去调用实际的方法
class Adapter : Target{
private Adaptee adpatee = new Adaptee();
public override void Request(){
adaptee.SpecificRequest();
}
}
此时我们就可以通过适配器类,来调用目标类了,因为适配器类中的方法名符合我们的要求
Target target = new Adapter();
target.Request()
这里的Adaptee类相当于日志实现框架,Target类相当于日志门面框架。
门面框架想直接调用日志实现框架,但是门面框架中保存的方法名与实现框架的不一致
所以就添加一个适配器类,在适配器类中定义一个符合门面框架要求的方法,在该方法中去调用日志实现框架
适配器模式在SpringMVC中的使用:
使用的原因:
在SpringMVC中,前端控制器处理请求的方式是固定的,但是不同的请求需要由不同的Controller来处理,
不同Controller中,方法名称是不同的,所以不同的Controller不能与前端控制器兼容
就需要在它们中间加一个适配器(HandlerAdapter) 每个Controller都有对应的适配器
运行流程:
1 前端控制器在收到请求后,会利用HandleMapping来寻找处理该请求的Controller
2 获取这个Controller的适配器
3 前端控制器会通过这个适配器来调用Controller中方法
适配器模式在AOP中的应用:
AOP中处理通知的接口是统一的,但是通知的类型却有多种,如前置通知、后置通知‘、异常通知
每个通知都有对用的拦截器:MethodBeforeAdviceInterceptor AfterReturningAdviceInterceptor ThrowsAdviceInterceptor
Spring需要将每个通知都封装成对应的拦截器类型
因为AOP的本质就是先用动态代理来生成代理对象,然后把通知转换成拦截器,用来拦截目标对象方法的执行
但是每个通知中的方法名是不同的,所以与拦截器不兼容
所以需要在它们中间添加适配器
需要被适配的类如下,即Adaptee
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable;
}
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(@Nullable Object var1, Method var2, Object[] var3, @Nullable Object var4) throws Throwable;
}
public interface ThrowsAdvice extends AfterAdvice {
}
而目标类如下,即target
public interface AdvisorAdapter {
boolean supportsAdvice(Advice var1);
MethodInterceptor getInterceptor(Advisor var1);
}
我们发现target中的方法和Adaptee中的不兼容,所以需要适配器类Adapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
@SuppressWarnings("serial")
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
客户端DefaultAdvisorAdapterRegistry,即结构图中的Client类
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList(3);
public DefaultAdvisorAdapterRegistry() {
// 这里注册了适配器
this.registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
this.registerAdvisorAdapter(new AfterReturningAdviceAdapter());
this.registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor)advice);
}
Iterator var4 = this.adapters.iterator();
while(var4.hasNext()) {
AdvisorAdapter adapter = (AdvisorAdapter)var4.next();
if (adapter.supportsAdvice(advice)) { // 这里调用适配器方法
interceptors.add(adapter.getInterceptor(advisor)); // 这里调用适配器方法
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
} else {
return (MethodInterceptor[])interceptors.toArray(new MethodInterceptor[0]);
}
}
// ...省略...
}