设计模式原则思考
问题一
请描述什么是依赖倒置原则,为什么有时候依赖倒置原则又被称为好莱坞原则?
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。
很清楚上面的描述包含两层含义:
高层模块不应该依赖于底层模块,而应该依赖于抽象,也就是说高层和底层模块之间不应该有直接的联系,而应该通过抽象接口进行调用。
抽象接口不应该依赖于实现类,而实现类应该依赖于接口和抽象。
思考:其实依赖好理解,关键在于倒置,什么是倒置呢,如果只是简单的对service层实现方法抽象出一个接口方法给外部调用,叫倒置吗?
这个依赖很有意思,可以想象在前后端配合开发时,是谁定义联调接口?如果后端定义了接口,供前端调用,之后主动权在后端处理上,这个设计合理吗?
其实正常的做法应该是前端定义接口,后端根据接口功能来实现,这也是大部分开发同学在工作中经常遇到的场景吧。
那么对于纯后端开发设计中,这种设计也是应该存在的,controller层负责前后端接口调用和数据传输,service层负责业务逻辑实现,那么谁来定义两者直接交互的接口呢?接口的定义应该放在controller层,因为只有controller层才知道自己想要什么,这样是依赖倒置的精髓,这个依赖的接口不应该由具体的实现类来定义,而应该由调用者来定义。
比如:
public class LibraryConller{ private BookService bookService; public List<BookInfo> showAllBooks(){ return bookService.listAllBooks(); }}//定义方法应该交由controller定义public interface BookService{ public List<BookInfo> listAllBooks();}
don‘t call us, we‘ll call you,这是著名的好莱坞原则,在好莱坞,把简历递交给演艺公司后就只有回家等待。由演艺公司对整个娱乐项的完全控制,演员只能被动式的接受公司的差使,在需要的环节中,完成自己的演出。
好莱坞原则强调的调用的单向性,这里需要引入IOC概念,也叫控制反转。控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的"控制反转"就是对组件对象控制权的转移,从程序代码本身转移到了外部容器,由容器来创建对象并管理对象之间的依赖关系。这与依赖倒置有异曲同工之妙,所有的控制权都在使用方,具体非业务提供方只需要按需提供功能就可以了。
问题二
请描述一个你熟悉的框架,是如何实现依赖倒置原则的。
spring如何实现依赖注入
一句话描述:将依赖注入容器中,在使用时根据beanName将容器中的实例进行初始化,并注入初始化的实例变量中,这样就可以直接使用了
初始化
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
obtainFreshBeanFactory()方法
在这个方法里,完成了配置文件的加载、解析
@Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
利用XML加载bean
@Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
注册bean
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
将创建的bean放在容器中(ConcurrentHashMap)
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "'beanName' must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); this.beanDefinitionMap.put(beanName, beanDefinition); }
注入的bean如何使用
初始化所有singleton bean
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(new StringValueResolver() { @Override public String resolveStringValue(String strVal) { return getEnvironment().resolvePlaceholders(strVal); } }); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
创建bean
@Override protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // Make sure bean class is actually resolved at this point, and // clone the bean definition in case of a dynamically resolved Class // which cannot be stored in the shared merged bean definition. Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Prepare method overrides. try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; }
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName); if (beanType != null) { InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); } }
解析Resource注解并进行依赖注入
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) { LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>(); Class<?> targetClass = clazz; do { final LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>(); ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException { if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields"); } currElements.add(new WebServiceRefElement(field, field, null)); } else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@EJB annotation is not supported on static fields"); } currElements.add(new EjbRefElement(field, field, null)); } else if (field.isAnnotationPresent(Resource.class)) { if (Modifier.isStatic(field.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static fields"); } if (!ignoredResourceTypes.contains(field.getType().getName())) { currElements.add(new ResourceElement(field, field, null)); } } } }); ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) { if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods"); } if (method.getParameterTypes().length != 1) { throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method); } PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new WebServiceRefElement(method, bridgedMethod, pd)); } else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@EJB annotation is not supported on static methods"); } if (method.getParameterTypes().length != 1) { throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method); } PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new EjbRefElement(method, bridgedMethod, pd)); } else if (bridgedMethod.isAnnotationPresent(Resource.class)) { if (Modifier.isStatic(method.getModifiers())) { throw new IllegalStateException("@Resource annotation is not supported on static methods"); } Class<?>[] paramTypes = method.getParameterTypes(); if (paramTypes.length != 1) { throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method); } if (!ignoredResourceTypes.contains(paramTypes[0].getName())) { PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz); currElements.add(new ResourceElement(method, bridgedMethod, pd)); } } } } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); }
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable { Collection<InjectedElement> elementsToIterate = (this.checkedElements != null ? this.checkedElements : this.injectedElements); if (!elementsToIterate.isEmpty()) { boolean debug = logger.isDebugEnabled(); for (InjectedElement element : elementsToIterate) { if (debug) { logger.debug("Processing injected element of bean '" + beanName + "': " + element); } element.inject(target, beanName, pvs); } } }
问题三
请用接口隔离原则优化 Cache 类的设计,画出优化后的类图。
xu'yao
需求分析:
业务层:暴露get、put、delete方法给业务层
Cache:需要动态加载
方案:Cache接口只包含get、put、delete,增加DynamicLoadCache接口,包含rebuild方法,继承Cache接口。CacheClient类,实现DynamicLoadCache接口,包含初始化方法等,可以动态加载配置进行更新CacheSupplier(实际cache服务方)。业务类初始化CacheClient,返回Cache,这样业务就只能使用get、put、delete了,
张瑞浩
还未添加个人签名 2018.09.18 加入
还未添加个人简介
评论